Wednesday, February 18, 2015

I just pushed a change that fixes a long-standing problem with interfaces.
Interfaces are implemented as ordinary annotations. When the @interface annotation is used, it expands a definition of a class derived from VTableBase. This lets us mix the interface in with arbitrary base classes and also lets us define a class with multiple interfaces (diamond inheritance, where a class inherits from multiple base classes with a common ancestor class, is not allowed in Crack except for the VTableBase class, so interfaces can not be derived from Object which classes are by default).
We could just derive from VTableBase except that we still need the reference counting mechanism defined in Object. For example:

    class A {}
    class B : VTableBase {}
    class C : A, B {}
    
    C c = {};
    B b = c;
    c = null;

In the example above, when 'c' is assigned to null, the reference count of its 
object drops to 0 and the object is deleted because B doesn't have bind and 
release operators or a reference count.  'b' now points to unmanaged memory.


Part of what @interface does is to generate bind and release operators that delegate to Object.oper bind() and Object.oper release(). But to be able to ues these, we need a way to convert the interface to the Object that its implementation class will be based on.
As a hack, the code used to generate a special method: _iface_get$(Interface)Object() ($(Interface) was the name of the interface class). This was abstract in the interface and defined in the implementation to simply "return this," allowing us to do the conversion. But _iface_get* had some problems because it was based on the simple class name. So:
  • Aliasing interfaces didn't work, because "@implements Alias" caused _iface_getAliasObject() methods to be generated instead of _iface_getRealClassNameObject() methods.
  • You couldn't implement two different interfaces with the same simple name, for example Functor1[void, int] and Functor1[void, String] (for generics, we only used the name of the generic class, not the instantiation class).
The solution to this is the special "oper from ClassName" operator. Like "oper to", "oper from" is implemented in the parser and has full knowledge of the type that is specified. Therefore, it can implement the operator based on the canonical name of the type, solving the problems above.
I originally considered creating a more complete implementation of virtual base classes, but with "oper from", I no longer see a use case for it. I think that everything that you can do with a virtual base class you can also do with "oper from" and annotations.
Of course, I'm open to a counterexample.