January 17, 2007
Hi,

Many structures in the win32 bindings project on dsource set their size using D's default initializer:

#struct STARTUPINFOA {
#	DWORD  cb = STARTUPINFOA.sizeof;
#	LPSTR  lpReserved;
#//...
#}

This does save some typing when in Win32 projects, but results in unexpected linker errors (dmd 1.0):

 Error 42: Symbol Undefined _D5win327winbase12STARTUPINFOA6__initZ

These errors can only be solved by
1) adding the appropriate .d files from the win32 folder on the command line, or
2) building a win32.lib (containing all initializers) and linking to it;
3) commenting out the "= ...sizeof;" part.

All of which seem inappropriate for a bindings project that merely contains translated headers.

One solution might be to add a version(..) around all default initializer, providing both an initialized and uninitialized version.

Another, far better, solution would be to change DMD: the compiler knows the initializer and can safely inline it. What's more, the there are no linker errors for the default built-in initializers:

struct S { int s; }    // no linker error
struct D { int d=0; }  // linker error!

The linking error occurs even with the -O -release -inline flags.

Why does DMD (I don't know about GDC) require that __init symbol for custom initializers and not for the default ones? Is there any chance that this requirement will be dropped?

L.
January 17, 2007
Lionello Lunesu wrote:
> What's more, the there are no linker errors for the default built-in initializers:
> 
> struct S { int s; }    // no linker error
> struct D { int d=0; }  // linker error!
> 
> The linking error occurs even with the -O -release -inline flags.
> 
> Why does DMD (I don't know about GDC) require that __init symbol for custom initializers and not for the default ones? Is there any chance that this requirement will be dropped?

Actually, it's not (only) about custom initializers:
*****
urxae@urxae:~/tmp$ cat test.d
import test2;
import std.stdio;

void main()
{
    S s;
    writefln(cast(int)s.c);
}
urxae@urxae:~/tmp$ cat test2.d
struct S { char c; }
urxae@urxae:~/tmp$ dmd test.d
gcc test.o -o test -m32 -lphobos -lpthread -lm -Xlinker -L/home/urxae/opt/dmd/lib
test.o: In function `_Dmain':
test.d:(.gnu.linkonce.t_Dmain+0x7): undefined reference to `_D5test21S6__initZ'
collect2: ld returned 1 exit status
--- errorlevel 1
*****

(Similarly fails for wchar, dchar, float, double and real)
It looks to be about everything but *all-zero* default initializers. Those seem to be the only ones not to need explicit initializers to be generated if the type is used as a local variable. Their initialization is inlined.

For some reason the inlining doesn't happen for explicitly zero-initialized variables, nor for non-zero or non-default initializers.

This despite the fact that global/static variables seem to get the initializer "inlined" into the '.data' or (if all-zero) '.bss' section[1].
So it's not that the compiler doesn't know what the initializers should be, it's that the code generated to initialize local variables at run-time depends on the initializer data being generated by compilation of the imported module if it's either non-default or not all-zero.

Preventing your link error would be a simple matter of treating initializer symbols similar to templates: put them in link-once sections in every object file where they're needed. Not that I'm saying this should necessarily be done; I'm just saying it should be relatively simple to implement.


[1]: At least, those are the names on Linux (and anywhere else that uses ELF binaries, for that matter). Not sure what they are on Windows, but the principle should be similar.