Is old style compile-time foreach redundant?

Steven Schveighoffer schveiguy at yahoo.com
Tue Jan 9 19:24:11 UTC 2018


On 1/9/18 11:35 AM, H. S. Teoh wrote:
> On Tue, Jan 09, 2018 at 10:57:03AM -0500, Steven Schveighoffer via Digitalmars-d-learn wrote:
>> I may have been misleading when I made my first comment. What I mean
>> is that you *can't* break or continue a static foreach, even with
>> labels. However, you *can* do it to a standard foreach over a tuple.
>> This may be one reason you want to use a tuple-foreach over a static
>> foreach.
> [...]
> 
> Actually, that's wrong too. Tuple-foreach does not interpret
> break/continue either. Here's a proof:
> 
> 	alias Seq(A...) = A;
> 	foreach (i; Seq!(0, 1, 2, 3)) {
> 		static if (i==2)
> 			break;
> 		static assert(i < 3); // will fail on the 4th iteration
> 	}
> 
> What actually happens is that all iterations are unrolled, then the
> unreachable iterations are elided by the optimizer during codegen. The
> foreach itself is not affected by break/continue at all.

A break or continue is simply a goto underneath. A goto in an unrolled 
loop isn't much different than a goto in a um... rolled loop :) It's 
just that there are copies of each loop body, and the gotos need copies 
of the labels.

So no, it's not "interpreted" by the foreach statement, but the foreach 
statement provides the anchors for the goto label targets.

e.g.:

int x;

foreach(i; Seq!(0, 1, 2, 3)) {
    x += i;
    static if(i % 2) continue;
    x *= i;
}

=>

int x;
{
    x += 0;
    x *= 0;
}
{
    x += 1;
    goto label1;
    x *= 1;
}
{
label1:
    x += 2;
    x *= 2;
}
{
    x += 3;
    goto label2;
    x *= 3;
}
label2:

And then of course, the optimizer weeds out the unreachable statements. 
Doing this with static foreach wouldn't be as pleasant. You'd have to 
branch the entire loop body, or use a goto in the case of a break.

-Steve


More information about the Digitalmars-d-learn mailing list