static assert(0) in template is a disaster

Paul Backus snarwin at gmail.com
Tue Jun 16 23:01:10 UTC 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:
>>> 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.


More information about the Digitalmars-d mailing list