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