Consider this code:
void foo()
{
goto bar;
int x = 0;
bar: ;
}
GCC and Clang reject it, because the jump to bar:
bypasses variable initialization. MSVC doesn't complain at all (except using x
after bar:
causes a warning).
We can do a similar thing with a switch
:
void foo()
{
switch (0)
{
int x = 0;
case 0: ;
}
}
Now all three compilers emit errors.
Are those snippets ill-formed? Or do they cause UB?
I used to think that both were ill-formed, but I can't find the revelant parts of the standard. [stmt.goto] doesn't say anything about this, and neither does [stmt.select].
It's ill-formed when the initialization is non-vacuous.
[stmt.dcl]
3 It is possible to transfer into a block, but not in a way that
bypasses declarations with initialization (including ones in
conditions and init-statements). A program that jumps from a point
where a variable with automatic storage duration is not in scope to a
point where it is in scope is ill-formed unless the variable has
vacuous initialization ([basic.life]). In such a case, the variables
with vacuous initialization are constructed in the order of their
declaration.
The initializer makes the initialization non-vacuous. To contrast, this
void foo()
{
goto bar;
int x; // no initializer
bar: ;
}
would be well-formed. Though the usual caveats about using x
with an indeterminate value would apply.
From goto statement:
If transfer of control enters the scope of any automatic variables
(e.g. by jumping forward over a declaration statement), the program is
ill-formed (cannot be compiled), unless all variables whose scope is
entered have
- scalar types declared without initializers
- class types with trivial default constructors and trivial destructors declared
without initializers
- cv-qualified versions of one of the above
- arrays of one of the above