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.