www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - In what order static if conditions are evaluated?

reply "Artur =?UTF-8?B?WmF3xYJvY2tpIg==?= <artur.zawlocki gmail.com> writes:
Hi,

DMD (I'm using v2.060) seems to evaluate conditions in static 
if's from top to bottom (at least in a given scope). For example, 
consider the following program:

   module test;

   const bool x = true;
   const bool y = true;

   struct S {

     static assert(y);

     static if(x)
        static const bool y = false;

     static if(y)
        static const bool x = false;
   }

'x' in the first static if condition refers to .x and the static 
if introduces S.y to which 'y' in the second static if refers. 
'y' in the static assert also refers to S.y and thus the 
assertion fails:

   test.d(8): Error: static assert  (y) is false

Now, I guess if the second static if were evaluated first then 
'y' in the static assert condition would refer to .y and the 
program would compile. Note that dmd evaluates the static assert 
*after* both static if's.

So clearly D program semantics depends on the order static if's 
and static assert's are evaluated. Are there any standard rules 
here (I cannot find anything in Language Reference) or is this 
behaviour implementation dependent?

Artur
Mar 28 2013
next sibling parent reply 1100110 <0b1100110 gmail.com> writes:
On 03/28/2013 06:29 AM, "Artur Zawłocki" <artur.zawlocki gmail.com>" wrote:
 Hi,

 DMD (I'm using v2.060) seems to evaluate conditions in static if's from
 top to bottom (at least in a given scope). For example, consider the
 following program:

 module test;

 const bool x = true;
 const bool y = true;

 struct S {

 static assert(y);

 static if(x)
 static const bool y = false;

 static if(y)
 static const bool x = false;
 }

 'x' in the first static if condition refers to .x and the static if
 introduces S.y to which 'y' in the second static if refers. 'y' in the
 static assert also refers to S.y and thus the assertion fails:

 test.d(8): Error: static assert (y) is false

 Now, I guess if the second static if were evaluated first then 'y' in
 the static assert condition would refer to .y and the program would
 compile. Note that dmd evaluates the static assert *after* both static
 if's.

 So clearly D program semantics depends on the order static if's and
 static assert's are evaluated. Are there any standard rules here (I
 cannot find anything in Language Reference) or is this behaviour
 implementation dependent?

 Artur
It should behave the "same" as a normal if statement. Except it is evaluated during compile-time and not runtime. The only different that I know of is that the brackets will not introduce a new scope. string Y; if(true) { string x = "I'm not in the same scope as Y"; } static if(true) { string z = "but I am"; } that's the only "gotcha" I know of, and I seems quite intuitive once you realize why that is the case. But I can't think of any reason why a compiler would want to mix up the static if's.... Sounds malicious to me. =P
Mar 28 2013
parent reply "Artur =?UTF-8?B?WmF3xYJvY2tpIg==?= <artur.zawlocki gmail.com> writes:
On Thursday, 28 March 2013 at 11:50:58 UTC, 1100110 wrote:
 On 03/28/2013 06:29 AM, "Artur Zawłocki" 
 <artur.zawlocki gmail.com>" wrote:
 Hi,

 DMD (I'm using v2.060) seems to evaluate conditions in static 
 if's from
 top to bottom (at least in a given scope). For example, 
 consider the
 following program:

 module test;

 const bool x = true;
 const bool y = true;

 struct S {

 static assert(y);

 static if(x)
 static const bool y = false;

 static if(y)
 static const bool x = false;
 }

 'x' in the first static if condition refers to .x and the 
 static if
 introduces S.y to which 'y' in the second static if refers. 
 'y' in the
 static assert also refers to S.y and thus the assertion fails:

 test.d(8): Error: static assert (y) is false

 Now, I guess if the second static if were evaluated first then 
 'y' in
 the static assert condition would refer to .y and the program 
 would
 compile. Note that dmd evaluates the static assert *after* 
 both static
 if's.

 So clearly D program semantics depends on the order static 
 if's and
 static assert's are evaluated. Are there any standard rules 
 here (I
 cannot find anything in Language Reference) or is this 
 behaviour
 implementation dependent?

 Artur
 It should behave the "same" as a normal if statement.
Why? static if is not a statement at all, it's a 'conditional declaration'. And the order in which declarations are processed on the top level is not relevant (unless you have static if's, that is), e.g. you can have: static if (a) static assert (true); const bool a = true; I've just discovered that this is also OK: static if (S.a) static assert (true); struct S { static if if (true) const bool a = true; }
Mar 28 2013
parent 1100110 <0b1100110 gmail.com> writes:
On 03/28/2013 08:15 AM, "Artur Zawłocki" <artur.zawlocki gmail.com>" wrote:
 On Thursday, 28 March 2013 at 11:50:58 UTC, 1100110 wrote:
 On 03/28/2013 06:29 AM, "Artur Zawłocki" <artur.zawlocki gmail.com>"
 wrote:
 Hi,

 DMD (I'm using v2.060) seems to evaluate conditions in static if's from
 top to bottom (at least in a given scope). For example, consider the
 following program:

 module test;

 const bool x = true;
 const bool y = true;

 struct S {

 static assert(y);

 static if(x)
 static const bool y = false;

 static if(y)
 static const bool x = false;
 }

 'x' in the first static if condition refers to .x and the static if
 introduces S.y to which 'y' in the second static if refers. 'y' in the
 static assert also refers to S.y and thus the assertion fails:

 test.d(8): Error: static assert (y) is false

 Now, I guess if the second static if were evaluated first then 'y' in
 the static assert condition would refer to .y and the program would
 compile. Note that dmd evaluates the static assert *after* both static
 if's.

 So clearly D program semantics depends on the order static if's and
 static assert's are evaluated. Are there any standard rules here (I
 cannot find anything in Language Reference) or is this behaviour
 implementation dependent?

 Artur
 It should behave the "same" as a normal if statement.
Why? static if is not a statement at all, it's a 'conditional declaration'. And the order in which declarations are processed on the top level is not relevant (unless you have static if's, that is), e.g. you can have: static if (a) static assert (true); const bool a = true; I've just discovered that this is also OK: static if (S.a) static assert (true); struct S { static if if (true) const bool a = true; }
Ah! I see what you're getting at. I had just assumed getting it right was up to us. =P
Mar 28 2013
prev sibling parent reply "Don" <turnyourkidsintocash nospam.com> writes:
On Thursday, 28 March 2013 at 11:29:49 UTC, Artur Zawłocki wrote:
 Hi,

 DMD (I'm using v2.060) seems to evaluate conditions in static 
 if's from top to bottom (at least in a given scope). For 
 example, consider the following program:

   module test;

   const bool x = true;
   const bool y = true;

   struct S {

     static assert(y);

     static if(x)
        static const bool y = false;

     static if(y)
        static const bool x = false;
   }

 'x' in the first static if condition refers to .x and the 
 static if introduces S.y to which 'y' in the second static if 
 refers. 'y' in the static assert also refers to S.y and thus 
 the assertion fails:

   test.d(8): Error: static assert  (y) is false

 Now, I guess if the second static if were evaluated first then 
 'y' in the static assert condition would refer to .y and the 
 program would compile. Note that dmd evaluates the static 
 assert *after* both static if's.

 So clearly D program semantics depends on the order static if's 
 and static assert's are evaluated. Are there any standard rules 
 here (I cannot find anything in Language Reference) or is this 
 behaviour implementation dependent?

 Artur
Yes, it currently evaluates it top to bottom. An algorithm has been discussed which would remove that restriction; your example would then compile. It's difficult to implement though. It has to deal with paradoxes: static if (!is(typeof(x)) int y = 1; static if (!is(typeof(y)) int x = 1; Who wins?
Mar 28 2013
next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Thursday, 28 March 2013 at 11:52:26 UTC, Don wrote:
 On Thursday, 28 March 2013 at 11:29:49 UTC, Artur Zawłocki 
 wrote:
 Hi,

 DMD (I'm using v2.060) seems to evaluate conditions in static 
 if's from top to bottom (at least in a given scope). For 
 example, consider the following program:

  module test;

  const bool x = true;
  const bool y = true;

  struct S {

    static assert(y);

    static if(x)
       static const bool y = false;

    static if(y)
       static const bool x = false;
  }

 'x' in the first static if condition refers to .x and the 
 static if introduces S.y to which 'y' in the second static if 
 refers. 'y' in the static assert also refers to S.y and thus 
 the assertion fails:

  test.d(8): Error: static assert  (y) is false

 Now, I guess if the second static if were evaluated first then 
 'y' in the static assert condition would refer to .y and the 
 program would compile. Note that dmd evaluates the static 
 assert *after* both static if's.

 So clearly D program semantics depends on the order static 
 if's and static assert's are evaluated. Are there any standard 
 rules here (I cannot find anything in Language Reference) or 
 is this behaviour implementation dependent?

 Artur
Yes, it currently evaluates it top to bottom. An algorithm has been discussed which would remove that restriction; your example would then compile. It's difficult to implement though. It has to deal with paradoxes: static if (!is(typeof(x)) int y = 1; static if (!is(typeof(y)) int x = 1; Who wins?
Current already have to deal with paradoxes.
Mar 28 2013
parent 1100110 <0b1100110 gmail.com> writes:
On 03/28/2013 07:04 AM, deadalnix wrote:
 On Thursday, 28 March 2013 at 11:52:26 UTC, Don wrote:
 On Thursday, 28 March 2013 at 11:29:49 UTC, Artur Zawłocki wrote:
 Hi,

 DMD (I'm using v2.060) seems to evaluate conditions in static if's
 from top to bottom (at least in a given scope). For example, consider
 the following program:

 module test;

 const bool x = true;
 const bool y = true;

 struct S {

 static assert(y);

 static if(x)
 static const bool y = false;

 static if(y)
 static const bool x = false;
 }

 'x' in the first static if condition refers to .x and the static if
 introduces S.y to which 'y' in the second static if refers. 'y' in
 the static assert also refers to S.y and thus the assertion fails:

 test.d(8): Error: static assert (y) is false

 Now, I guess if the second static if were evaluated first then 'y' in
 the static assert condition would refer to .y and the program would
 compile. Note that dmd evaluates the static assert *after* both
 static if's.

 So clearly D program semantics depends on the order static if's and
 static assert's are evaluated. Are there any standard rules here (I
 cannot find anything in Language Reference) or is this behaviour
 implementation dependent?

 Artur
Yes, it currently evaluates it top to bottom. An algorithm has been discussed which would remove that restriction; your example would then compile. It's difficult to implement though. It has to deal with paradoxes: static if (!is(typeof(x)) int y = 1; static if (!is(typeof(y)) int x = 1; Who wins?
Current already have to deal with paradoxes.
Does the current *implementation* have to deal with paradoxes, or do the current *users*? =P But no serious question. I'm curious.
Mar 28 2013
prev sibling next sibling parent reply "Artur =?UTF-8?B?WmF3xYJvY2tpIg==?= <artur.zawlocki gmail.com> writes:
On Thursday, 28 March 2013 at 11:52:26 UTC, Don wrote:
 Yes, it currently evaluates it top to bottom.
As long as all the static if's in question are in a single scope. Here dmd evaluates the second (nested) static if before the first one: static if (S.a) static assert (S.a); struct S { static if (true) const bool a = true; }
 An algorithm has been discussed which would remove that 
 restriction; your example would then compile. It's difficult to 
 implement though. It has to deal with paradoxes:

 static if (!is(typeof(x)) int y = 1;
 static if (!is(typeof(y)) int x = 1;

 Who wins?
Nice example! Shouldn't the compiler report errors when it detects kind of 'circular dependency' between the names occurring in static if conditions and the ones declared in their bodies? Artur
Mar 28 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 03/28/2013 02:34 PM, "Artur Zawłocki" <artur.zawlocki gmail.com>" wrote:
 On Thursday, 28 March 2013 at 11:52:26 UTC, Don wrote:
 Yes, it currently evaluates it top to bottom.
As long as all the static if's in question are in a single scope. Here dmd evaluates the second (nested) static if before the first one: static if (S.a) static assert (S.a); struct S { static if (true) const bool a = true; }
Yes; DMD is buggy and the specification is insufficient. Currently the meaning of D code as interpreted by DMD may depend on the order the modules are passed to the compiler on the command line.
 An algorithm has been discussed which would remove that restriction;
 your example would then compile. It's difficult to implement though.
 It has to deal with paradoxes:

 static if (!is(typeof(x)) int y = 1;
 static if (!is(typeof(y)) int x = 1;

 Who wins?
Nice example! Shouldn't the compiler report errors when it detects kind of 'circular dependency' between the names occurring in static if conditions and the ones declared in their bodies? Artur
Indeed, the above code should not compile. My upcoming D front end currently reports the following after fixing the grammatical mistakes (For now. The error message text should maybe be improved. Ideas welcome.) tt.d:2:32: error: declaration of 'x' smells suspiciously fishy static if (!is(typeof(y))) int x = 1; ^ tt.d:1:23: note: this lookup should have succeeded if it was valid static if (!is(typeof(x))) int y = 1; ^ tt.d:1:32: error: declaration of 'y' smells suspiciously fishy static if (!is(typeof(x))) int y = 1; ^ tt.d:2:23: note: this lookup should have succeeded if it was valid static if (!is(typeof(y))) int x = 1; ^ There are lots of similar analysis order issues without static if. (Luckily, they can be detected well enough conservatively in a quite general way.) The following is a simplified example from my test suite: class A{ int string; } template Mixin(string s){ mixin("alias "~s~" Mixin;"); } class D: Mixin!({D d = new D; return d.foo();}()){ int foo(int x){ return 2;} string foo(){ return "A"; } } The problem is of course that 'string' has to be resolved in order to compute the parent of 'D'. However, that parent then changes the meaning of 'string' in the subclass scope. Therefore, the code is meaningless. Apparently, this currently crashes DMD (segmentation fault). bug.d:1:14: error: declaration of 'string' smells suspiciously fishy class A{ int string; } ^~~~~~ bug.d:7:5: note: this lookup on subclass 'D' should have succeeded if it was valid string foo(){ return "A"; } ^~~~~~
Mar 28 2013
next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Thursday, 28 March 2013 at 18:24:19 UTC, Timon Gehr wrote:
 Yes; DMD is buggy and the specification is insufficient. 
 Currently the meaning of D code as interpreted by DMD may 
 depend on the order the modules are passed to the compiler on 
 the command line.
Can you have a look at http://wiki.dlang.org/DIP31 ? I have written a proposal to specify this, but it does currently not precise/complete enough.
Mar 28 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 03/29/2013 04:22 AM, deadalnix wrote:
 On Thursday, 28 March 2013 at 18:24:19 UTC, Timon Gehr wrote:
 Yes; DMD is buggy and the specification is insufficient. Currently the
 meaning of D code as interpreted by DMD may depend on the order the
 modules are passed to the compiler on the command line.
Can you have a look at http://wiki.dlang.org/DIP31 ? I have written a proposal to specify this, but it does currently not precise/complete enough.
Poisoning is the right approach. However, the analysis order should be the order of potential dependencies between mixin expressions/static if conditions and potentially generated symbols. In case there is a cycle in the potential dependencies, the symbol lookups occuring in the connected component that appears first in a topological ordering of all connected components of the potential-dependency graph should poison the respective scopes. Analysis can then proceed, because the potential dependencies on the cycle are eliminated. It is possible that your way of dealing with overloads is fine, however, I'd just have poisoned those overloads that would necessitate revisiting an already taken overload-resolution decision.
Mar 29 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 03/29/2013 03:01 PM, Timon Gehr wrote:
 On 03/29/2013 04:22 AM, deadalnix wrote:
 On Thursday, 28 March 2013 at 18:24:19 UTC, Timon Gehr wrote:
 Yes; DMD is buggy and the specification is insufficient. Currently the
 meaning of D code as interpreted by DMD may depend on the order the
 modules are passed to the compiler on the command line.
Can you have a look at http://wiki.dlang.org/DIP31 ? I have written a proposal to specify this, but it does currently not precise/complete enough.
Poisoning is the right approach. However, the analysis order should be the order of potential dependencies between mixin expressions/static if conditions and potentially generated symbols. In case there is a cycle in the potential dependencies, the symbol lookups occuring in the connected component that appears first in a topological ordering of all connected components of the potential-dependency graph should poison the respective scopes. Analysis can then proceed, because the potential dependencies on the cycle are eliminated. ...
I meant to write _strongly_ connected components, obviously.
Mar 29 2013
prev sibling parent reply "Artur =?UTF-8?B?WmF3xYJvY2tpIg==?= <artur.zawlocki gmail.com> writes:
On Thursday, 28 March 2013 at 18:24:19 UTC, Timon Gehr wrote:

 (...)
 Indeed, the above code should not compile. My upcoming D front 
 end currently reports the following after fixing the 
 grammatical mistakes (For now. The error message text should 
 maybe be improved. Ideas welcome.)

 tt.d:2:32: error: declaration of 'x' smells suspiciously fishy
 static if (!is(typeof(y))) int x = 1;
                                ^
 tt.d:1:23: note: this lookup should have succeeded if it was 
 valid
 static if (!is(typeof(x))) int y = 1;
                       ^
 tt.d:1:32: error: declaration of 'y' smells suspiciously fishy
 static if (!is(typeof(x))) int y = 1;
                                ^
 tt.d:2:23: note: this lookup should have succeeded if it was 
 valid
 static if (!is(typeof(y))) int x = 1;
                       ^
Surely it will be fun to work with (though 'this lookup' message is somewhat confusing)!
 There are lots of similar analysis order issues without static 
 if. (Luckily, they can be detected well enough conservatively 
 in a quite general way.) The following is a simplified example 
 from my test suite:

 class A{ int string; }
 template Mixin(string s){
     mixin("alias "~s~" Mixin;");
 }
 class D: Mixin!({D d = new D; return d.foo();}()){
     int foo(int x){ return 2;}
     string foo(){ return "A"; }
 }

 The problem is of course that 'string' has to be resolved in 
 order to compute the parent of 'D'. However, that parent then 
 changes the meaning of 'string' in the subclass scope. 
 Therefore, the code is meaningless.
I am lost here. Are 'int string' in A in and 'int foo(int)' in D relevant? Shouldn't this example fail simply because in order to process the declaration of D the compiler needs to instantiate D in the delegate body? So this would be similar to: template Mixin(bool b : true) { alias Object Mixin; } class D: Mixin!(new D == new D) {} Artur
Mar 29 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 03/29/2013 12:33 PM, "Artur Zawłocki" <artur.zawlocki gmail.com>" wrote:
 On Thursday, 28 March 2013 at 18:24:19 UTC, Timon Gehr wrote:

 (...)
 Indeed, the above code should not compile. My upcoming D front end
 currently reports the following after fixing the grammatical mistakes
 (For now. The error message text should maybe be improved. Ideas
 welcome.)

 tt.d:2:32: error: declaration of 'x' smells suspiciously fishy
 static if (!is(typeof(y))) int x = 1;
                                ^
 tt.d:1:23: note: this lookup should have succeeded if it was valid
 static if (!is(typeof(x))) int y = 1;
                       ^
 tt.d:1:32: error: declaration of 'y' smells suspiciously fishy
 static if (!is(typeof(x))) int y = 1;
                                ^
 tt.d:2:23: note: this lookup should have succeeded if it was valid
 static if (!is(typeof(y))) int x = 1;
                       ^
Surely it will be fun to work with (though 'this lookup' message is somewhat confusing)!
Yes. What would be a better message?
 There are lots of similar analysis order issues without static if.
 (Luckily, they can be detected well enough conservatively in a quite
 general way.) The following is a simplified example from my test suite:

 class A{ int string; }
 template Mixin(string s){
     mixin("alias "~s~" Mixin;");
 }
 class D: Mixin!({D d = new D; return d.foo();}()){
     int foo(int x){ return 2;}
     string foo(){ return "A"; }
 }

 The problem is of course that 'string' has to be resolved in order to
 compute the parent of 'D'. However, that parent then changes the
 meaning of 'string' in the subclass scope. Therefore, the code is
 meaningless.
I am lost here. Are 'int string' in A
Yes.
 in and 'int foo(int)' in D
No.
 relevant?
 Shouldn't this example fail simply because in order to process
 the declaration of D the compiler needs to instantiate D in the delegate
 body?
No, D's parent is not required to be known in order to execute what is in the delegate body.
 So this would be similar to:

    template Mixin(bool b : true) {
      alias Object Mixin;
    }
    class D: Mixin!(new D == new D) {}
This fails because new D == new D is false. (Otherwise it would work.)
Mar 29 2013
parent reply "Artur =?UTF-8?B?WmF3xYJvY2tpIg==?= <artur.zawlocki gmail.com> writes:
On Friday, 29 March 2013 at 13:54:03 UTC, Timon Gehr wrote:
 On 03/29/2013 12:33 PM, "Artur Zawłocki" 
 <artur.zawlocki gmail.com>" wrote:
 On Thursday, 28 March 2013 at 18:24:19 UTC, Timon Gehr wrote:

 (...)
 Indeed, the above code should not compile. My upcoming D 
 front end
 currently reports the following after fixing the grammatical 
 mistakes
 (For now. The error message text should maybe be improved. 
 Ideas
 welcome.)

 tt.d:2:32: error: declaration of 'x' smells suspiciously fishy
 static if (!is(typeof(y))) int x = 1;
                               ^
 tt.d:1:23: note: this lookup should have succeeded if it was 
 valid
 static if (!is(typeof(x))) int y = 1;
                      ^
 tt.d:1:32: error: declaration of 'y' smells suspiciously fishy
 static if (!is(typeof(x))) int y = 1;
                               ^
 tt.d:2:23: note: this lookup should have succeeded if it was 
 valid
 static if (!is(typeof(y))) int x = 1;
                      ^
Surely it will be fun to work with (though 'this lookup' message is somewhat confusing)!
Yes. What would be a better message?
I don't know.
 There are lots of similar analysis order issues without 
 static if.
 (Luckily, they can be detected well enough conservatively in 
 a quite
 general way.) The following is a simplified example from my 
 test suite:

 class A{ int string; }
 template Mixin(string s){
    mixin("alias "~s~" Mixin;");
 }
 class D: Mixin!({D d = new D; return d.foo();}()){
    int foo(int x){ return 2;}
    string foo(){ return "A"; }
 }

 The problem is of course that 'string' has to be resolved in 
 order to
 compute the parent of 'D'. However, that parent then changes 
 the
 meaning of 'string' in the subclass scope. Therefore, the 
 code is
 meaningless.
I am lost here. Are 'int string' in A
Yes.
 in and 'int foo(int)' in D
No.
 relevant?
 Shouldn't this example fail simply because in order to process
 the declaration of D the compiler needs to instantiate D in 
 the delegate
 body?
No, D's parent is not required to be known in order to execute what is in the delegate body.
Knowing the parent of D is not required to create an instance of D?
 So this would be similar to:

   template Mixin(bool b : true) {
     alias Object Mixin;
   }
   class D: Mixin!(new D == new D) {}
This fails because new D == new D is false. (Otherwise it would work.)
See my comment above.
Mar 29 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 03/29/2013 03:25 PM, "Artur Zawłocki" <artur.zawlocki gmail.com>" wrote:
 ...

 Knowing the parent of D is not required to create an instance of D?
...
In this case it is not necessary. The restriction will be introduced later. (I have not implemented super constructor calls yet. They will make it break down, because in general the subclass cannot know that the unknown super class does not define a constructor.) This is a simpler case that possibly illustrates my actual point better: class A{ alias int string; } template Mixin(string s){ mixin("alias "~s~" Mixin;"); } class B : Mixin!(B.x){ immutable string x = "A"; } This still crashes DMD.
Mar 29 2013
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 03/28/2013 12:52 PM, Don wrote:
 ...

 Yes, it currently evaluates it top to bottom. An algorithm has been
 discussed which would remove that restriction;
I'm not sure that restriction is the correct word. There is not always a well-defined top-to-bottom order.
  your example would then compile. It's difficult to implement though.
It was rather easy to implement. Do you mean it might be hard to fit it into the existing DMD code base?
 It has to deal with paradoxes:

 static if (!is(typeof(x)) int y = 1;
 static if (!is(typeof(y)) int x = 1;

 Who wins?
Nobody. :o)
Mar 28 2013