Fixing spurious "statement is not reachable" in template code

Steven Schveighoffer via Digitalmars-d digitalmars-d at puremagic.com
Mon Oct 26 05:31:37 PDT 2015


On 10/24/15 1:25 PM, tsbockman wrote:
> While improving the DMD front-end's constant folding:
>      https://github.com/D-Programming-Language/dmd/pull/5229
> I found out about DMD issue 14835:
>      https://issues.dlang.org/show_bug.cgi?id=14835
>
> Briefly:
> ///////////////////////////////
> module main;
>
> import std.stdio;
>
> void reachIf(bool x)()
> {
>      if(!x)
>          return;
>      writeln("reached"); // Warning: statement is not reachable
> }
>
> void main(string[] args) {
>      reachIf!true();  // prints "reached"
>      reachIf!false(); // triggers warning
> }
> ///////////////////////////////
>
> This is, I think, a big problem.

I agree

> Affected code is rare today, but that is only because DMD's constant
> folding and value-range-propagation is weak. The more improvements are
> made in this area, the more common erroneous "statement is not
> reachable" warnings will become.
>
> Unfortunately, from what I can tell, this bug is just a natural
> consequence of DMD's current design; I think an ideal fix will not be
> simple.
>
> Some possible solutions:
>
> 1. Defer "not reachable" warnings until compilation has been completed,
> and only issue the warning if *all* instantiations of the statement were
> unreachable.

This isn't good either. One instantiation of reachIf being able to 
compile shouldn't be dependent on whether another one was ever used.

> 2. For semantic analysis purposes, first instantiate each template using
> dummy parameters with the widest possible VRP ranges. Only statements
> found to be "not reachable" in this dummy run should actually generate
> warnings.

How does the compiler figure this out? This seems like the halting 
problem to me.

> 3. ??? I don't know the compiler internals very well, so I may be
> missing a more elegant solution.

I think the easiest solution would be to just give up on worrying about 
unreachable code if the branch is dependent on a compile time variable 
that could legitimately change from instantiation to instantiation. In 
other words, for that instantiation, you obviously don't need to include 
the code, but you can't declare that the line of code is unreachable.

This means that this will compile:

void foo(int x)()
{
    if(x == 5)
        return;
    writeln("boo!");
}

void main()
{
    foo!5();
}

But this will not:

void main()
{
    enum x = 5;
    if(x == 5)
       return;
    writeln("boo!");
}

Even though they are effectively equivalent. But that's not a good 
reason to create errors which are obviously untrue. In the second case, 
the compiler can know definitively that the statement really will never 
be executed or compiled. In the first case, it can only be sure for that 
*instance*.

Even this could compile IMO (but a sufficiently smarter compiler may 
complain):

void foo(int x)() if(x == 5)
{
    if(x == 5)
       return;
    writeln("boo!");
}

Let's not forget that "unreachable statement" errors are really not 
errors in the sense that it will cause a crash, or corrupt memory. It 
just means some statements you wrote were wasted effort. It's OK to

-Steve


More information about the Digitalmars-d mailing list