Thread overview
Unreachable warning is annoying
Mar 13, 2018
Yuxuan Shui
Mar 13, 2018
Yuxuan Shui
Mar 13, 2018
H. S. Teoh
Mar 13, 2018
Timon Gehr
Mar 16, 2018
Ali Çehreli
March 13, 2018
See this simple example:

int staticFind(T, S...)() {
    foreach(id, R; S) {
        if (is(T == R))
            return id;
        }
    }
    return -1;
}

staticFind!(int, int, double) will generate a 'statement is unreachable' warning, and staticFind!(int, double) won't.

This behaviour is understandable, but annoying. If template writer cares about this unreachable warning, they basically can't use any early return at all.

So previous example has to be re-written into:

int staticFind(T, S...)() {
    int ret = -1;
    foreach(id, R; S) {
        if (is(T == R) && ret == -1)
            ret = id;
        }
    }
    return ret;
}

However, this might not always be so simple in more complex functions. And this could potentially increase compilation time (maybe?).

March 13, 2018
On 3/13/18 10:25 AM, Yuxuan Shui wrote:
> See this simple example:
> 
> int staticFind(T, S...)() {
>      foreach(id, R; S) {
>          if (is(T == R))
>              return id;
>          }
>      }
>      return -1;
> }
> 
> staticFind!(int, int, double) will generate a 'statement is unreachable' warning, and staticFind!(int, double) won't.
> 
> This behaviour is understandable, but annoying. If template writer cares about this unreachable warning, they basically can't use any early return at all.
> 
> So previous example has to be re-written into:
> 
> int staticFind(T, S...)() {
>      int ret = -1;
>      foreach(id, R; S) {
>          if (is(T == R) && ret == -1)
>              ret = id;
>          }
>      }
>      return ret;
> }
> 
> However, this might not always be so simple in more complex functions. And this could potentially increase compilation time (maybe?).
> 

This has been discussed before. There are a few ways around this. One is to do what you did. Another is to append a sentinel, or use id to terminate the loop:

foreach(id, R; S) {
    static if(is(T == R))
        return id;
    else static if(id + 1 == S.length)
        return -1;
}

IMO, the "unreachable statement" error is bogus because it's reachable depending on the template parameters! In the coder's eyes, what matters is whether the line of source is reachable or not, not whether it's reachable in that instantiation.

-Steve
March 13, 2018
On Tuesday, 13 March 2018 at 14:40:21 UTC, Steven Schveighoffer wrote:
> On 3/13/18 10:25 AM, Yuxuan Shui wrote:
>> [...]
>
> This has been discussed before. There are a few ways around this. One is to do what you did. Another is to append a sentinel, or use id to terminate the loop:
>
> foreach(id, R; S) {
>     static if(is(T == R))
>         return id;
>     else static if(id + 1 == S.length)
>         return -1;
> }

Wait, I don't understand how this works.

Isn't this going to be expanded to something like:

return 0;
return 4;
// One return for every match
...
return -1;

Shouldn't this trigger unreachable warning too?

>
> IMO, the "unreachable statement" error is bogus because it's reachable depending on the template parameters! In the coder's eyes, what matters is whether the line of source is reachable or not, not whether it's reachable in that instantiation.

Agreed.

>
> -Steve

March 13, 2018
On 3/13/18 12:50 PM, Yuxuan Shui wrote:
> On Tuesday, 13 March 2018 at 14:40:21 UTC, Steven Schveighoffer wrote:
>> On 3/13/18 10:25 AM, Yuxuan Shui wrote:
>>> [...]
>>
>> This has been discussed before. There are a few ways around this. One is to do what you did. Another is to append a sentinel, or use id to terminate the loop:
>>
>> foreach(id, R; S) {
>>     static if(is(T == R))
>>         return id;
>>     else static if(id + 1 == S.length)
>>         return -1;
>> }
> 
> Wait, I don't understand how this works.
> 
> Isn't this going to be expanded to something like:
> 
> return 0;
> return 4;
> // One return for every match
> ....
> return -1;
> 
> Shouldn't this trigger unreachable warning too?

Yes, it's going to trigger one return for every match, and the return for -1.

But there is something special about an unrolled foreach. Notice, it's NOT a static foreach. A static foreach would result in the same problem.

An unrolled foreach on a tuple has a notion that the flow control jumps out of the loop, and it's OK to skip further loops (even though they are technically unrolled).

This works for break and continue as well. It's a very convoluted notion, one that is hard to grasp and explain.

In any case, the better solution would be if the compiler turned off the "unreachable statement" detection when it encountered a static if based on template parameters.

-Steve
March 13, 2018
On Tue, Mar 13, 2018 at 01:32:55PM -0400, Steven Schveighoffer via Digitalmars-d wrote: [...]
> An unrolled foreach on a tuple has a notion that the flow control jumps out of the loop, and it's OK to skip further loops (even though they are technically unrolled).
[...]

