I'm playing around with using the DMD frontend as a library. I wanted to store some nodes from the AST in a set. Since there doesn't seem to be a set container that is shipped with Phobos I wrapped an associative array and added some functions to use the AA as a set.

To my surprise when I did that my code started to behave strangely. It started to print out things like this:

foo 0x10bc70800
bar 0x10bc70c00

I've been banging my head against the wall for several days trying to figure out why the above output was printed. I've been modifying my own code and removed as much as possible, but it didn't contained any calls to any "write" or "printf" functions. The problem seemed to appear when I put an AST node in the set (associative array). So I started to look through the AST classes and looking for overrides of "toHash". None of the AST nodes override "toHash". Then I noticed a suspicious "printf" call in a "print" method in the RootObject class in the DMD source code [1] (this class is the root of all classes in DMD). I removed the call to "printf" and now the output was gone. Trying to figure out what was calling this "print" method, that had called "printf", I added a failing assert. Thanks to the druntime printing out the stacktrace of an uncaught exception, I got this output (reduce):


??:? _d_assertp [0xd4d3311]
../../../.dub/packages/dmd-master/dmd/src/dmd/func.d:494 _ZN15FuncDeclaration5printEv [0xd41d97c] ??:? const nothrow @trusted ulong object.TypeInfo_Class.getHash(scope const(void*)) [0xd4c825f]
??:? _aaGetY [0xd4e5adc]
~/.dvm/compilers/dmd-2.081.0/osx/bin/../../src/druntime/import/object.d:326 pure nothrow @safe void dlp.core.set.Set!(dmd.func.FuncDeclaration).Set.put(dmd.func.FuncDeclaration) [0xd30cab9]

At the third line there's a call from object.TypeInfo_Class.getHash. I looked up to see what the "getHash" method is doing in druntime [2], the method looks like this:

override size_t getHash(scope const void* p) @trusted const
{
    auto o = *cast(Object*)p;
    return o ? o.toHash() : 0;
}

The method is basically calling "toHash" on the passed in object. But none of the AST nodes were overriding "toHas", hmm.

Then I started to think, all the AST nodes in DMD are C++ classes (both LDC and GDC are written in C++ and need to use the AST). Without really thinking of it I had put instance of C++ classes as keys in a D associative array. This is, for some reason, calling object.TypeInfo_Class.getHash which only expects to operate on D classes. I'm guessing that somehow what is usually the "toHash" method in a D class matched up with the "print" method in the C++ class.

All this just compiled without any error or warnings. No runtime exceptions or asserts were triggered. I just got a really weird behavior.

[1] https://github.com/dlang/dmd/blob/c3f6320d59ec14d6fa81a18f92b59393671b346a/src/dmd/root/rootobject.d#L55-L58

[2] https://github.com/dlang/druntime/blob/430585650930797369eeb5b3b142faba10572f10/src/object.d#L1712-L1716

--
/Jacob Carlborg

Reply via email to