Using a C++ class in a D associative array

Jacob Carlborg doob at me.com
Mon Aug 20 22:16:09 UTC 2018


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


More information about the Digitalmars-d mailing list