test.d
------
struct S
{
~this();
}
------
Compile with windows DMD, eg: dmd -m64 -main test.d
Get:
test.obj : error LNK2001: unresolved external symbol _D4test1S6__dtorMFZv
test.exe : fatal error LNK1120: 1 unresolved externals
If build `-m32`, no problem.
If you change `~this()` to `this(int)`, no problem.
If you change `~this()` to `void fun()`, no problem.
Something special about the destructor causes this link error, even though the function is never referenced.
This is blocking std::string, std::vector, it's impossible to build druntime.
Comment #1 by r.sagitario — 2019-07-25T09:37:44Z
The problem is that the dtor is referenced by the TypeInfo for S and that the MS linker doesn't eliminate the reference to it before removing unused COMDATs due to the default /OPT:REF. optlink is more aggressive and works the other way araound.
Even if the MS linker behaved the same, the default for /DEBUG builds is /OPT:NOREF so the issue would reappear, and more so with debug info generation (optlink also reports the unresolved symbol in that case).
The online compiler (probably linux) also reports an unresolved symbol, so the problem is not Windows specific.
For the MS linker, a workaround might be to add a pragma(linkerDirective, "/ALTERNATENAME:...") to weakly redirect the symbol to a dummy function.
Comment #2 by turkeyman — 2019-07-25T17:01:19Z
I suspected the TypeInfo, but shouldn't that only contain destructor for classes?
In my case, the struct is extern(C++) too, which means the D TypeInfo shouldn't even exist?
Comment #3 by kinke — 2019-07-25T18:17:51Z
(In reply to Manu from comment #2)
> I suspected the TypeInfo, but shouldn't that only contain destructor for
> classes?
Nope, it's needed to implement the `destroy(void*)` method.
> In my case, the struct is extern(C++) too, which means the D TypeInfo
> shouldn't even exist?
That seems to be a common misconception. Without D TypeInfo, you'd lose the ability to have dynamic arrays of that extern(C++) struct, associative arrays with that struct as key type etc. etc.
Comment #4 by turkeyman — 2019-07-25T18:43:21Z
(In reply to kinke from comment #3)
> (In reply to Manu from comment #2)
> > I suspected the TypeInfo, but shouldn't that only contain destructor for
> > classes?
>
> Nope, it's needed to implement the `destroy(void*)` method.
What is that? I don't want it...
> > In my case, the struct is extern(C++) too, which means the D TypeInfo
> > shouldn't even exist?
>
> That seems to be a common misconception. Without D TypeInfo, you'd lose the
> ability to have dynamic arrays of that extern(C++) struct,
Why?
> associative
> arrays with that struct as key type etc. etc.
Why?
Comment #5 by r.sagitario — 2019-07-25T18:51:52Z
> Nope, it's needed to implement the `destroy(void*)` method.
I don't think it is needed for destroy, the dtor can be called directly.
>>Without D TypeInfo, you'd lose the ability to have dynamic arrays of
>> that extern(C++) struct, associative arrays with that struct as key
>> type etc. etc.
> Why?
It doesn't have to, but that's how dynamic and associative arrays are currently implemented. There's a GSoC project trying to reduce that dependency.
The typeinfo is also needed to finalize structs that are allocated on the GC heap.
Comment #6 by turkeyman — 2019-07-25T21:59:04Z
Well... there's no way to extern to a struct in that case.
What's the workaround?
Comment #7 by kinke — 2019-07-25T22:39:53Z
(In reply to Manu from comment #6)
> Well... there's no way to extern to a struct in that case.
> What's the workaround?
Well, define 'to extern'. How is the missing C++ implementation supposed to be there at link-time if I'm allocating a struct on the D side and need the dtor? E.g., is there a dependency on a certain C++ lib when importing your D module, or is the struct just designed to be allocated on the C++ side and passed by ref/pointer to the D side? In the latter case, you could annotate the dtor on the D side with @disable.
Comment #8 by r.sagitario — 2019-07-26T05:16:38Z
> What's the workaround?
If you can assume that the dtor is supplied as soon as you actually use the struct, this could work on Windows:
struct S
{
~this();
}
void dummyDtor()
{
// dtor not linked in!
assert(false);
}
pragma(linkerDirective, "/ALTERNATENAME:" ~ S.__dtor.mangleof ~ "=" ~ dummyDtor.mangleof);
IIRC there is some other way on Posix to get weak linkage.
Comment #9 by turkeyman — 2019-07-26T20:01:56Z
Mmm. I could use weak linkage, feels like a massive hack though. Situation is not-great.
Comment #10 by robert.schadek — 2024-12-13T19:04:43Z