This is not true.  Foreach on a tuple does *not* skip expanding all iterations of the loop regardless of any `break`s or `continue`s. It's the codegen that eliminates the resulting dead code.  See:

	https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time#Case_Study:_foreach_over_a_type_list


T

-- 
"No, John.  I want formats that are actually useful, rather than over-featured megaliths that address all questions by piling on ridiculous internal links in forms which are hideously over-complex." -- Simon St. Laurent on xml-dev
March 13, 2018
On 13.03.2018 18:43, H. S. Teoh wrote:
> On Tue, Mar 13, 2018 at 01:32:55PM -0400, Steven Schveighoffer via Digitalmars-d wrote:
> [...]
>> An unrolled foreach on a tuple has a notion that the flow control
>> jumps out of the loop, and it's OK to skip further loops (even though
>> they are technically unrolled).
> [...]
> 
> This is not true.  Foreach on a tuple does *not* skip expanding all
> iterations of the loop regardless of any `break`s or `continue`s. It's
> the codegen that eliminates the resulting dead code.  See:
> 
> 	https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time#Case_Study:_foreach_over_a_type_list
> 
> 
> T
> 

I think that's what he was saying. :)
March 13, 2018
On 3/13/18 3:21 PM, Timon Gehr wrote:
> On 13.03.2018 18:43, H. S. Teoh wrote:
>> On Tue, Mar 13, 2018 at 01:32:55PM -0400, Steven Schveighoffer via Digitalmars-d wrote:
>> [...]
>>> An unrolled foreach on a tuple has a notion that the flow control
>>> jumps out of the loop, and it's OK to skip further loops (even though
>>> they are technically unrolled).
>> [...]
>>
>> This is not true.  Foreach on a tuple does *not* skip expanding all
>> iterations of the loop regardless of any `break`s or `continue`s. It's
>> the codegen that eliminates the resulting dead code.  See:
>>
>>     https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time#Case_Study:_foreach_over_a_type_list 
>>
>>
>>
>> T
>>
> 
> I think that's what he was saying. :)
Yes. In fact, when you use the ever-so-handy -vcg-ast switch, it shows this:

staticFind!(int, int, double)
{
	pure nothrow @nogc @safe int staticFind()
	{
		/*unrolled*/ {
			{
				enum ulong id = 0LU;
				alias R = int;
				return 0;
			}
			{
				enum ulong id = 1LU;
				alias R = double;
				return -1;
			}
		}
	}

}


If I compile that directly, I get the unreachable warning. Obviously there is something hidden in the actual AST that says "it's ok this statement isn't reached, because it's really skipped by returning out of the foreach".

If I use static foreach, it has the warning, and I'm assuming this is because static foreach is a straight loop unrolling, whereas foreach on a tuple has some semblance of flow control with the loop itself.

Note: I don't have any idea how this really works internally, I'm guessing at the black box behavior :)

-Steve
March 15, 2018
On 3/13/18 3:21 PM, Timon Gehr wrote:
> On 13.03.2018 18:43, H. S. Teoh wrote:
>> On Tue, Mar 13, 2018 at 01:32:55PM -0400, Steven Schveighoffer via Digitalmars-d wrote:
>> [...]
>>> An unrolled foreach on a tuple has a notion that the flow control
>>> jumps out of the loop, and it's OK to skip further loops (even though
>>> they are technically unrolled).
>> [...]
>>
>> This is not true.  Foreach on a tuple does *not* skip expanding all
>> iterations of the loop regardless of any `break`s or `continue`s. It's
>> the codegen that eliminates the resulting dead code.  See:
>>
>>     https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time#Case_Study:_foreach_over_a_type_list 
>>
>>
>>
>> T
>>
> 
> I think that's what he was saying. :)

Note, I replied with the following text, but for some reason the forum does NOT see this post. It should be here:

https://forum.dlang.org/post/p8990d$2ona$1@digitalmars.com

And it does exist on the NNTP server.

Anyway, here's what I said:

Yes. In fact, when you use the ever-so-handy -vcg-ast switch, it shows this:

staticFind!(int, int, double)
{
    pure nothrow @nogc @safe int staticFind()
    {
        /*unrolled*/ {
            {
                enum ulong id = 0LU;
                alias R = int;
                return 0;
            }
            {
                enum ulong id = 1LU;
                alias R = double;
                return -1;
            }
        }
    }

}


If I compile that directly, I get the unreachable warning. Obviously there is something hidden in the actual AST that says "it's ok this statement isn't reached, because it's really skipped by returning out of the foreach".

If I use static foreach, it has the warning, and I'm assuming this is because static foreach is a straight loop unrolling, whereas foreach on a tuple has some semblance of flow control with the loop itself.

Note: I don't have any idea how this really works internally, I'm guessing at the black box behavior

-Steve
March 15, 2018
On 03/15/2018 04:27 PM, Steven Schveighoffer wrote:

> Note, I replied with the following text, but for some reason the forum does NOT see this post. It should be here:
> 
> https://forum.dlang.org/post/p8990d$2ona$1@digitalmars.com
> 
> And it does exist on the NNTP server.

It was received by Thunderbird.

Ali