Currently phobos is going through a transition where DIP 1000 is being enabled. This presents a unique problem because when DIP 1000 is enabled, it will cause certain functions to be mangled differently. This means that if a module in phobos was compiled with DIP 1000 enabled and you don't enable it when compiling your application, you can end up with cryptic linker errors that are difficult to root cause.

This problem has exposed what I think to be a deeper problem with the way D handles precompiled modules. Namely:

Precompiled D libraries do not expose the "important compiler configuration" that was used to compile them. "Important compiler configuration" meaning what versions were used, whether unittest was enabled, basically anything that an application using it needs to know to properly interpret the module the same way it was interpreted when it was compiled.

For example, say you have a library foo with a single module.

module foo;

struct Foo
{
    int x;
    version (FatFoo)
    {
        private int[100] y;
    }
    void init() @safe nothrow
    {
        x = 0;
        version (FatFoo)
        {
            y[] = 0;
        }
    }
}

Now let's compile it:

dmd -c foo.d

Now let's use it:

import foo;

int main() @safe nothrow
{
    Foo foo;
    foo.init();
    return 0;
}

dmd main.d foo.o   (foo.obj for windows)
./main             (main.exe for windows)

It runs and we're good to go.  Now let's do something sinister...

dmd -version=FatFoo -c foo.d

Now compile and run our program again, but don't include the `-version=FatFoo`

dmd main.d foo.o   (foo.obj for windows)
./main             (main.exe for windows)

We've just stomped all over our stack and now it's just a pancake of zeros! Your results will be unpredictable but on my windows box main throws an exception even though the function is marked @safe and nothrow :)

The root of the problem in this situation comes back to the problem that DIP 1000 is currently having. The "important compiler configuration" used to compile our library is unknown. If we could take our precompiled library foo.o and see what compiler configuration was used to compile it, we wouldn't have this problem because we would have seen it was compiled with the "FatFoo" version. Then we would have interpreted the module we used to load it with the "FatFoo" version and avoided this terrible "pancake stack" :)

So what do people think? Is this something we should address? We could explore ways of including information in our pre-compiled libraries that the compiler could use to know how it was compiled and therefore how to interpret the modules the same way they were when being compiled. All object formats that I know of support sections that tools can use to inject information like this. We could also just tell people that they must make sure to use the same compiler configuration for their own applications that were used when their libraries were compiled. If they don't ensure this then all safety guarantees are gone...not ideal but less work for D right? :)




Reply via email to