Consider the following:
let x: Int
while true {
if Bool.random() {
x = 42
break
} else {
continue
}
}
print(x) // ERROR: Constant 'x' used before being initialized
Shouldn't path analysis understand that, in order to get to print(x), x must be initialized?
1 Like
eskimo
(Quinn “The Eskimo!”)
2
Well, “should” is always a tricky word. I recommend that you file a bug about this. There may be good technical reasons for why this isn’t possible, but it doesn’t hurt to ask (-:
Please post your bug number, just for the record.
Share and Enjoy
Quinn “The Eskimo!” @ DTS @ Apple
1 Like
Nobody1707
(Nobody1707)
3
The weird thing is that it works if you implement the while loop manually:
do {
let x: Int
loop: do {
if Bool.random() {
x = 42
break loop
} else {
continue loop
}
}
print(x)
}
1 Like
ole
(Ole Begemann)
4
I'm surprised you can use do { … } to write a loop in this way.
If I'm reading The Swift Programming Language correctly, continue is only meant to work in loops (i.e. for in, while, and repeat { … } while. And break is meant to work in loops and if and switch statements.
itaiferber
(Itai Ferber)
5
The key to the above is that the do, break, and continue statement are all labeled; the relevant portion of the book to reference is for Labeled Statements:
You can prefix a loop statement, an if statement, a switch statement, or a do statement with a statement label , which consists of the name of the label followed immediately by a colon (:). Use statement labels with break and continue statements to be explicit about how you want to change control flow in a loop statement or a switch statement, as discussed in Break Statement and Continue Statement below.
From the linked continue section:
When a continue statement is followed by the name of a statement label, it ends program execution of the current iteration of the loop statement named by that label.
When a continue statement isn’t followed by the name of a statement label, it ends program execution of the current iteration of the innermost enclosing loop statement in which it occurs.
The same goes for break.
It's not terribly common, but totally possible to express!
3 Likes
Jumhyn
(Frederick Kellison-Linn)
6
See, I read that wording as excluding the construction above. The book says you can label a "loop statement, an if statement, a switch statement, or a do statement," but the continue section only permits continuing a "loop statement named by that label."
OTOH, the break section explicitly allows other labeled statement types:
When a break statement is followed by the name of a statement label, it ends program execution of the loop, if statement, or switch statement named by that label.
(though, notably, do statements are not mentioned here).
Perhaps this is a documentation bug rather than an implementation bug, but I think the wording here is not at all an endorsement of the behavior above!
itaiferber
(Itai Ferber)
7
I would say that this is implicit and can stand to be written out more explicitly in the book, rather than an implementation bug. The language grammar as documented unambiguously supports this (and, well, the language itself supports it with an implementation), but I think it should be called out more clearly.
The grammar allows loop-statements, if-statements, switch-statements, and do-statements to be labeled with a label-name. The only places a label-name may be referenced is in that statement-label, in break-statements, and in continue-statements:
Grammar of a labeled statement
labeled-statement → statement-label loop-statement
labeled-statement → statement-label if-statement
labeled-statement → statement-label switch-statement
labeled-statement → statement-label do-statement
statement-label → label-name :
label-name → identifier
Grammar of a break statement
break-statement → break label-name opt
Grammar of a continue statement
continue-statement → continue label-name opt
(The Summary of the Grammar has the full language grammar in one place for searching.)
Since the only thing you can do with a label is break from it or continue to it, there'd really be no point in being allowed to label a do-statement otherwise, if this construction were not allowed.
5 Likes
tera
8
Feels like goto to me, I'd remove labels altogether (although I might be biased as I haven't used them once).
Re the original question - I believe escape analysis could be better, and it's not the end of the world that it doesn't currently catch this situation as the workaround is trivial.
Nobody1707
(Nobody1707)
9
It's largely a leftover from when repeat { ... } while loops were spelled do { ... } while.
2 Likes
I think I got it, and it's, probably, technically not a bug. The key is that the compiler doesn't seem to take into account some values into the code path analysis, and probably that's the intended behavior.
For example this doesn't compile
let y: Int
if true {
y = 42
}
print(y)
even if y will surely be initialized when used. The fact that I wrote if true is not taken into account by the compiler. There still seems to be something weird going on because for example in this code
let y: Int
if true {
y = 42
} else {
y = 43 // WARNING: Will never be executed
}
print(y)
a warning is emitted because it's impossible to enter the else branch based on the true value.
Anyway, that seems to be the reason. In fact, both in
while true {
if Bool.random() {
x = 42
break
} else {
continue
}
}
and
repeat {
if Bool.random() {
x = 42
break
} else {
continue
}
} while true
the only guarantee that the block will loop until x is initialized is in the true value passed to the while declaration.
As mentioned by @Nobody1707, it works if you manually implement the loop with a labeled do block:
let x: Int
loop: do {
if Bool.random() {
x = 42
break loop
} else {
continue loop
}
}
print(x)
and that's because the loop is guaranteed by very code structure, not due to a specific true value.
2 Likes