On Monday, 26 October 2015 at 12:31:37 UTC, Steven Schveighoffer wrote:
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.

I agree this is not ideal, however it would be much better than what we have currently, and the compiler implementation should be relatively simple.

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.

My solution #2 is the same as the one you proposed - the dummy parameter stuff is my vague proposal for a way to actual implement this behavior in the compiler.

The halting problem is no more of an issue than it is for the compiler today. (That is to say, the halting problem leads to false negatives, but not false positives.)

I think the compiler currently does something like this:

void main(string[] args) {
    reachIf!true();  // prints "reached"
    reachIf!false(); // triggers warning
}

// A template will be instantiated and undergo semantic analysis
// once for each combination of CT parameters fed to it in the program.

// reachIf!true pass:
void reachIf(bool x)() // VRP narrows x to true
{
    if(!x) // VRP + constant folding says (!true) always == false
return; // never reached (this doesn't always generate a warning)
    writeln("reached"); // always reached
}

// reachIf!false pass:
void reachIf(bool x)() // VRP narrows x to false
{
    if(!x) // VRP + constant folding says (!false) always == true
        return; // always reached
    writeln("reached"); // Warning: statement is not reachable
}

My solution #2 would add this before the reachIf!true pass:

// first instantiate the template with the widest possible
// VRP ranges for the CT paramters
void reachIf(bool x)() // VRP says x could be true or false
{
    if(!x) // not constant; cannot be folded
        return; // reachable
    writeln("reached"); // reachable
}

"statement not reachable" warnings would only be generated during this preliminary pass; they would be suppressed in the reachIf!true and reachIf!false passes.

The reason solution #2 could end up being a ton of work, is that a dummy type will have to be created to apply this solution to cases like:

module main;

import std.stdio;

void reachIf(T...)()
{
    if(T.length != 1 || T[0].sizeof != 4)
        return;
    writeln("reached"); // Warning: statement is not reachable
}

void main(string[] args) {
    reachIf!int();  // prints "reached"
    reachIf!long(); // triggers warning

    stdin.readln();
}

(Note that this case can only be reproduced with my constant folding upgrade patch applied.)

In order to apply solution #2 to this code, we need a dummy AliasSeq of indeterminate length, whose elements have a sizeof property of indeterminate value. I strongly suspect that a fake type like that will require explicit support to be added all over the DMD code base; it would be a huge change just to eliminate some spurious warnings.

Reply via email to