www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - static assert(0) in template is a disaster

reply Nils Lankila <NilsLankila gmx.us> writes:
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 to
  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
          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
next sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
Bug report please, this may be an issue that can be fixed.
Jun 16
prev sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
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
parent reply Nils Lankila <NilsLankila gmx.us> writes:
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:
 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 ?
yes. I've removed it after changing the else statement.
Jun 16
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
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:
 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 ?
yes. I've removed it after changing the else statement.
Thank you very much for finding this. I'll be having a look at this shortly.
Jun 16
parent reply Nils Lankila <NilsLankila gmx.us> writes:
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:
 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:
 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 ?
yes. I've removed it after changing the else statement.
Thank you very much for finding this. I'll be having a look at this shortly.
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 in
 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.g
 ErroneousTemplateInstance[TemplateInstanceBox] failedInstances;
Jun 16
parent reply Paul Backus <snarwin gmail.com> writes:
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:
 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:
 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 ?
yes. I've removed it after changing the else statement.
Thank you very much for finding this. I'll be having a look at this shortly.
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.
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));
Jun 16
parent reply Nils Lankila <NilsLankila gmx.us> writes:
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:
 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:
 [...]
Thank you very much for finding this. I'll be having a look at this shortly.
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.
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));
that's unfortunate, looks like the problem cant be solved then.
Jun 16
parent reply Dennis <dkorpel gmail.com> writes:
On Tuesday, 16 June 2020 at 18:14:26 UTC, Nils Lankila wrote:
 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));
that's unfortunate, looks like the problem cant be solved then.
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.org
Jun 16
parent reply Paul Backus <snarwin gmail.com> writes:
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:
 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));
that's unfortunate, looks like the problem cant be solved then.
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.org
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 that
 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
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 17 June 2020 at 01:26:48 UTC, Andrei Alexandrescu 
wrote:
 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.
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.
Jun 16
parent reply Avrina <avrina12309412342 gmail.com> writes:
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:
 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.
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.
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.
Jun 16
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
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:
 On Wednesday, 17 June 2020 at 01:26:48 UTC, Andrei 
 Alexandrescu wrote:
 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.
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.
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.
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.
Jun 16
parent reply Avrina <avrina12309412342 gmail.com> writes:
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:
 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:
 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.
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.
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.
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.
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 17
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
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:
 [...]
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 } }
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.
Jun 19
parent Avrina <avrina12309412342 gmail.com> writes:
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:
 On Wednesday, 17 June 2020 at 03:15:52 UTC, Stefan Koch 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 } }
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.
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.
Jun 19
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 17.06.20 03:26, Andrei Alexandrescu wrote:
 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?
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;
 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
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 17 June 2020 at 07:05:25 UTC, Timon Gehr wrote:
 On 17.06.20 03:26, Andrei Alexandrescu 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;
 [...]
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.)
What classifies as D code as ill-defined? Could you post some examples? Greetings, Stefan
Jun 17
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 6/17/20 3:05 AM, Timon Gehr wrote:
 On 17.06.20 03:26, Andrei Alexandrescu wrote:
 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?
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;
Yah that's what I had in mind. Problem is made worse if the definitions are in different modules.
 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.)
I guess that's what you get when one proceeds with too little formalism.
Jun 17
prev sibling parent Paul Backus <snarwin gmail.com> writes:
On Wednesday, 17 June 2020 at 01:26:48 UTC, Andrei Alexandrescu 
wrote:
 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.
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/35
Jun 17
prev sibling next sibling parent Dennis <dkorpel gmail.com> writes:
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
prev sibling parent Tim <tim.dlang t-online.de> writes:
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