This Week in D October 9, 2016

Welcome to This Week in D! Each week, we'll summarize what's been going on in the D community and write brief advice columns to help you get the most out of the D Programming Language.

The D Programming Language is a general purpose programming language that offers modern convenience, modeling power, and native efficiency with a familiar C-style syntax.

This Week in D has an RSS feed.

This Week in D is edited by Adam D. Ruppe. Contact me with any questions, comments, or contributions.

Statistics

Major Changes

Beta 2.072.0-b2 and LDC 1.1.0-beta3 were released this week.

In the community

Community announcements

See more at the announce forum.

Editorial

I want to write a little about the -betterC switch to dmd today. This switch started out as an undocumented hack that Walter used for testing code, but it was eventually discovered by the community and is now documented - though still unsupported.

Walter is considering changing that, and I have mixed feelings on it. On one hand, yeah, I think I'd enjoy using it if it worked well, but on the other hand, virtually all my practical, real world code wouldn't touch it.

However, on the gripping hand, having something fun to play with is a way to get developers excited about the language, which, in turn, can convert them to become true missionaries for the church of D, and that's something I want. So let's take a look at this.

I find -betterC to be disappointing right now because:

import core.stdc.stdio;

extern(C)
int main() {
    printf("Hello, world!\n");
    return 0;
}

Fails to compile with any version of dmd without druntime on Linux (it does, however, work on Windows! I haven't tried other systems). dmd test.d -betterC -defaultlib= gives undefined reference to '_d_dso_registry'. _d_dso_registry is an internal druntime function that gets called with information about the executable or shared object - it is part of D's dll support and generally useful, but I don't think it should be required for a C hello world program.

Well, that's easy to hack around, we can define the function to do nothing:

version(linux) extern(C) __gshared void _d_dso_registry() {}

Add that, and now it compiles and runs. Cool! Though, this step should not be necessary. If we want to promote -betterC (Walter told me via email that he would like a better name if we actually start promoting this, by the way, and the internal team does have one in mind: DPrime), it should just work out of the box at least for a C-style hello.

But, we need to go a step further. D without structs is almost useless; struct is a must-have, even C has that! So, let's add one:

struct Foo {}
extern(C) int main() {
  Foo foo;
  return 0;
}
version(linux) extern(C) __gshared void _d_dso_registry() {}

Same compile: dmd -betterC -defaultlib= test.d.... new error: undefined reference to '_D15TypeInfo_Struct6__vtblZ'. Yes, it insists on generating typeinfo, which the compiler makes a subclass of a parent class, which is assumed to be defined in druntime. Since we aren't using druntime, the reference to the parent class causes a linker error.

This needs to change. As you use more types, more typeinfo linker errors will pop up. If we want to enjoy using this switch, all this needs to just work - the compiler must suppress generation of type info unless it is sure it will actually be necessary. (Such as using classes - typeinfo is needed for dynamic casting, so if you declare a class, it is fair game. At that point, my preference would be for the compiler to magically generate all the info - I'm not sure why it is library defined at all, since the library definition must exactly match the compiler's layout anyway. But, I can also live with it generating the data and issuing the linker error. The developer can then bring in some of the info from druntime or implement it themselves. Yes, I see this as being an advanced hacker's toy, where linker errors are allowed to happen and the user is assumed to know how to deal with it. I am just annoyed when the happen unnecessarily.)

A recent commit to dmd made it stop emitting exception handling info unless it was actually needed. I applaud this, and believe it helps here (though to be honest, I haven't proven it, I accidentally overwrote my old dmd with a new dmd so I didn't do a fair before and after comparison) - we don't need to add any exception handling hacks unless we want exceptions....

...almost. Exception handling code is actually used in dmd in two places that you might not expect: scope guard statements and destructors. Go back to our struct example and add a destructor. You'll get a link error: r.o:(.eh_frame+0x6b): undefined reference to '__dmd_personality_v0' which is referring to DWARF exception info (or Symbol Undefined __d_framehandle on Windows, same thing, different platform).

Yes, internally, the compiler rewrites struct destructors (as well as scope guards) into try/finally blocks, meaning it reuses the exception handling logic. (BTW, don't freak out about this, the implementation is quite efficient.) I'm not sure what is best here... on the one hand, C doesn't have struct destructors, so we aren't behind, but they are REALLY nice to have, so I'd love to see them work too.

Finally, some of the stuff like assert and invariant blocks may trigger runtime code, and assert in particular pops up in a few places unless you use -release to dmd. I have said before that I don't like -release and very rarely recommend its use because the benefits of bounds checking and assertions IMO FAR outweigh the cost in 98% of cases... but this is one of those situations where I do think it is a fair thing to use. -release suppresses automatic checks, which again, just puts us on the same level as C, while keeping the door open to stop using the switch and bring in assert functions from druntime when you want it.

In summary, I cannot promote -betterC today... but I could, given the following conditions:

Hello world with a struct compiles and links out-of-the-box using dmd -betterC -defaultlib= -release.

Yes, that's all I ask. Here's the concrete items I see as needed:

That isn't a long list. TypeInfo is the hardest one - it might necessitate = void initialization by the user in cases of structs with non-zero initializers... but I'm OK with that. Actually, I kinda like it. (All-zero initializers do not need an init block, the compiler just memset(0) it to auto initialize. float and char data types are the most common non-zero initializers, and you can force them to be zero with =0 in the struct definition. So this is easy to work with.)

I can't promise Walter would accept patches... but the impression I get from our emails is that he would, and if these things started to work, you'd have my backing too.

Again, I don't think this is super useful in real life, so I wouldn't spend a great deal of time on this... but I do honestly think it'd give a fun boost to a bunch of us, so it is reasonably high bang for buck.

Any takers?

Learn more about D

To learn more about D and what's happening in D: