Why do I need opCmp for anonymous class?

I'm not really sure how to explain this, so please ask me to clarify anything that doesn't make sense. I have a template interface and function that returns functions that return anonymous inner classes based on a compile-time argument:

interface MyInterface {
    void getName();
}
MyInterface function() getMyInterfaceFactory(string name)() {
    return function() {
        return new class MyInterface {
            void getName() { //Do something involving name here }
        };
    };
}

      

Now getMyInterfaceFactory()

used getMyInterface()

and it is used to directly return an anonymous object. Everything worked fine. When I added factory functions, I started getting an exception when running from Object:

object.Exception.....(102): need opCmp for class mymodule.getMyInterfaceFactory!("someargument").getMyInterfaceFactory.__funcliteral14.__anonclass13

      

So I looked at the throwing line in the druntime source and it looks like the default opCmp implementation for Object just throws. I am not comparing factory functions or MyInterface

anywhere else. I store factories as string indexed associative array values, but opCmp was not required when I was storing anonymous classes directly in this array, only when I started storing functions. If I insert the opCmp (using a memory address) everything seems to work fine, but MyInterface is not very comparable, so I would rather not do it unless I have to. If possible, I would like to know why / where opCmp is being called in anonymous classes, and how I can prevent or work around it.

Note. The default implementation of opCmp in the default object includes a comment vaguely referencing the error, a mapped comparison of memory addresses, and then a thrown version.

Thank!

Edit: I have to mention, I tried both windbg and ddbg to track where opCmp was called, but in both cases it failed. Windbg didn't give any useful information because it stubbornly refused to load any symbols, ddbg loaded symbols, but an exception occurs during initialization (after the static module constructors, but before the main one) and presumably ddbg did not have access to druntime symbols?

+3


source to share


1 answer


Update: I am having problems reproducing the opCmp error, especially in the toy examples, but I think I figured out what was going on.
It seems that creating anonymous inner classes inheriting interfaces inside anonymous functions is a bug (go figure). In particular, anonymous classes and don't relate very well to virtual functions. Even with opCmp set, I had errors with toString and default constructors, and they had members that just don't do anything (but don't get called or called when called). __traits(allMembers, MyInterface)

returns the expected information, as well as __traits(allMembers, typeof(anonInstance))

, but invoking frequently enumerated members does not work. Weird.
But if I change the interface to a class with abstract methods, the opCmp error is resolved, the anonymous class behaves as expected, etc. I don't know much about compilers, but I think that at compile time, a symbol table is generated that maps virtual function names to memory addresses stored in vtbl. I think what is happening is that the generated map changes when the anonymous class returned from the interface is returned. This is possible because interfaces support multiple inheritance and therefore cannot enforce absolute matching to vtbl. However, classes may require all descendants to adhere to the same mapping scheme (I don't know if they do, but they can), and therefore anonymous classes cannot end up with another mapping.
Again, I'm not really sure, but it seems to be this symptom caused by opCmp, although I haven't used it anywhere. I don't think this is exactly the opCmp issue, I think all virtual functions defined in Object are vulnerable. I was able to support this with the following:

testopcmphelper.d
interface TestInterface {
    string helloWorld();
}
class TestClass {
    abstract string helloWorld();
}

testopcmp.d
import testopcmphelper;
import std.stdio;

void invokeFn(TestInterface function() f) {
    auto t = f();
    auto s = t.helloWorld();
    writeln(s);
}

unittest {
    auto f = function() {
        return new class TestInterface {
            string helloWorld() {
                return "Hello World!";
            }
        };
    };
    invokeFn(f);
}

void invokeFn(TestClass function() f) {
    auto t = f();
    auto s = t.helloWorld();
    writeln(s);
}

unittest {
    auto f = function() {
        return new class TestClass {
            string helloWorld() {
                return "Goodbye World!";
            }
        };
    };
    invokeFn(f);
}

      

What prints:



src.utilities.testopcmp.__unittest2.__funcliteral1.__anonclass10
Goodbye World!

      

By specifying what invokeFn(TestInterface)

is calling Object.toString

instead TestInterface.helloWorld

.

I'm going to leave the question open for another day if I make a mistake. I will probably report this as a bug in the DMD. I'll get around the problem by only using abstract classes for the anonymous types of factory functions. TL; DR This seems to be a bug.

0


source







All Articles