digitalmars.D - static assert(0) in template is a disaster
- Nils Lankila (19/47) Jun 16 2020 Today I've decided to test the new -vtemplates feature.
- rikki cattermole (1/1) Jun 16 2020 Bug report please, this may be an issue that can be fixed.
- Stefan Koch (2/6) Jun 16 2020 Are you sure it's not the T.stringof ?
- Nils Lankila (2/9) Jun 16 2020 yes. I've removed it after changing the else statement.
- Stefan Koch (3/13) Jun 16 2020 Thank you very much for finding this.
- Nils Lankila (11/27) Jun 16 2020 The problem is quite clear.
- Paul Backus (14/35) Jun 16 2020 Unfortunately this is not true:
- Nils Lankila (2/29) Jun 16 2020 that's unfortunate, looks like the problem cant be solved then.
- Dennis (15/30) Jun 16 2020 In my opinion, the fact that that compiles is a blatant bug.
- Paul Backus (21/55) Jun 16 2020 I agree that this is a bug, but it is a bug in the design of the
- Andrei Alexandrescu (3/5) Jun 16 2020 Is that a sort of a compile-time race condition? If that's the case, D
- Stefan Koch (9/15) Jun 16 2020 That is ......
- Avrina (21/37) Jun 16 2020 It's more than a order-dependency issue.
- Stefan Koch (9/49) Jun 16 2020 That's because of how pragma(msg) works.
- Avrina (24/82) Jun 17 2020 It's illogical, the size of Foo changes because it is querying it
- Stefan Koch (5/30) Jun 19 2020 For all static if's the length to the tuple is 1.
- Avrina (12/49) Jun 19 2020 Not sure what you mean, it's wrong, just plain wrong, it doesn't
- Timon Gehr (12/19) Jun 17 2020 Not exactly as there is a clear ordering of declarations. Rather, it's a...
- Stefan Koch (5/20) Jun 17 2020 What classifies as D code as ill-defined?
- Andrei Alexandrescu (4/28) Jun 17 2020 Yah that's what I had in mind. Problem is made worse if the definitions
- Paul Backus (6/12) Jun 17 2020 Unfortunately it is quite easy to run into this sort of thing in
- Dennis (41/71) Jun 17 2020 I don't think it's fundamentally unavoidable. Here's a way to
- Tim (21/28) Jun 17 2020 A similar problem can even happen, when porting C code to D,
Today I've decided to test the new -vtemplates feature. I don't know if what i've found is already known but it looks like a dormant disaster in phobos. So if you compile dparse with -vtemplate you'll get for example this strange thing:694 694 AliasThisTypeOf(T) if (isAggregateType!T)That's for the less very suspicious: 694 instantiations, 694 unique instances ! So I've put a pragma msg in the template to check that every template parameter passed is unique. It's definitively not the case. `AliasThisTypeOf` is really a simple template that wraps a compiler `__traits`:private template AliasThisTypeOf(T) if (isAggregateType!T) { alias members = __traits(getAliasThis, T); static if (members.length == 1) { alias AliasThisTypeOf = typeof(__traits(getMember, T.init, members[0])); } else static assert(0, T.stringof~" does not have alias this type"); }The problem can only comes from the assertion. So I've changed the implementation toprivate template AliasThisTypeOf(T) if (isAggregateType!T) { alias members = __traits(getAliasThis, T); static if (members.length == 1) { alias AliasThisTypeOf = typeof(__traits(getMember, T.init, members[0])); } else alias AliasThisTypeOf = AliasSeq!(); }And recompiled dparse. The results look more coherant now:694 167 AliasThisTypeOf(T) if (isAggregateType!T)conclusion: avoid static `assert(0)` in templates bodies. They prevent instance uniqueness. `std.traits` contains plenty of them and they should be removed in favor of something similar to what's done here.
Jun 16 2020
Bug report please, this may be an issue that can be fixed.
Jun 16 2020
On Tuesday, 16 June 2020 at 14:05:28 UTC, Nils Lankila wrote:Today I've decided to test the new -vtemplates feature. I don't know if what i've found is already known but it looks like a dormant disaster in phobos. [...]Are you sure it's not the T.stringof ?
Jun 16 2020
On Tuesday, 16 June 2020 at 14:19:06 UTC, Stefan Koch wrote:On Tuesday, 16 June 2020 at 14:05:28 UTC, Nils Lankila wrote:yes. I've removed it after changing the else statement.Today I've decided to test the new -vtemplates feature. I don't know if what i've found is already known but it looks like a dormant disaster in phobos. [...]Are you sure it's not the T.stringof ?
Jun 16 2020
On Tuesday, 16 June 2020 at 14:22:25 UTC, Nils Lankila wrote:On Tuesday, 16 June 2020 at 14:19:06 UTC, Stefan Koch wrote:Thank you very much for finding this. I'll be having a look at this shortly.On Tuesday, 16 June 2020 at 14:05:28 UTC, Nils Lankila wrote:yes. I've removed it after changing the else statement.Today I've decided to test the new -vtemplates feature. I don't know if what i've found is already known but it looks like a dormant disaster in phobos. [...]Are you sure it's not the T.stringof ?
Jun 16 2020
On Tuesday, 16 June 2020 at 14:26:18 UTC, Stefan Koch wrote:On Tuesday, 16 June 2020 at 14:22:25 UTC, Nils Lankila wrote:The problem is quite clear. If a template fails once to instantiate with a given set of template parameters then it will always fail with the same set of parameters. But if failed instantiation are not stored inOn Tuesday, 16 June 2020 at 14:19:06 UTC, Stefan Koch wrote:Thank you very much for finding this. I'll be having a look at this shortly.On Tuesday, 16 June 2020 at 14:05:28 UTC, Nils Lankila wrote:yes. I've removed it after changing the else statement.Today I've decided to test the new -vtemplates feature. I don't know if what i've found is already known but it looks like a dormant disaster in phobos. [...]Are you sure it's not the T.stringof ?TemplateInstance[TemplateInstanceBox] instances;That's sure that this is tried again and again... Maybe something like an `ErroneousTemplateInstance` class could be added as it's probably not reasonable to have a TemplateInstance instance for something that is not usable. Then this failed instance could be stored in an additional map, e.gErroneousTemplateInstance[TemplateInstanceBox] failedInstances;
Jun 16 2020
On Tuesday, 16 June 2020 at 16:57:50 UTC, Nils Lankila wrote:On Tuesday, 16 June 2020 at 14:26:18 UTC, Stefan Koch wrote:Unfortunately this is not true: template example(T) { static assert(__traits(hasMember, T, "x")); alias example = T.x; } struct S { static if (!__traits(compiles, example!S)) { int x; } } static assert(is(typeof(example!S) == int));On Tuesday, 16 June 2020 at 14:22:25 UTC, Nils Lankila wrote:The problem is quite clear. If a template fails once to instantiate with a given set of template parameters then it will always fail with the same set of parameters.On Tuesday, 16 June 2020 at 14:19:06 UTC, Stefan Koch wrote:Thank you very much for finding this. I'll be having a look at this shortly.On Tuesday, 16 June 2020 at 14:05:28 UTC, Nils Lankila wrote:yes. I've removed it after changing the else statement.Today I've decided to test the new -vtemplates feature. I don't know if what i've found is already known but it looks like a dormant disaster in phobos. [...]Are you sure it's not the T.stringof ?
Jun 16 2020
On Tuesday, 16 June 2020 at 17:06:33 UTC, Paul Backus wrote:On Tuesday, 16 June 2020 at 16:57:50 UTC, Nils Lankila wrote:that's unfortunate, looks like the problem cant be solved then.On Tuesday, 16 June 2020 at 14:26:18 UTC, Stefan Koch wrote:Unfortunately this is not true: template example(T) { static assert(__traits(hasMember, T, "x")); alias example = T.x; } struct S { static if (!__traits(compiles, example!S)) { int x; } } static assert(is(typeof(example!S) == int));On Tuesday, 16 June 2020 at 14:22:25 UTC, Nils Lankila wrote:The problem is quite clear. If a template fails once to instantiate with a given set of template parameters then it will always fail with the same set of parameters.[...]Thank you very much for finding this. I'll be having a look at this shortly.
Jun 16 2020
On Tuesday, 16 June 2020 at 18:14:26 UTC, Nils Lankila wrote:In my opinion, the fact that that compiles is a blatant bug. However, the D specification is not thorough enough to conclusively say. Even if we define it better and patch the compiler, unfortunately the paradoxical "if this doesn't compile yet, make it compile" pattern has been adopted in the wild so it would be a breaking change. To see why it's problematic, ask yourself why this compiles: ``` static assert(x == 3); static if (!is(typeof(x))) immutable x = 3; static if (!is(typeof(x))) immutable x = 5; ``` I've written about this in more detail before: https://forum.dlang.org/post/tlpogchogwsopswgbpdu forum.dlang.orgtemplate example(T) { static assert(__traits(hasMember, T, "x")); alias example = T.x; } struct S { static if (!__traits(compiles, example!S)) { int x; } } static assert(is(typeof(example!S) == int));that's unfortunate, looks like the problem cant be solved then.
Jun 16 2020
On Tuesday, 16 June 2020 at 22:16:34 UTC, Dennis wrote:On Tuesday, 16 June 2020 at 18:14:26 UTC, Nils Lankila wrote:I agree that this is a bug, but it is a bug in the design of the language itself, not the implementation. You write in your linked post thatIn my opinion, the fact that that compiles is a blatant bug. However, the D specification is not thorough enough to conclusively say. Even if we define it better and patch the compiler, unfortunately the paradoxical "if this doesn't compile yet, make it compile" pattern has been adopted in the wild so it would be a breaking change. To see why it's problematic, ask yourself why this compiles: ``` static assert(x == 3); static if (!is(typeof(x))) immutable x = 3; static if (!is(typeof(x))) immutable x = 5; ``` I've written about this in more detail before: https://forum.dlang.org/post/tlpogchogwsopswgbpdu forum.dlang.orgtemplate example(T) { static assert(__traits(hasMember, T, "x")); alias example = T.x; } struct S { static if (!__traits(compiles, example!S)) { int x; } } static assert(is(typeof(example!S) == int));that's unfortunate, looks like the problem cant be solved then.The 'before' and 'after' are implementation details showing up as a result of underspecification. Module level declarations are supposed to be order invariant....but in fact, the existence of 'before' and 'after' states is an unavoidable consequence of how `static if` works. The condition of a `static if` statement *must* be evaluated before the body is processed by the compiler. Without some mechanism for controlling the order in which declarations undergo semantic analysis, it would be impossible to implement `static if` in the first place. In more principled languages (e.g., Lisps), this ordering is explicit, and chosen by the programmer: a macro takes the 'before' state as its input and produces the 'after' state as its output, and the programmer is free to compose as many of them as they like in any order. In D, the ordering is implicit, and chosen by the compiler via on-demand semantic analysis, but apart from that it is fundamentally the same. The ugly truth here is that Walter has designed himself into a corner: module-level declarations are not order invariant in D, and never will be.
Jun 16 2020
On 6/16/20 7:01 PM, Paul Backus wrote:I agree that this is a bug, but it is a bug in the design of the language itself, not the implementation.Is that a sort of a compile-time race condition? If that's the case, D should deem the program ill-formed with no diagnostic required.
Jun 16 2020
On Wednesday, 17 June 2020 at 01:26:48 UTC, Andrei Alexandrescu wrote:On 6/16/20 7:01 PM, Paul Backus wrote:That is ...... I lack the words. I must be misunderstanding. Are you saying that it is fine for D to act differently on code that used to compile fine, because we don't want to detect order-dependency issues? Please clearify.I agree that this is a bug, but it is a bug in the design of the language itself, not the implementation.Is that a sort of a compile-time race condition? If that's the case, D should deem the program ill-formed with no diagnostic required.
Jun 16 2020
On Wednesday, 17 June 2020 at 01:34:18 UTC, Stefan Koch wrote:On Wednesday, 17 June 2020 at 01:26:48 UTC, Andrei Alexandrescu wrote:It's more than a order-dependency issue. pragma(msg, Foo.tupleof.length); // prints 1 struct Foo { int a; static if(Foo.tupleof.length == 1) { int b; } static if(Foo.tupleof.length == 2) { int c; } } pragma(msg, Foo.tupleof.length); // prints 1 void main() { writeln(Foo.tupleof.length); // prints 2 } If you try to do the same thing with "Foo.sizeof == 4/8" in the `static if`'s you get a compile error. Some attempt was made to prevent this situation, but not much. It doesn't make sense to probe information that isn't known at the time, or cannot be known at any time in that scope. It is ill-formed.On 6/16/20 7:01 PM, Paul Backus wrote:That is ...... I lack the words. I must be misunderstanding. Are you saying that it is fine for D to act differently on code that used to compile fine, because we don't want to detect order-dependency issues? Please clearify.I agree that this is a bug, but it is a bug in the design of the language itself, not the implementation.Is that a sort of a compile-time race condition? If that's the case, D should deem the program ill-formed with no diagnostic required.
Jun 16 2020
On Wednesday, 17 June 2020 at 03:04:43 UTC, Avrina wrote:On Wednesday, 17 June 2020 at 01:34:18 UTC, Stefan Koch wrote:That's because of how pragma(msg) works. It's evaluated eagerly. If you put a pragma(msg), inside your static if body it should print 2. static if is supposed to be able to introduce declarations, if the code you posted qualifies as legitimately as ill-formed, I am afraid there is lots and lots of code. Which does classify as ill-formed.On Wednesday, 17 June 2020 at 01:26:48 UTC, Andrei Alexandrescu wrote:It's more than a order-dependency issue. pragma(msg, Foo.tupleof.length); // prints 1 struct Foo { int a; static if(Foo.tupleof.length == 1) { int b; } static if(Foo.tupleof.length == 2) { int c; } } pragma(msg, Foo.tupleof.length); // prints 1 void main() { writeln(Foo.tupleof.length); // prints 2 } If you try to do the same thing with "Foo.sizeof == 4/8" in the `static if`'s you get a compile error. Some attempt was made to prevent this situation, but not much. It doesn't make sense to probe information that isn't known at the time, or cannot be known at any time in that scope. It is ill-formed.On 6/16/20 7:01 PM, Paul Backus wrote:That is ...... I lack the words. I must be misunderstanding. Are you saying that it is fine for D to act differently on code that used to compile fine, because we don't want to detect order-dependency issues? Please clearify.I agree that this is a bug, but it is a bug in the design of the language itself, not the implementation.Is that a sort of a compile-time race condition? If that's the case, D should deem the program ill-formed with no diagnostic required.
Jun 16 2020
On Wednesday, 17 June 2020 at 03:15:52 UTC, Stefan Koch wrote:On Wednesday, 17 June 2020 at 03:04:43 UTC, Avrina wrote:It's illogical, the size of Foo changes because it is querying it for information before the structure is even defined. Yes I imagine there is probably a lot of code that compiles that shouldn't. There's probably a lot of silent bugs, and from time to time when D changes and the order the of the semantic changes, code that once did compile won't compile anymore because the code never made sense in the first place. static assert(Foo.tupleof.length == 1); struct Foo { int a; static if(Foo.tupleof.length == 1) { int b; static assert(Foo.tupleof.length == 3); // ok } static if(Foo.tupleof.length == 1) { int c; } static assert(Foo.tupleof.length == 3); // ok static if(Foo.tupleof.length == 3) { int d; static assert(0); // never run } }On Wednesday, 17 June 2020 at 01:34:18 UTC, Stefan Koch wrote:That's because of how pragma(msg) works. It's evaluated eagerly. If you put a pragma(msg), inside your static if body it should print 2. static if is supposed to be able to introduce declarations, if the code you posted qualifies as legitimately as ill-formed, I am afraid there is lots and lots of code. Which does classify as ill-formed.On Wednesday, 17 June 2020 at 01:26:48 UTC, Andrei Alexandrescu wrote:It's more than a order-dependency issue. pragma(msg, Foo.tupleof.length); // prints 1 struct Foo { int a; static if(Foo.tupleof.length == 1) { int b; } static if(Foo.tupleof.length == 2) { int c; } } pragma(msg, Foo.tupleof.length); // prints 1 void main() { writeln(Foo.tupleof.length); // prints 2 } If you try to do the same thing with "Foo.sizeof == 4/8" in the `static if`'s you get a compile error. Some attempt was made to prevent this situation, but not much. It doesn't make sense to probe information that isn't known at the time, or cannot be known at any time in that scope. It is ill-formed.On 6/16/20 7:01 PM, Paul Backus wrote:That is ...... I lack the words. I must be misunderstanding. Are you saying that it is fine for D to act differently on code that used to compile fine, because we don't want to detect order-dependency issues? Please clearify.I agree that this is a bug, but it is a bug in the design of the language itself, not the implementation.Is that a sort of a compile-time race condition? If that's the case, D should deem the program ill-formed with no diagnostic required.
Jun 17 2020
On Wednesday, 17 June 2020 at 16:08:41 UTC, Avrina wrote:On Wednesday, 17 June 2020 at 03:15:52 UTC, Stefan Koch wrote:For all static if's the length to the tuple is 1. For all static asserts the length of the tuple is 3. You made it look more variant than it is. What we have to do is to formalize the behavior.[...]It's illogical, the size of Foo changes because it is querying it for information before the structure is even defined. Yes I imagine there is probably a lot of code that compiles that shouldn't. There's probably a lot of silent bugs, and from time to time when D changes and the order the of the semantic changes, code that once did compile won't compile anymore because the code never made sense in the first place. static assert(Foo.tupleof.length == 1); struct Foo { int a; static if(Foo.tupleof.length == 1) { int b; static assert(Foo.tupleof.length == 3); // ok } static if(Foo.tupleof.length == 1) { int c; } static assert(Foo.tupleof.length == 3); // ok static if(Foo.tupleof.length == 3) { int d; static assert(0); // never run } }
Jun 19 2020
On Friday, 19 June 2020 at 12:38:44 UTC, Stefan Koch wrote:On Wednesday, 17 June 2020 at 16:08:41 UTC, Avrina wrote:Not sure what you mean, it's wrong, just plain wrong, it doesn't matter if it's more or less wrong, it is still wrong. Like I said, if you use Foo.sizeof, you get a compiler error. That's what it should be, a compiler error because it is ill-formed. It's wrong for the same reason this is wrong: struct Foo { typeof(Foo.a) a; } It doesn't make sense. The compiler just allows incorrect logic. There's nothing to formalize to make it work in a way that makes sense.On Wednesday, 17 June 2020 at 03:15:52 UTC, Stefan Koch wrote:For all static if's the length to the tuple is 1. For all static asserts the length of the tuple is 3. You made it look more variant than it is. What we have to do is to formalize the behavior.[...]It's illogical, the size of Foo changes because it is querying it for information before the structure is even defined. Yes I imagine there is probably a lot of code that compiles that shouldn't. There's probably a lot of silent bugs, and from time to time when D changes and the order the of the semantic changes, code that once did compile won't compile anymore because the code never made sense in the first place. static assert(Foo.tupleof.length == 1); struct Foo { int a; static if(Foo.tupleof.length == 1) { int b; static assert(Foo.tupleof.length == 3); // ok } static if(Foo.tupleof.length == 1) { int c; } static assert(Foo.tupleof.length == 3); // ok static if(Foo.tupleof.length == 3) { int d; static assert(0); // never run } }
Jun 19 2020
On 17.06.20 03:26, Andrei Alexandrescu wrote:On 6/16/20 7:01 PM, Paul Backus wrote:Not exactly as there is a clear ordering of declarations. Rather, it's a self-contradicting program, like this one: static if(!is(typeof(x))) int x; This would be an example of a race condition: static if(!is(typeof(x))) int y; static if(!is(typeof(y))) int x;I agree that this is a bug, but it is a bug in the design of the language itself, not the implementation.Is that a sort of a compile-time race condition?If that's the case, D should deem the program ill-formed with no diagnostic required.Unfortunately, if dependency structures get a bit tricky, DMD will fail to correctly compile even well-formed programs. It's why I have refrained from going too crazy with metaprogramming after my experience with my D frontend that stopped compiling after DMD 2.060. (Ironically, it does produce a diagnostic for each of the examples discussed here.)
Jun 17 2020
On Wednesday, 17 June 2020 at 07:05:25 UTC, Timon Gehr wrote:On 17.06.20 03:26, Andrei Alexandrescu wrote:What classifies as D code as ill-defined? Could you post some examples? Greetings, Stefan[...]Not exactly as there is a clear ordering of declarations. Rather, it's a self-contradicting program, like this one: static if(!is(typeof(x))) int x; This would be an example of a race condition: static if(!is(typeof(x))) int y; static if(!is(typeof(y))) int x;[...]Unfortunately, if dependency structures get a bit tricky, DMD will fail to correctly compile even well-formed programs. It's why I have refrained from going too crazy with metaprogramming after my experience with my D frontend that stopped compiling after DMD 2.060. (Ironically, it does produce a diagnostic for each of the examples discussed here.)
Jun 17 2020
On 6/17/20 3:05 AM, Timon Gehr wrote:On 17.06.20 03:26, Andrei Alexandrescu wrote:Yah that's what I had in mind. Problem is made worse if the definitions are in different modules.On 6/16/20 7:01 PM, Paul Backus wrote:Not exactly as there is a clear ordering of declarations. Rather, it's a self-contradicting program, like this one: static if(!is(typeof(x))) int x; This would be an example of a race condition: static if(!is(typeof(x))) int y; static if(!is(typeof(y))) int x;I agree that this is a bug, but it is a bug in the design of the language itself, not the implementation.Is that a sort of a compile-time race condition?I guess that's what you get when one proceeds with too little formalism.If that's the case, D should deem the program ill-formed with no diagnostic required.Unfortunately, if dependency structures get a bit tricky, DMD will fail to correctly compile even well-formed programs. It's why I have refrained from going too crazy with metaprogramming after my experience with my D frontend that stopped compiling after DMD 2.060. (Ironically, it does produce a diagnostic for each of the examples discussed here.)
Jun 17 2020
On Wednesday, 17 June 2020 at 01:26:48 UTC, Andrei Alexandrescu wrote:On 6/16/20 7:01 PM, Paul Backus wrote:Unfortunately it is quite easy to run into this sort of thing in real life if you use templates and design-by-introspection. See below for an example: https://github.com/pbackus/sumtype/issues/35I agree that this is a bug, but it is a bug in the design of the language itself, not the implementation.Is that a sort of a compile-time race condition? If that's the case, D should deem the program ill-formed with no diagnostic required.
Jun 17 2020
On Tuesday, 16 June 2020 at 23:01:10 UTC, Paul Backus wrote:...but in fact, the existence of 'before' and 'after' states is an unavoidable consequence of how `static if` works. The condition of a `static if` statement *must* be evaluated before the body is processed by the compiler. Without some mechanism for controlling the order in which declarations undergo semantic analysis, it would be impossible to implement `static if` in the first place.I don't think it's fundamentally unavoidable. Here's a way to make it work:static if (x) { <body> }The compiler rewrites it to this:static assert(!x);And it also tries this:static assert(x); <body>If exactly one of the two compiles, you found a solution. Otherwise it's a contradiction / ambiguity. E.g:static if (is(T == int)) { alias T = int; }Has 2 solutions: ambiguity.static if (!is(T == int)) { alias T = int; }Has 0 solutions: contradiction.static if (is(T == int)) { alias T = int; int x; } static if (!is(typeof(x)) { int x; }Has 1 solution:static assert(is(T == int)); alias T = int; int x; static assert(is(typeof(x));Now of course, there are several problems: - this is difficult to implement given the existing dmd codebase - this is exponentially slow. I'm pretty sure the Boolean satisfiability problem [0] is reducible to `static if` and declarations, so compiling D this way is NP-complete. - do we as programmers want to deal with code that requires complex constraint solving to understand? We could make the language more strict by disallowing changing 'already determined' things, like already happens here: ``` struct S { static if (S.sizeof < 8) {int x;} // technically solvable } // Error: variable onlineapp.S.x cannot be further field because it will change the determined S size ``` I don't yet have an idea how to do this generally, but a first guess is: at the end of semantic analysis, re-evaluate all static conditions (static if, static assert, template constraints) and see if they still hold.The ugly truth here is that Walter has designed himself into a corner: module-level declarations are not order invariant in D, and never will be.That's not certain. But if we accept and embrace that that D has a compilation order with mutable state, nothing is stopping us from allowing this: ``` enum int x = 0; static foreach (i; 1..5) { x += i; } pragma(msg, x); // 10 ``` [0] https://en.wikipedia.org/wiki/Boolean_satisfiability_problem
Jun 17 2020
On Tuesday, 16 June 2020 at 23:01:10 UTC, Paul Backus wrote:...but in fact, the existence of 'before' and 'after' states is an unavoidable consequence of how `static if` works. The condition of a `static if` statement *must* be evaluated before the body is processed by the compiler. Without some mechanism for controlling the order in which declarations undergo semantic analysis, it would be impossible to implement `static if` in the first place.A similar problem can even happen, when porting C code to D, because D does not allow to use forward declarations. Consider the following example: const FOO_VER = 2; static if(FOO_VER >= 2) // If Bar is defined struct Bar { // Foo should be defined too. Foo *foo; } static if(FOO_VER >= 1) struct Foo { } It results in the following error: Error: undefined identifier Foo, did you mean variable foo? The equivalent C code would use a forward declaration of Foo, but that is not possible in D. Changing the order would work in this case, but would make it harder to port future changes from the C version. Using version instead of static if also solves the problem, but is less flexible. The example is based on https://issues.dlang.org/show_bug.cgi?id=3743
Jun 17 2020