Intended behavior of continue in repeat while?

I was a bit surprised by the behavior of a continue statement within a repeat while loop.

Looking at this program:

var x = 0

here: repeat {                  // <-----.
    x += 1                      //       |
    print("A:", x)              //       |
    if x < 4 { continue here }  // ------'
    print("B:", x)
} while x < 2

print("C:", x)

I would expect the program flow to be as indicated in the comments, until x is 4, and thus that the output would be:

A: 1
A: 2
A: 3
A: 4
B: 4
C: 4

But it turns out that the actual program flow is as follows, until x is 2:

var x = 0

here: repeat {                  // <-----.
    x += 1                      //       |
    print("A:", x)              //       |
    if x < 4 { continue here }  // ---.  |
    print("B:", x)              //    |  |
} while x < 2                   // <==´--'

print("C:", x)

And thus the actual output is:

A: 1
A: 2
C: 2

I can kind of understand the logic of always evaluating the while condition, even for iterations that are interrupted by a continue statement, but I can't help thinking that it's somewhat unintuitive, and therefore I just want to make sure that it is working as intended. Is it?

I believe this is the intended behavior. The continue statement is intended to exit the iteration early (to avoid ugly if {} nesting), but not avoid the loop mechanics. Despite the placement of the label in the actual code, it's intended to be parsed as follows:

let x = 0
repeat {
    print("hi")
    if x == 0 {
        goto next_iteration
    }
    print("bye")

    next_iteration:
} while ...

That's been my mental model since I learned of the construct in C, some 25 (very odd) years ago.

2 Likes

A helpful way to think about it might be that continue, like break, only jumps "forward". continue jumps forward to the end of a loop body, while break jumps forward to just after the loop.

1 Like

(Off topic, unrelated) Interesting that do and repeat have similar semantics. (trolling: How else are going going to write a c style for loop? )

  for( a = 0; a < 10; a += 1 ){
      printf("value of a: %d\n", a);
   }

//v1
var i = 0; forloop:do { guard i < 10 else { break forloop }; defer { i += 1 };
    print(i)
continue forloop
}

// v2
var j = 0; while j < 10 { defer { j += 1 }
    print(j)
} 

// v3
var k = 0; repeat  { defer { k += 1 }
    print(k)
} while k < 10

I like v2

1 Like

Its possible that any do semantics that match repeat are artifacts of older Swift versions where do was repeat instead of a scope declaration.

Oh, ick. @Nobody1707 is right, that's a bug—or else we'd allow it with if and switch as well. Can you file it, please?

Filed [SR-7708] labeled do statement can be looped with continue · Issue #50248 · apple/swift · GitHub

Apparently, and imo surprisingly, being allowed to use continue with a labeled do, effectively iterating it, is intended behavior and not a bug, at least according to @John_McCall’s resolution for my above bug report.

Please have a look at it.

Apart from your argument (that if continue should be allowed with labeled do, then it should also be allowed with labeled if and switch), the current behavior is not documented anywhere (afaics).

I'd guess that the following is the true reason for the current behavior: