static import object tricks

Posted 2022-07-18

Did you know you can static import object; and suppress the automatic implicit import? Let's play with it!

Core D Development Statistics

In the community

Community announcements

See more at the announce forum.

static import object

Iain Buclaw in a github comment pointed out that the D compilers will not implicitly import object; if your first thing, after the module definition, is your own static import object;

module test;
static import object;

void main() {}

Compiling will give you errors about undefined identifier _d_cmain here, which normally comes from object.d. Of course, you can just import the things it needs:

module test;
static import object;
import object : _d_cmain, string, idup;

void main() {
        string s = "foo".idup;
}

And make it work. But what's interesting about this is the reminder about how many things that we think are built into D, are actually definitions in that implicitly-imported object module.

The string type, for example, is not a language keyword. It is instead alias string = immutable(char)[]; in module object. Same with the functions dup, idup, and many others.

And yes, you can override them locally:

module test;

alias string = int;

void main() {
        string s = "foo".idup;
}
Error: cannot implicitly convert expression idup("foo") of type string to int

This is because it goes through the same lookup rules as any other symbol. If you changed it to:

module test;

alias string = int;

void main() {
        object.string s = "foo".idup;
}

It now looks up through the object module and makes it work.

Let's go back to the static imported object, to see what other things we might assume are built in but actually aren't.

module test;
static import object;

import object : _d_cmain;

void main() {
        object.string s = "foo";
}

Hmmm... what is that _d_cmain?

A template containing C main and any call(s) to initialize druntime and call D main. Any module containing a D main function declaration will cause the compiler to generate a mixin _d_cmain(); statement to inject this code into the module.

From the ddoc (note that dpldocs.info does NOT generate pages for these, since it is in an internal package. You'll want to open up the source.)

The comment says "this code"... but as we saw with string, you can locally override many object.d references and we can see in the error message only a naked name. So what if...

module test;
// no need for these here, we'll just do a local override
// static import object;
// import object : _d_cmain;

template _d_cmain() {
        extern(C)
        int main() {
                return 0;
        }
}

void main() {
        import core.stdc.stdio;
        printf("Hello, D!\n");
}

Compile and run it, do we see Hello, D? Nope! The _d_cmain we defined overrode the original!

Fun fact, in the interpolation dip we wrote, we specifically had the compiler reference object._d_interpolated instead of just _d_interpolated to prohibit this kind of local override behavior. Apparently, however, dmd itself was not so strict.

What other fun things might be in object.d that we don't realize? Maybe we should play with this static import in some other modules and see what happens.

Why would you want this outside of playing? Well, the github thread it was brought up in was a user accidentally calling object.destroy instead of his own function. static import object; import object : list, of, things; was suggested as one way to take full control of the namespace.

I generally see that as more hassle than it is worth, but still, it might be useful to you somewhere, so nice to be aware it is possible.