Go uses both dynamic and static binding. From my understanding, if you need to use a type assertion then it's dynamic. I want to validate my assumptions.
type Xer interface {
X()
}
type XYer interface {
Xer
Y()
}
type Foo struct{}
func (Foo) X() { println("Foo#X()") }
func (Foo) Y() { println("Foo#Y()") }
Assumptions:
foo := Foo{}
// static: Foo -> XYer
var xy XYer = foo
// static: XYer -> Xer
var x Xer = xy
// static: Xer -> interface{}
var empty interface{} = x
// dynamic: interface{} -> XYer
xy2 := empty.(XYer)
// dynamic: XYer -> Foo
foo2 := xy2.(Foo)
So when converting from type A -> interface B, if A satisfies B then you don't need an assertion and the itable can be generated at compile time. What about a case where you use an assertion where it's not needed:
var x Xer = Foo{}
empty := x.(interface{})
what happens in this case? If someone could clarify this for me that would great.
To extend on jnml's answer, 6g generates a type assertion nevertheless.
empty := x.(interface{})
is expanded to:
0034 (dumb.go:19) MOVQ $type.interface {}+0(SB),(SP)
0035 (dumb.go:19) LEAQ 8(SP),BX
0036 (dumb.go:19) MOVQ x+-32(SP),BP
0037 (dumb.go:19) MOVQ BP,(BX)
0038 (dumb.go:19) MOVQ x+-24(SP),BP
0039 (dumb.go:19) MOVQ BP,8(BX)
0040 (dumb.go:19) CALL ,runtime.assertI2E+0(SB)
0041 (dumb.go:19) MOVQ 24(SP),BX
0042 (dumb.go:19) MOVQ BX,empty+-16(SP)
0043 (dumb.go:19) MOVQ 32(SP),BX
0044 (dumb.go:19) MOVQ BX,empty+-8(SP)
To clarify what is happening here, in line 34 the InterfaceType of interface{} is loaded to the
first value of the stack. Line 35-36 and 37-38 put the tab and data values of x
onto the stack. The stack is then ready for runtime.assertI2E to be called,
which simply assigns the underlying type and data to the return value. The compiler knows that
you're assigning to an empty interface, hence the call to assertI2E: I2E stands for Interface to Eface (Empty Interface), so no check for methods is necessary. The only restriction assertI2E enforces is that
the asserted value must be an interface.
If however, you're doing x.(Xer), runtime.assertI2I would've been called, which then checks if
the methods implement the interface.
I don't know what is static binding of interfaces or dynamic binding of interfaces. The language specification never mentions such terms. Let me assume you mean type checking at compile time and type checking at run time. With this assumptions, all of your examples are, AFAICS, correct. It boils down to a simple schema (a condensed version of the relevant language specification parts):
All assignments are type checked at compile time.
All type assertions (.(T)) are type checked at run time.
Which also answers the "what happens in this case?".
That said, the compiler is free to optimize cases where it can prove at compile time that the type is known and assignment compatible. But that's only an implementation detail which one cannot rely on - as it may not be the case of other, specification conforming implementation(s).
Actually the gc compiler has (IINM) such optimizations and even in the opposite sense. It can say 'impossible type assertion' where it can prove at compile time that the type assertion will fail.
Related
I encountered an unfamiliar pattern of initialization from Objective-C that I'm struggling to replicate in Swift.
Objective-C
In the example code, they defined a C struct such as this (abbreviated, original here):
struct AQPlayerState {
AudioFileID mAudioFile;
}
Here's an example that uses AQPlayerState:
AQPlayerState aqData; // 1
OSStattus result =
AudioFileOpenURL(
audioFileURL,
fsRdPerm,
0,
&aqData.mAudioFile // 2
);
The key takeaway from above is that aqData currently has uninitialized properties, and AudioFileOpenURL is initializing aqData.mAudioFile on it's behalf.
Swift
I'm trying to replicate this behaviour in Swift. Here's what I've tried so far:
Models:
class Person {
var name: String
init(name: String) {
self.name = name
}
}
class Foo {
var person: Person?
}
My idea was to replicate the Objective-C code by passing a reference of Foo.person into a function that would instantiate it on it's behalf.
Initialization Function:
func initializeWithBob(_ ptr: UnsafeMutablePointer<Person?>) {
ptr.pointee = Person(name: "Bob")
}
initializeWithBob takes a pointer to an address for a Person? type and initializes it with a Person(name: "Bob") object.
Here's my test code:
let foo = Foo()
let ptr = UnsafeMutablePointer<Person?>.allocate(capacity: 1)
ptr.initialize(to: foo.person)
defer {
ptr.deinitialize()
ptr.deallocate(capacity: 1)
}
initializeWithBob(ptr)
print(foo.person) // outputs nil
initializeWithBob failed to "install" an instance of type Person in my Foo instance. I presume some of my assumptions are wrong. Looking for help in correcting my assumptions and understanding of this situation.
Thanks in advance!
You can achieve what you are looking for via withUnsafeMutablePointer(to:_:) like so:
let foo = Foo()
withUnsafeMutablePointer(to: &foo.person) { (ptr) -> Void in
initializeWithBob(ptr)
}
print(foo.person!.name) // outputs Bob
However, I wouldn't recommend this approach. IMHO it makes more sense to wrap the APIs you are working with in a C function that you can make 'nice' to call from Swift. The problem with your current approach is that this type of Swift is hard to read for Swift developers and also hard to read for Audio Toolbox developers.
#kelvinlau Is this what you were thinking of trying to achieve?
func initializeWithBob(_ ptr: UnsafeMutablePointer<Foo>) {
ptr.pointee.person = Person(name: "Bob")
}
let foo = Foo()
let ptr = UnsafeMutablePointer<Foo>.allocate(capacity: 1)
ptr.initialize(to: foo)
initializeWithBob(ptr)
print(foo.person?.name ?? "nil")
ptr.deinitialize()
ptr.deallocate(capacity: 1)
print(foo.person?.name ?? "nil")
The code pattern you have in Objective-C is for out parameters, that is parameters which return a value, or in out parameters, that is parameters which both pass a value in and return one. Objective-C does not directly support these so pointers are used to produce the semantics.
Swift has in out parameters indicated by the keyword inout in the function declaration. Within the function an assignment to an inout parameters effectively assigns a value to the variable that was passed as the argument. At the function call site the variable must be prefixed by & to indicate it is the variable itself and not its value which is effectively being passed.
Keeping your Person and Foo as is your function becomes:
func initializeWithBob(_ ptr: inout Person?)
{
ptr = Person(name: "Bob")
}
and it may be used, for example, like:
var example = Foo()
initializeWithBob(&example.person)
Using inout in Swift is better than trying to build the same semantics using pointers.
HTH
Note: You can skip this unless you are curious
"Effectively" was used a few times above. Typically out parameters are implemented by the parameter passing method call-by-result, while in out use call-by-value-result. Using either of these methods the returned value is only assigned to the passed variable at the point the function returns.
Another parameter passing method is call-by-reference, which is similar to call-by-value-result except that each and every assignment to the parameter within the function is immediately made to passed variable. This means changes to the passed variable may be visible before the function returns.
Swift by design does not specify whether its inout uses call-by-value-result or call-by-reference. So rather than specify the exact semantics in the answer "effectively" is used.
Trying to override a trait cast problem, described here. Stuck at implementing the trait function which returns the enum instance with own implementation inside:
//the "trait matcher" enum
enum Side<'a> {
Good(&'a GoodDude),
Bad(&'a BadDude),
}
//very general trait
trait Dude {
fn who_am_i(&self) -> Side;
fn do_useful_stuff(&self);
}
//specific trait #1
trait GoodDude: Dude {
fn who_am_i_inner(&self) -> Side {
Side::Good(&self)
}
fn save_the_world(&self);
}
//specific trait #2
trait BadDude: Dude {
fn who_am_i_inner(&self) -> Side {
Side::Bad(&self)
}
fn do_evil(&self);
}
But for some reason the compilation of this part fails with E0277:
trait GoodDude: Dude {
fn who_am_i_inner(&self) -> Side {
Side::Good(&self) //&self should be &GoodDude, but compiler says it is not...
}
fn save_the_world(&self);
}
And results in:
<anon>:16:20: 16:25 error: the trait `GoodDude` is not implemented for the type `&Self` [E0277]
<anon>:16 Side::Good(&self)
^~~~~
<anon>:16:20: 16:25 help: see the detailed explanation for E0277
<anon>:16:20: 16:25 note: required for the cast to the object type `GoodDude`
Can this be worked out?
Full sample: https://play.rust-lang.org/?gist=8ae2384e401da76c16214c4a642ce8b4&version=stable&backtrace=0
Firstly, the type of self in fn who_am_i_inner is already a reference, so you don't need to &.
fn who_am_i_inner(&self) -> Side {
Side::Good(self)
}
But then rustc complains...
<anon>:13:20: 13:24 error: the trait `core::marker::Sized` is not implemented for the type `Self` [E0277]
<anon>:13 Side::Good(self)
^~~~
<anon>:13:20: 13:24 help: see the detailed explanation for E0277
<anon>:13:20: 13:24 note: `Self` does not have a constant size known at compile-time
<anon>:13:20: 13:24 note: required for the cast to the object type `GoodDude`
Admittedly the error message is very unclear and E0277 is about something totally different. Let's try the nightly compiler instead, which gives better error messages:
error: the trait bound `Self: std::marker::Sized` is not satisfied [--explain E0277]
--> <anon>:13:20
13 |> Side::Good(self)
|> ^^^^
help: consider adding a `where Self: std::marker::Sized` bound
note: required for the cast to the object type `GoodDude`
OK let's try to add the where Self: Sized:
fn who_am_i_inner(&self) -> Side where Self: Sized {
Side::Good(self)
}
and now it works.
World saved. Press any key to continue
May the 4th be with you
Pew Pew Pew
Luke I am yr father
The where Self: Sized is Rust's way to signify that the method cannot be used from trait objects. We say the method ignored from "object-safety", or "cannot be virtual" if you like C++.
The effect is that if all you have got is luke: &GoodDude, then you cannot call luke.who_am_i_inner() since *luke has an unknown size.
The reason we need to make the method not object-safe is due to the cast &Self → &GoodDude. In Rust a trait object reference like &GoodDude is a fat pointer, internally it is represented as a 2-tuple (pointer, method_table). However, in a trait the self is a thin-pointer.
We cannot convert a thin-pointer to a fat-pointer, since there is a missing information, the method_table. This can be filled in if we knew the concrete type. That's why we add the where Self: Sized.
If you want to make who_am_i_inner object-safe, then you cannot provide a default implementation.
The follow codes produces "prog.go:17: c.Test undefined (type Child has no field or method Test)". (http://play.golang.org/p/g3InujEX9W)
package main
import "fmt"
type Base struct {
X int
}
func (b Base) Test() int {
return b.X
}
type Child Base
func main() {
c := Child{4}
fmt.Println(c.Test())
}
I realize Test is technically defined on Base, but should Child inherit that method?
the way to go for inheritance in go is using struct embedding with anonymous struct members.
Here is an adaption of your example.
Read about struct embedding and go's approach to inheritance etc here
The behaviour you encountered is expected and in sync with the golang specification, which explicitly states that:
The method set of any type T consists of all methods with receiver type T. The method set of the corresponding pointer type *T is the set of all methods with receiver *T or T (that is, it also contains the method set of T). Further rules apply to structs containing anonymous fields, as described in the section on struct types. Any other type has an empty method set.
/clr:pure switch generates pure MSIL but it is not verifible. Native array and pointer can be used in this mode. Does that mean that there is a structure in MSIL to hold native arrays and pointers? If yes, I would like to ask how can I code MSIL native array and pointer?
Yes, there is a type in CIL to represent unmanaged pointers. They are similar to managed pointers (ref and out in C#, & in CIL), except that GC ignores them and you can do some arithmetic operations on them (those that make sense with pointers).
Interestingly, the pointer type does contain information about the target type (so it's e.g. int32*), but all arithmetic operations are byte based.
As an example, the following C++/CLI method:
void Bar(int *a)
{
a[5] = 15;
}
produces the following CIL when it's inside a ref class (as reported by Reflector):
.method private hidebysig instance void Bar(int32* a) cil managed
{
.maxstack 2
L_0000: ldarg.1 // load the value of a pointer to the stack
L_0001: ldc.i4.s 20 // load the number 20 (= 4 * 5) to the stack
L_0003: add // add 20 to the pointer
L_0004: ldc.i4.s 15 // load the number 15 to the stack
L_0006: stind.i4 // store the value of 15 at the computed address
L_0007: ret // return from the method
}
When I learned MIPS assembly a few months ago, a question popped into my head that I forgot to ask then, so I thought I'd ask it now:
Is it possible to implement polymorphism without indirect jump instructions? If so, how?
(Indirect jumps are the "jump register" instructions, e.g. jr $t0 in MIPS or jmp EAX in x86.)
One such solution I came up with is self-modifying code, but I'm curious as to whether polymorphism (and so OOP) would be possible by any other means.
The simplest answer to your question would be to write your program (and possible assembler) in such a way that all method calls can be resolved at runtime thus negating the need for a lookup table. I'm going to assume you're talking about passing a subclass to a function that was designed for a superclass and it is therefore impossible to implement this optimization.
Eliminating the lookup, I think, is outside the scope of your question so I'm going to suggest replacements for the jmp <reg> instruction (sorry, I only know x86).
You can execute a call <mem> on a memory
address (isn't this how you would do
it using a lookup table?)
You can execute a call <reg> on a register
(not entirely different from the jmp
, but does answer your question)
You can jmp <mem> if you wanted, but
that's not all that different from jmp <reg>
All of these are possible and solve your problem, but are all alike. I guess this illustrates my confusion on why you would want to do what you're asking. You have to have some way to choose which method to call (the vtable) and some way to transfer execution to the method (using jmp or call).
The only other possible way to do this would be to fiddle with the register that is used to point to the next command in the execution chain (EIP in x86). Whether you can or should is another question. I suppose if you were intimately knowledgeable of the architecture and weren't worried about it changing you could do that.
Yes
You may want to consider changing your question however
Consider this C++ code where Derived is obviously polymorphic (yes, I didn't delete the object):
class Base
{
public: int foo(){ return 1; }
};
class Derived: public Base
{
public: int foo(){ return 2; };
};
int main()
{
Base* b = new Derived();
return b->foo(); //Returns 1
}
The assembly generated by gcc is
main:
.LFB2:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $48, %rsp
.seh_stackalloc 48
.seh_endprologue
call __main
movl $1, %ecx
call _Znwm
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movq %rax, %rcx
call _ZN4Base3fooEv
addq $48, %rsp
popq %rbp
ret
As you can see there is no indirect jump/call.
That's because polymorphism is not the point here (though it is necessary), virtual methods are.
Then the answer becames
No
If by indirect jump/call you means every technique that use a runtime value for computing the target of the jump/call (so including things like ret, call [], call reg, jr, jalr).
Consider this source
#include <iostream>
class Base
{
public: virtual int foo(){ return 1; }
};
class Derived: public Base
{
public: int foo(){ return 2; };
};
class Derived2: public Base
{
public: int foo(){ return 3; };
};
int main()
{
int a;
Base* b = 0; //don't remember the header for std::nullptr right now...
std::cin >> a;
if (a > 241)
b = new Derived();
else
b = new Derived2();
return b->foo(); //Returns what?
}
The result depends on the user input, an archetypal runtime value, so must be the called routine and no static address would do.
Note that in this case the compiler could use a jump toward calls with static address (as either Derived2::foo or Derived::foo is called) but this is not in general possible (you may have multiple object files without the source, you may have an aliasing pointer, the pointer b could be set by an external library, and so on).