Thanks for the very good description, Nick! So if I understand correctly, if

1. I use an "auto" return value or suchlike in a module Y.d
2. module X.d calls this function
3. I call "dmd -c X.d" and "dmd -c Y.d" as separate steps

Then the compiler will have to fully parse Y twice and fully analyze the Y function twice, although it generates object code for the function only once. Right? I wonder how smart it is about not analyzing things it does not need to analyze (e.g. when Y is a big module but X only calls one function from it - the compiler has to parse Y fully but it should avoid most of the semantic analysis.)

What about templates? In C++ it is a problem that the compiler will instantiate templates repeatedly, say if I use vector<string> in 20 source files, the compiler will generate and store 20 copies of vector<string> (plus 20 copies of basic_string<char>, too) in object files.

1. So in D, if I compile the 20 sources separately, does the same thing happen (same collection template instantiated 20 times with all 20 copies stored)? 2. If I compile the 20 sources all together, I guess the template would be instantiated just once, but then which .obj file does the instantiated template go in?

$rdmd --build-only (any other flags) main.d

Then, RDMD will figure out *all* of the source files needed (using the full compiler's frontend, so it never gets fooled into missing anything), and if any of them have been changed, it will automatically
pass them *all* into DMD for you. This way, you don't have to
manually keep track of all your files and pass them all into
DMD youself. Just give RDMD your main file and that's it, you're golden.

Side note: Another little trick with RDMD: Omit the --build-only and it will compile AND then run your program:

Yes. (Unless you never import anything from in phobos...I think.) But it's very, very fast to parse. Lightning-speed if you compare it to C++.

I don't even want to legitimize C++ compiler speed by comparing it to any other language ;)

- Is there any concept of an incremental build?

Yes, but there's a few "gotcha"s:

1. D compiles so damn fast that it's not nearly as much of an issue as
it is with C++ (which is notoriously ultra-slow compared
to...everything, hence the monumental importance of C++'s incremental
builds).

I figure as CTFE is used more, especially when it is used to decide which template overloads are valid or how a mixin will behave, this will slow down the compiler more and more, thus making incremental builds more important. A typical example would be a compile-time parser-generator, or compiled regexes.

Plus, I've heard some people complaining that the compiler uses over 1 GB RAM, and splitting up compilation into parts might help with that.

BTW, I think I heard the compiler uses multithreading to speed up the build, is that right?

It keeps diving deeper and deeper to find anything it can "start" with. One it finds that, it'll just build everything back up in whatever
order is necessary.

I hope someone can give more details about this.

- In light of the above (that the meaning of D code can be interdependent with other D code, plus the presence of mixins and all that), what are the limitations of __traits(allMembers...) and other compile-time reflection operations, and what kind of problems might a user expect to encounter?

Shouldn't really be an issue. Such things won't get evaluated until the types/identifiers involved are *fully* analyzed (or at least to the extent that they need to be analyzed). So the results of things like __traits(allMembers...) should *never* change during compilation, or
when changing the order of files or imports (unless there's some
compiler bug). Any situation that *would* result in any such ambiguity
will get flagged as an error in your code.

Hmm. Well, I couldn't find an obvious example... for example, you are right, this doesn't work, although the compiler annoyingly doesn't give a reason:

struct OhCrap {
        void a() {}
        // main.d(72): Error: error evaluating static if expression
        //             (what error? syntax error? type error? c'mon...)
        static if ([ __traits(allMembers, OhCrap) ].length > 1) {
                auto b() { return 2; }
        }
        void c() {}
}

But won't this be a problem when it comes time to produce run-time reflection information? I mean, when module A asks to create run-time reflection information for all the functions and types in module A.... er, I naively thought the information would be created as a set of types and functions *in module A*, which would then change the set of allMembers of A. But, maybe it makes more sense to create that stuff in a different module (which A could then import??)

Anyway, I can't even figure out how to enumerate the members of a module A; __traits(allMembers, A) causes "Error: import Y has no members".

Aside: I first wrote the above code as follows:

// Shouldn't this be in Phobos somewhere?
bool contains(alias pred = "a == b", R, E)(R haystack, E needle)
    if (isInputRange!R &&
        is(typeof(binaryFun!pred(haystack.front, needle)) : bool))
{
        return !(find!(pred, R, E)(haystack, needle).empty);
}

struct OhCrap {
        void a() {}
        static if ([ __traits(allMembers, OhCrap) ].contains("a")) {
                auto b() { return 2; }
        }
        void c() {}
}

But it causes a series of 204 error messages that I don't understand.

Reply via email to