5

Consider the following code:

static if (!is(MyStruct))
{
    struct MyStruct
    {
    }
}

static if (is(MyStruct))
{
    static assert(0);
}

My original understanding has been that the order of declarations (in global scope) does not matter in D.

However, in this case, the order of the static ifs makes the difference between whether or not the program compiles.

Is D's compile-time evaluation stage, therefore, a procedural feature (like C/C++), a declarative feature, or something else? What is it currently, and what is it planned to be (if the two are different)?


Edit:

I just realized, the problem doesn't even end here. What happens of a static if uses .tupleof to enumerate the members of the current module, and create the same type of problem?

Community
  • 1
  • 1
user541686
  • 205,094
  • 128
  • 528
  • 886

2 Answers2

2

It's a declarative feature that has procedural properties as a side effect of the implementation.

FeepingCreature
  • 3,648
  • 2
  • 26
  • 25
  • I think there has been some talk of making that example code illegal. Either rejecting it outright or declaring it undefined behavior. – BCS Oct 28 '11 at 13:21
  • @BCS: Isn't it impossible to detect? At least when mixins come into play, it looks like the halting problem.... – user541686 Oct 28 '11 at 14:10
  • @Mehrdad Not necessarily, especially in the common cases. One approach would be to have `is()` "booby trap" the symbol table and then consider it an error to insert a symbol that would change the value of an already evaluated `static if` – BCS Oct 28 '11 at 14:54
  • @BCS: How would that work correctly, if two modules had cross-dependencies and they both checked for each others' types? Which one is first? Should it be important? Should one be able to block the compilation of the other? – user541686 Oct 28 '11 at 18:25
  • @Mehrdad: Being in different modules wold be irrelevant, the OP's code illustrates the point: Say the second `if` runs first, it does nothing and then the first `if` runs, "traps" the symbol `MyStruct`, and then tries to add that simple tripping the trap and gives an error message. If things happen the other way, the same error message is given without even getting to the 2nd `if`. -- OTOH this works by making the most useful version of the problem illegal. – BCS Oct 28 '11 at 20:20
  • @Mehrdad: lol, I'm the OP himself. :P Yes, we're saying the same thing -- being in the same module is irrelevant to the problem; that's my point. The problem happens either way. But now the trouble is that an entire module can be prevented from being compiled *from another module*, just because of a collision -- which seems a little weird of an idea to me. Maybe it's just me, though, idk... – user541686 Oct 28 '11 at 22:55
  • @Mehrdad: Cross module interference already exists because trying to use a (non fully qualified) symbol that is defined from multiple modules triggers an error. I think the solution for that case is `static import` or something like that. – BCS Oct 28 '11 at 23:47
  • @BCS: I just realized another reason why you can't make it "booby trap" so easily: you can bypass `is(MyStruct)` with `is(typeof({ MyStruct a; }))` or anything arbitrarily complicated. Can/should the compiler figure all this out? – user541686 Oct 31 '11 at 02:37
  • @Mehrdad The booby trap would be set on every symbol lookup is done during the evaluation of the conditional on a `static if` no matter how far from the `if` it is. – BCS Oct 31 '11 at 05:40
2

It gets complicated. It's essentially declarative, but order can still matter when a static if introduces a new symbol. Aside from that, I don't believe that it ever matters, but as your example shows, when you introduce a new symbol in a static if, and another static if uses it, the order definitely can matter.

There has been some discussion recently about how to make it as consistent and intuitive as possible. So, particularly in corner cases, the situation may change in the near future. But I would expect that your example would continue to trigger the static assert. The question is whether it will start to trigger the static assert if you reverse the order of the static if blocks, and I'm not sure that that's really been decided yet. The discussion on it in the compiler's newsgroup isn't entirely conclusive and a bit hard to follow IMHO, so I can't say for sure. But I expect that ordering will still matter in at least some cases which involve a static if block introducing a new symbol.

EDIT:

This was recently posted by one of dmd's primary contributors:

At present, the order of compile-time evaluation is not defined; DMD currently does it vaguely in lexical order but that is planned to change in the near future. 'static if' and 'mixin' will be evaluated in lexical order, before anything else is done. Afterwards, everything else will be evaluated on-demand.

Apart from the "static if/mixin" pass, compilation can proceed in parallel (though the current implementation doesn't yet do this) which means there's no ordering (multiple items may complete compilation simultaneously).

So, hopefully that clarifies things.

Jonathan M Davis
  • 37,181
  • 17
  • 72
  • 102
  • I think the issue isn't quite with the `static if` per se -- I think it could come up with template matching and aliases, changing the behavior of the code rather unexpectedly, right? – user541686 Oct 28 '11 at 18:29
  • It's not exactly the same with them, but it's related and similar. The order of declaration of templates has no effect on template constraints, but the order of `static if`s can affect them, and if they introduce new symbols, that can have an effect in the same way as `static if`. I don't believe that the order of `alias`es has any effect, because they don't involve conditional compilation. They simply introduce a new symbol (which could then affect other `static if`s if they're inside of a `static if`, but their order has no effect in and of themselves). – Jonathan M Davis Oct 28 '11 at 19:34