digitalmars.D - lazy evaluation of logical operators in enum definition
- Shachar Shemesh (28/28) Apr 15 2018 Consider the following program:
- Basile B. (4/34) Apr 15 2018 Hello, i've encountered a similar issue recently (see
- Jacob Carlborg (28/58) Apr 16 2018 I think the issue is better illustrated with a function:
- Simen =?UTF-8?B?S2rDpnLDpXM=?= (9/39) Apr 17 2018 There's a kinda neat (and kinda ugly) way to implement prop in
- Shachar Shemesh (21/27) Apr 17 2018 Also, extremely dangerous.
- Atila Neves (5/11) Apr 18 2018 A very good rule of thumb. I've lost of how many times I've had a
- Meta (5/19) Apr 18 2018 Let me third that.
- Walter Bright (9/10) Apr 18 2018 Evaluation does stop, semantic analysis does not. For example:
- Timon Gehr (15/31) Apr 18 2018 His use case is `enum x = 0 && undefined_variable;`, for which at least
Consider the following program: struct S1 { enum member = 3; } struct S2 { enum member = 2; } struct S3 { } enum prop(T) = __traits(hasMember, T, "member") && T.member==3; pragma(msg, prop!S1); pragma(msg, prop!S2); pragma(msg, prop!S3); When compiled, it produces: true false test.d(12): Error: no property member for type S3 test.d(16): Error: template instance `test.prop!(S3)` error instantiating test.d(16): while evaluating pragma(msg, prop!(S3)) If I change the definition of "prop" to: template prop(T) { static if( __traits(hasMember, T, "member") && T.member==3 ) enum prop = true; else enum prop = false; } then everything compiles as expected. It seems that the && evaluation does not stop when the first false is found.
Apr 15 2018
On Monday, 16 April 2018 at 05:57:01 UTC, Shachar Shemesh wrote:Consider the following program: struct S1 { enum member = 3; } struct S2 { enum member = 2; } struct S3 { } enum prop(T) = __traits(hasMember, T, "member") && T.member==3; pragma(msg, prop!S1); pragma(msg, prop!S2); pragma(msg, prop!S3); When compiled, it produces: true false test.d(12): Error: no property member for type S3 test.d(16): Error: template instance `test.prop!(S3)` error instantiating test.d(16): while evaluating pragma(msg, prop!(S3)) If I change the definition of "prop" to: template prop(T) { static if( __traits(hasMember, T, "member") && T.member==3 ) enum prop = true; else enum prop = false; } then everything compiles as expected. It seems that the && evaluation does not stop when the first false is found.Hello, i've encountered a similar issue recently (see https://issues.dlang.org/show_bug.cgi?id=18115#c13). There are explanations in the last comments.
Apr 15 2018
On Monday, 16 April 2018 at 05:57:01 UTC, Shachar Shemesh wrote:Consider the following program: struct S1 { enum member = 3; } struct S2 { enum member = 2; } struct S3 { } enum prop(T) = __traits(hasMember, T, "member") && T.member==3; pragma(msg, prop!S1); pragma(msg, prop!S2); pragma(msg, prop!S3); When compiled, it produces: true false test.d(12): Error: no property member for type S3 test.d(16): Error: template instance `test.prop!(S3)` error instantiating test.d(16): while evaluating pragma(msg, prop!(S3)) If I change the definition of "prop" to: template prop(T) { static if( __traits(hasMember, T, "member") && T.member==3 ) enum prop = true; else enum prop = false; } then everything compiles as expected. It seems that the && evaluation does not stop when the first false is found.I think the issue is better illustrated with a function: bool prop(T)() { if (__traits(hasMember, T, "member")) { if (T.member == 3) return true; } return false; } In this function there's no way to tell if the function is going to be executed at compile time or at runtime. Therefore the semantics of the whole function need to be valid. Replacing the `if` with a `static if` would solve the problem, as you mentioned. This works because the semantic analysis of `static if` and CTFE evaluation of a function occurs at different phases in the compiler. This post explains this better and in more detail [1]. The issue is that the compiler will do the semantic analysis of `T.member` before it has run CTFE or constant folding on the expression. Although, one could argue that in your case it's clear that the expression only will be evaluated at compile time, but that's not how the compiler works currently. [1] https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time -- /Jacob Carlborg
Apr 16 2018
On Monday, 16 April 2018 at 05:57:01 UTC, Shachar Shemesh wrote:Consider the following program: struct S1 { enum member = 3; } struct S2 { enum member = 2; } struct S3 { } enum prop(T) = __traits(hasMember, T, "member") && T.member==3; pragma(msg, prop!S1); pragma(msg, prop!S2); pragma(msg, prop!S3); When compiled, it produces: true false test.d(12): Error: no property member for type S3 test.d(16): Error: template instance `test.prop!(S3)` error instantiating test.d(16): while evaluating pragma(msg, prop!(S3)) If I change the definition of "prop" to: template prop(T) { static if( __traits(hasMember, T, "member") && T.member==3 ) enum prop = true; else enum prop = false; } then everything compiles as expected. It seems that the && evaluation does not stop when the first false is found.There's a kinda neat (and kinda ugly) way to implement prop in one line: enum prop(T) = __traits(compiles, { static assert(T.member == 3); }); Now, that's not the same as short-circuiting, and only useful in some cases, but for those cases, it's useful. -- Simen
Apr 17 2018
On 17/04/18 13:59, Simen Kjærås wrote:There's a kinda neat (and kinda ugly) way to implement prop in one line: enum prop(T) = __traits(compiles, { static assert(T.member == 3); }); Now, that's not the same as short-circuiting, and only useful in some cases, but for those cases, it's useful.Also, extremely dangerous. Seriously, guys and gals. __traits(compiles) (and its uglier sibling, is(typeof())) should be used *extremely* sparingly. The problem is that just about any use of __traits(compiles) I know of is seeking to weed out one particular reason for the compilation failure. There is a known bug in D, however, that the compiler consistently fails to read the programmer's mind. The compiler only knows that the code as provided does not compile, and that in that case you asked for something to happen. The usual outcome is that 80-90% of the times your code does what you expect, but then something comes along that throws you off the beaten track. In those cases, instead of getting an error message, you get one of the sides of the "if", which results in random behavior by your code. If you're lucky, you will get an error message much further down the compilation line, and then start having a fun day of trying to figure code will actually compile. My personal rule of thumb is this: If there is *any* way of achieving the result I want without __traits(compiles), do it that way. Shachar
Apr 17 2018
On Wednesday, 18 April 2018 at 04:44:23 UTC, Shachar Shemesh wrote:On 17/04/18 13:59, Simen Kjærås wrote:A very good rule of thumb. I've lost of how many times I've had a bug because of __traits(compiles) being false, but not in the way I expected it to be![...]Also, extremely dangerous. Seriously, guys and gals. __traits(compiles) (and its uglier sibling, is(typeof())) should be used *extremely* sparingly. [...]
Apr 18 2018
On Wednesday, 18 April 2018 at 10:19:20 UTC, Atila Neves wrote:On Wednesday, 18 April 2018 at 04:44:23 UTC, Shachar Shemesh wrote:Let me third that. Although this would not be as big of a problem if we had a way of printing out the values for failed template constraints (isn't this already done for static if now?)On 17/04/18 13:59, Simen Kjærås wrote:A very good rule of thumb. I've lost of how many times I've had a bug because of __traits(compiles) being false, but not in the way I expected it to be![...]Also, extremely dangerous. Seriously, guys and gals. __traits(compiles) (and its uglier sibling, is(typeof())) should be used *extremely* sparingly. [...]
Apr 18 2018
On 4/15/2018 10:57 PM, Shachar Shemesh wrote:It seems that the && evaluation does not stop when the first false is found.Evaluation does stop, semantic analysis does not. For example: bool foo() { return 0 && undefined_variable; } does not compile. I'd speculate that most would consider this code compiling successfully as surprising behavior. It would also be difficult to specify, as just when is e1 of (e1 && e2) statically known at compile time (i.e. how much flow analysis is the compiler expected to do to determine this?).
Apr 18 2018
On 18.04.2018 09:18, Walter Bright wrote:On 4/15/2018 10:57 PM, Shachar Shemesh wrote:His use case is `enum x = 0 && undefined_variable;`, for which at least your second concern does not apply. It is also surprising that a `static if` condition cannot be hoisted out: --- static if(expression){ ... } --- --- // not necessarily the same as the above enum c = expression; static if(c){ ... } --- I think the suggestion is to do lazy semantic analysis for all standalone expressions that need to be evaluated at compile-time (and not just for `static if`/`static assert` conditions).It seems that the && evaluation does not stop when the first false is found.Evaluation does stop, semantic analysis does not. For example: bool foo() { return 0 && undefined_variable; } does not compile. I'd speculate that most would consider this code compiling successfully as surprising behavior. It would also be difficult to specify, as just when is e1 of (e1 && e2) statically known at compile time (i.e. how much flow analysis is the compiler expected to do to determine this?).
Apr 18 2018