Re: Disallowing break label (and continue label) inside an expression switch
> On 23 Mar 2018, at 20:51, Guy Steelewrote: > > > String s = switch (e) { > case 0 -> break “foofoo”; > case 1: > if (p == 0) break x; > else { String z = hairy(x); break z+z; } > case 2 -> “barbar”; > }; > > Now I decide that case 1 has three subcases. So I change the `if` to a > statement `switch`. > > String s = switch (e) { > case 0 -> break “foofoo”; > case 1: > switch (p) { > case 0: break x; > case 1: break x+x; > default: String z = hairy(x); break z+z; > } > case 2 -> “barbar”; > }; > > FAIL. The inner switch is actually an expression switch, so you just need an extra break: String s = switch (e) { case 0 -> “foofoo”; case 1: break switch (p) { case 0: break x; case 1: break x+x; default: String z = hairy(x); break z+z; }; case 2 -> “barbar”; }; > All I’m doing is demonstrating that a common refactoring pattern “turn the > `if` statement into a `switch` statement” has more pitfalls than it used to > once we introduce `switch` expressions. Agreed. Gavin
Re: Disallowing break label (and continue label) inside an expression switch
Stepping back, I think there's two ways to look at this: - break expression and break label are totally different statements, that happen to be spelled similarly - break is the same statement all around, but just as return requires a value in a value-returning method and requires no value in a void method, the meaning of break must agree with the innermost breaky context. I think the latter is far easier for users to reason about, while giving up relatively little flexibility. So doing a "break label" in an e-switch is the same error as doing "return 3" in a void method. On 3/23/2018 5:42 PM, Brian Goetz wrote: The rest of it is about where to put the sharp edges: Can I break-e from a switch-e wherever I might consider doing return from a method/lambda? Or does break-e have extra restrictions to prevent certain ambiguities? Your answer is the latter. Right. To avoid ambiguity with other breaky contexts, we let the innermost breaky context determine the allowable break modes. Speaking of ambiguities, should this be illegal, even though under your rules it happens to be unambiguous? Or is it just a puzzler we tolerate? e-switch { LABEL: s-switch { { int LABEL = 2; break LABEL; } } } It could be allowed (since you can't break-e from the switch), but it seems safer to call it a compile error. After all, you can always alpha-rename the label. Also the other way: LABEL: s-switch { e-switch { int LABEL = 2; break LABEL; } } You want "break LABEL" to be immediately recognized as either a break-l or a break-e. The above cases seem to make it hard to do so. We could declare such code pathological and demand a relabel in either case, just as we declare local variable shadowing pathological in most cases, demanding a renaming of one of the variables. Local variable shadowing is more likely to occur than label shadowing, given that labels are a rare construct, so maybe we just let the above be a puzzler, rather than add a rule. Or, make it the same rule. If in "break x", x could resolve as either a label or an expression, call it an ambiguity. (In either case, depending on whether you rename the variable or the label, you could then get a different error, which is "we don't serve that kind of break round these parts.") But I think its the same game either way.
Re: Disallowing break label (and continue label) inside an expression switch
The rest of it is about where to put the sharp edges: Can I break-e from a switch-e wherever I might consider doing return from a method/lambda? Or does break-e have extra restrictions to prevent certain ambiguities? Your answer is the latter. Right. To avoid ambiguity with other breaky contexts, we let the innermost breaky context determine the allowable break modes. Speaking of ambiguities, should this be illegal, even though under your rules it happens to be unambiguous? Or is it just a puzzler we tolerate? e-switch { LABEL: s-switch { { int LABEL = 2; break LABEL; } } } It could be allowed (since you can't break-e from the switch), but it seems safer to call it a compile error. After all, you can always alpha-rename the label. Also the other way: LABEL: s-switch { e-switch { int LABEL = 2; break LABEL; } } You want "break LABEL" to be immediately recognized as either a break-l or a break-e. The above cases seem to make it hard to do so. We could declare such code pathological and demand a relabel in either case, just as we declare local variable shadowing pathological in most cases, demanding a renaming of one of the variables. Local variable shadowing is more likely to occur than label shadowing, given that labels are a rare construct, so maybe we just let the above be a puzzler, rather than add a rule. Or, make it the same rule. If in "break x", x could resolve as either a label or an expression, call it an ambiguity. (In either case, depending on whether you rename the variable or the label, you could then get a different error, which is "we don't serve that kind of break round these parts.") But I think its the same game either way.
Re: Disallowing break label (and continue label) inside an expression switch
On Mar 23, 2018, at 1:33 PM, Brian Goetzwrote: > >> i just realized that the X-heavy first column also means that I can't >> make use of statement labels *anywhere inside* of e-switch, even >> if I just copy-and-pasted self-contained code from elsewhere. > > No, you can do this: > > e-switch { > LABEL: > s-switch { > s-switch { break LABEL; } > } > } (Looking again at the table.) Yes, I see; the break-l has a P. OK, I was reading the table wrong. > You just can't "throw" *across* an e-switch boundary. The X means > if a nested construct doesn't handle it, there's a problem. On that we all agree: e-switch, like any other e, can complete normally with a value, or else can complete with a throw. It *cannot* complete with a branch to some location other than a catch statement (which location would be defined by a label, loop, or switch outside the switch, if it were anywhere, but it isn't). OK, so half of my discomfort was a temporary misunderstanding. You can paste locally consistent control flow in and out of switch-e's, even if the control flow uses statement labels. Good. The rest of it is about where to put the sharp edges: Can I break-e from a switch-e wherever I might consider doing return from a method/lambda? Or does break-e have extra restrictions to prevent certain ambiguities? Your answer is the latter. Speaking of ambiguities, should this be illegal, even though under your rules it happens to be unambiguous? Or is it just a puzzler we tolerate? e-switch { LABEL: s-switch { { int LABEL = 2; break LABEL; } } } Also the other way: LABEL: s-switch { e-switch { int LABEL = 2; break LABEL; } } You want "break LABEL" to be immediately recognized as either a break-l or a break-e. The above cases seem to make it hard to do so. We could declare such code pathological and demand a relabel in either case, just as we declare local variable shadowing pathological in most cases, demanding a renaming of one of the variables. Local variable shadowing is more likely to occur than label shadowing, given that labels are a rare construct, so maybe we just let the above be a puzzler, rather than add a rule. — John
Re: Disallowing break label (and continue label) inside an expression switch
It seems to me that your argument cuts in a different direction: If the important risk is that users can't be sure what "break X" means without a non-local scan, we should absolutely require the parentheses. I think that's a reasonable conversation -- but it wouldn't make me any more interested in softening the break-e rules. That feels like all downside and no upside to me. And, requiring the parens may well just be seen as fussiness on the part of the compiler, so I'm not sure of the point. So, we could fix the main problem by requiring "break X" to always mean the label X and "break (x)" to always mean the value x. I don't think "expressions always complete normally or throw" is a "problem" to be "fixed" -- I think its a feature! i just realized that the X-heavy first column also means that I can't make use of statement labels *anywhere inside* of e-switch, even if I just copy-and-pasted self-contained code from elsewhere. No, you can do this: e-switch { LABEL: s-switch { s-switch { break LABEL; } } } You just can't "throw" *across* an e-switch boundary. The X means if a nested construct doesn't handle it, there's a problem.
Re: Disallowing break label (and continue label) inside an expression switch
On Mar 23, 2018, at 1:20 PM, John Rosewrote: > > I think mandating "break (e)" is a lighter > weight solution, even though it has some of the > #{ Stand Out }# smell. > > — John P.S. And to be clear, I'm simply pointing out surprising consequences of your design, to elucidate its trade-offs. I won't be too disappointed if we go with the odd new contextual restriction on "break-l", since "break-l" is a marginal feature only seldom used. It can be relegated to a helper method as a last resort. But, again, mandating a helper method to get out of a tight spot can be a design smell. Whatever we decide, let's document the consequences on coders as best as we can.
Re: Disallowing break label (and continue label) inside an expression switch
I think the burden should go the other way -- we should justify why such flexibility is warranted, and I think this is difficult to do. The conditions under which such control flow are useful are already small, and further, when you might want to code like that, the expression switch form offers relatively little benefit over expression statements anyway (since by definition you're using a lot of control-flow statements to make your complex control-flow decision.) In exchange, the cost on all readers is high, because they can't be sure about what "break X" means. Why burden all users with this complexity? Why spend any of our complexity budget on such a low-leverage option? On 3/23/2018 3:53 PM, John Rose wrote: On Mar 23, 2018, at 12:41 PM, Brian Goetz> wrote: I think so. Its not detecting the ambiguity, its that the possibility of mixing control flow kinds means the poor reader has to reason about all the possibilities, and not be sure what "break FOO" means in a switch statement. A lighter fix, then, would be to require parentheses always. An even lighter fix would be leave that kind of clarification to style guides.
Re: Disallowing break label (and continue label) inside an expression switch
The symptom of this design choice is the large number of X entries in the break-e column, where there are few X entries in the return column. I suppose you are aiming in this direction to reduce occasional ambiguities between break-e and break-l. But such ambiguities can be controlled in other ways while getting more free passage of break-e to its enclosing switch, through intervening control flow. I am not worried that there will be occasional ambiguities; I'm worried that the code reader will see "break X" in a switch statement and _not be able to know what it means_ without doing a nonlocal analysis. Given that the requirement for a nested switch statement to break-e out of an enclosing switch expression already seems tenuous, it seemed best to segregate the break forms. Either break/break-L is allowed in a given context, or break-E is allowed in that context, but not both. Specifically, this should not be ruled out, IMO: int x = switch (e) ( case 0: for (int y : someInts) { if (y < x) break (y); } return 0; default: return e; }; I would prefer to rule it out. We don't allow returns out of the middle of a conditional, after all. I prefer the simplicity that "all expressions either complete normally or complete abruptly with cause exception." If you really need this kind of control flow, where maybe you yield a value or maybe you return, use a switch statement: int result; switch (e) { case 0: for (int y : someInts) { if (y < x) { result = y; break ; } } return 0; default: return e; } // consume result here We have already discussed ways to deal with the ambiguity that could (rarely!!) arise if a name like "y" above also looks like a statement label. Reporting the ambiguity as an error is an easy thing to do, or quietly accepting the label in preference to the expression is also easy. Under either rule, users can use parens (as above) to make clear which kind of break statement they mean. Am I missing some other consideration? I think so. Its not detecting the ambiguity, its that the possibility of mixing control flow kinds means the poor reader has to reason about all the possibilities, and not be sure what "break FOO" means in a switch statement.
Re: Disallowing break label (and continue label) inside an expression switch
On Mar 23, 2018, at 11:45 AM, Brian Goetzwrote: > > More formally; we can construct a table whose rows are the control constructs > and whose columns are the nonlocal branches, and whose entries are "handlers" > for a nonlocal branch. Each block has a parent (the immediately enclosing > block.) It surprises me that break-e is so much more restricted than return, since I would expect we would aim at a design where an switch-e could be refactored incrementally to a method and vice versa (in cases where the e-switch had no side effects on locals). The symptom of this design choice is the large number of X entries in the break-e column, where there are few X entries in the return column. I suppose you are aiming in this direction to reduce occasional ambiguities between break-e and break-l. But such ambiguities can be controlled in other ways while getting more free passage of break-e to its enclosing switch, through intervening control flow. The break-e column assigns L to switch-e (obviously the root requirement) and X to everything else except block. I would expect the break-e column would assign P to everything except lambda and method, in symmetry with the return column, which assigns P to everything except switch-e (and naturally L to lambda and method). Specifically, this should not be ruled out, IMO: int x = switch (e) ( case 0: for (int y : someInts) { if (y < x) break (y); } return 0; default: return e; }; We have already discussed ways to deal with the ambiguity that could (rarely!!) arise if a name like "y" above also looks like a statement label. Reporting the ambiguity as an error is an easy thing to do, or quietly accepting the label in preference to the expression is also easy. Under either rule, users can use parens (as above) to make clear which kind of break statement they mean. Am I missing some other consideration? — John
Re: Disallowing break label (and continue label) inside an expression switch
See comments in two places below. > On Mar 23, 2018, at 2:45 PM, Brian Goetzwrote: > > Just want to sync and make sure we're on the same page here... > > Certain constructs (switch expression, switch statement, for/while/do) give > meaning to some flavor of break. Inside those, you can't use the other > flavor, nor can you break "through" a construct of the opposite flavor. > > switch-expression { > break / break-label not allowed; > break-expr allowed; > continue, return not allowed; > > if (foo) { > break / break-label disallowed; > break-expr allowed; > } > > LOOP: > for (...) { > break, continue allowed; > return not allowed; > break-label allowed if within the switch expression > break expression not allowed; > } > > switch (e) { > // same as for loop Actually, same as for loop _except_ continue not allowed. > switch-expression { > break expr allowed > break, break-label, continue, return not allowed > } > } > > More formally; we can construct a table whose rows are the control constructs > and whose columns are the nonlocal branches, and whose entries are "handlers" > for a nonlocal branch. Each block has a parent (the immediately enclosing > block.) > > break-e break break-l continue return > > switch-e L X X X X > switch-s X L P L P > for X L P L P > while X L P L P > block P P P P P > labeled X X L*X P > lambdaX X X X L > methodX X X X L > > The handlers mean: > > X -- not allowed > P -- let the parent handle it > L -- handle it and complete normally > L* -- handle it and complete normally if the labels match, otherwise P > > (I might have mangled the labeled row, in which case surely Guy will correct > me.) Right. It should be: labeled P P L*P P “In all other cases of abrupt completion of the Statement, the labeled statement completes abruptly for the same reason.” JLS SE8 14.7, page 413 All the other rows look good to me. > The idea here is that each nested block acts as an "exception handler", > catching some exceptions, and propagating others, and some contexts act as > exception barriers, like trying to throw a "continue" out of a method. —Guy
Re: Disallowing break label (and continue label) inside an expression switch
Just want to sync and make sure we're on the same page here... Certain constructs (switch expression, switch statement, for/while/do) give meaning to some flavor of break. Inside those, you can't use the other flavor, nor can you break "through" a construct of the opposite flavor. switch-expression { break / break-label not allowed; break-expr allowed; continue, return not allowed; if (foo) { break / break-label disallowed; break-expr allowed; } LOOP: for (...) { break, continue allowed; return not allowed; break-label allowed if within the switch expression break expression not allowed; } switch (e) { // same as for loop switch-expression { break expr allowed break, break-label, continue, return not allowed } } More formally; we can construct a table whose rows are the control constructs and whose columns are the nonlocal branches, and whose entries are "handlers" for a nonlocal branch. Each block has a parent (the immediately enclosing block.) break-e break break-l continue return switch-e L X X X X switch-s X L P L P for X L P L P while X L P L P block P P P P P labeled X X L* X P lambda X X X X L method X X X X L The handlers mean: X -- not allowed P -- let the parent handle it L -- handle it and complete normally L* -- handle it and complete normally if the labels match, otherwise P (I might have mangled the labeled row, in which case surely Guy will correct me.) The idea here is that each nested block acts as an "exception handler", catching some exceptions, and propagating others, and some contexts act as exception barriers, like trying to throw a "continue" out of a method. On 3/2/2018 9:30 AM, Remi Forax wrote: Hi all, as far as i remember, the current idea to differentiate between a break label and a break value is to let the compiler figure this out, i wonder if it's not simpler to disallow break label (and continue label) inside an expression switch. After all, an expression switch do not exist yet, so no backward compatibility issue, it may make some refactoring impossible but had the great advantage to do not allow a lot of puzzler codes like the one below. enum Result { ONE, MANY } Result result(String[] args) { ONE: for(String s: args) { return switch(s) { case "several": case "many": break MANY; case "one": break ONE; default: continue; }; } throw ...; } Rémi
Re: Disallowing break label (and continue label) inside an expression switch
Jan has updated the prototype to make the switch expression a bubble penatrable only by exceptions. Please take a look! On 3/2/2018 9:30 AM, Remi Forax wrote: Hi all, as far as i remember, the current idea to differentiate between a break label and a break value is to let the compiler figure this out, i wonder if it's not simpler to disallow break label (and continue label) inside an expression switch. After all, an expression switch do not exist yet, so no backward compatibility issue, it may make some refactoring impossible but had the great advantage to do not allow a lot of puzzler codes like the one below. enum Result { ONE, MANY } Result result(String[] args) { ONE: for(String s: args) { return switch(s) { case "several": case "many": break MANY; case "one": break ONE; default: continue; }; } throw ...; } Rémi
Re: Disallowing break label (and continue label) inside an expression switch
> Envoyé: Vendredi 2 Mars 2018 20:45:52 > Objet: Re: Disallowing break label (and continue label) inside an expression > switch > On Mar 2, 2018, at 8:12 AM, Brian Goetz < [ mailto:brian.go...@oracle.com | > brian.go...@oracle.com ] > wrote: >> Do we disallow the "break TOP" and return in the inner switch? >> IOW, does the expression form a barrier through which control >> can only pass via break or exceptions? > IIRC last time we talked about this that was the consensus. > It's consistent with what Kevin just wrote also, about constraining > the result of a switch-expr. > We currently don't have any expressions which can branch, > just return normally or throw. Let's keep it that way. I'm glad we all agree here. > (Remember we had an option for lambdas to branch > outside the lambda expression, and we decisively shut > it down for various reasons.) > We *do* have expressions which can *internally branch* > without completing. Those are lambdas. Switch expressions > should also be able to have arbitrary control flow inside > (if the coder decides). But that "barrier" idea expresses > the key constraint: That the branch labels used inside > the switch are operationally disjoint from those outside. > (Separately, as a syntax constraint, labels enclosing > the same bit of syntax should be distinct. But that > doesn't mean an outer label is ever reachable from > inner control flow. Trying to reach across the barrier > must be an error.) > If someone wants to write code like Remi's puzzlers, > fine, but they have to refactor to a statement-switch, > and assign the result to a temp, like today. Statements > can branch to locations other than their standard > completion point. yes ! > — John Rémi
Re: Disallowing break label (and continue label) inside an expression switch
Hi Kevin, i've already proposed to remove 'case' (using match instead of ??) see http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-December/000211.html and http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-December/000232.html but Brian did not like it :) Rémi > De: "Kevin Bourrillion" <kev...@google.com> > À: "Brian Goetz" <brian.go...@oracle.com> > Cc: "Remi Forax" <fo...@univ-mlv.fr>, "amber-spec-experts" > <amber-spec-experts@openjdk.java.net> > Envoyé: Vendredi 2 Mars 2018 17:40:05 > Objet: Re: Disallowing break label (and continue label) inside an expression > switch > I would very much favor constraining what can be done inside an expression > switch as sharply as we can. > In fact, if we were designing the language from scratch, all at the same time, > but having already decided the behavior of the conditional operators ?: ... > might we not logically design this as similarly as possible to that? > // compare this two-way choice > return status == ABC ? something() : somethingElse(); > // to this three-way choice > return status ?? { ABC -> something(); DEF, XYZ -> somethingElse(); default -> > somethingOther(); } > (stand-in syntax only) > You could ideally think of ?: as sugar for the latter. Might we possibly be > better off thinking of it this way, and not even worrying about > multi-statement > cases (you can always extract a method)? It seems like several of our woes > disappear. > Something to consider is whether we want a construct that > readability-conscious > developers will use only as the object of a return statement or variable > assignment (which is what I think we have, currently), or one that they might > comfortably use in-line in other circumstances, like > Result r = methodCall( > param1, > param2, > status ?? { > ABC -> something(); > DEF, XYZ -> somethingElse(); > default -> somethingOther(); > }); > I can see that usage at least being debatable, while I suspect we might frown > on > it all spelled out with `switch` and `case`? > As a last point in its favor, `null` can be treated completely normally - if > you > didn't list `null ->` or `ABC, null ->`, etc., then use the default. > On Fri, Mar 2, 2018 at 8:12 AM, Brian Goetz < [ mailto:brian.go...@oracle.com > | > brian.go...@oracle.com ] > wrote: >> Thanks for bringing this up. I remember it being discussed once before, but I >> don't think we acted on it. >> I agree that expression switch is an expression, and it should either yield a >> value or throw something; breaking out of the middle of an expression is not >> something we have, nor does it seem necessary. (Though I'm sure clever folks >> could come up with a good example where it would be convenient.) >> A sensible extension of this is no "return" from a switch expression either: >> int foo(int x) { >> return switch (x) { >> case 1 -> 2; >> case 2 -> 4; >> case 3 -> 8; >> default: return Integer.MAX_VALUE; >> } >> } >> Like conditionals, then, switch expressions would either yield a value >> (through >> breaking) or throw. This seems consistent, but...what happens when we nest a >> statement in a switch expression? >> void foo(int x, int y, int z) { >> TOP: >> switch (z) { >> case 1: >> int i = switch (x) { >> case 1 -> 2; >> case 2: >> switch (y) { >> case 1: return; >> default: break TOP; >> } >> } >> } >> } >> Do we disallow the "break TOP" and return in the inner switch? IOW, does the >> expression form a barrier through which control can only pass via break or >> exceptions? >> On 3/2/2018 9:30 AM, Remi Forax wrote: >>> Hi all, >>> as far as i remember, the current idea to differentiate between a break >>> label >>> and a break value is to let the compiler figure this out, >>> i wonder if it's not simpler to disallow break label (and continue label) >>> inside >>> an expression switch. >>> After all, an expression switch do not exist yet, so no backward >>> compatibility >>> issue, it may make some refactoring impossible but had the great advantage >>> to >>> do not allow a lot of puzzler codes like the one below. >>> enum Result { >>> ONE, MANY >>> } >>> Result result(String[] args) { >>> ONE: for(String s: args) { >>> return switch(s) { >>> case "several": >>> case "many": >>> break MANY; >>> case "one": >>> break ONE; >>> default: >>> continue; >>> }; >>> } >>> throw ...; >>> } >>> Rémi > -- > Kevin Bourrillion | Java Librarian | Google, Inc. | [ > mailto:kev...@google.com | > kev...@google.com ]
Re: Disallowing break label (and continue label) inside an expression switch
On Mar 2, 2018, at 8:12 AM, Brian Goetzwrote: > > Do we disallow the "break TOP" and return in the inner switch? > IOW, does the expression form a barrier through which control > can only pass via break or exceptions? IIRC last time we talked about this that was the consensus. It's consistent with what Kevin just wrote also, about constraining the result of a switch-expr. We currently don't have any expressions which can branch, just return normally or throw. Let's keep it that way. (Remember we had an option for lambdas to branch outside the lambda expression, and we decisively shut it down for various reasons.) We *do* have expressions which can *internally branch* without completing. Those are lambdas. Switch expressions should also be able to have arbitrary control flow inside (if the coder decides). But that "barrier" idea expresses the key constraint: That the branch labels used inside the switch are operationally disjoint from those outside. (Separately, as a syntax constraint, labels enclosing the same bit of syntax should be distinct. But that doesn't mean an outer label is ever reachable from inner control flow. Trying to reach across the barrier must be an error.) If someone wants to write code like Remi's puzzlers, fine, but they have to refactor to a statement-switch, and assign the result to a temp, like today. Statements can branch to locations other than their standard completion point. — John
Re: Disallowing break label (and continue label) inside an expression switch
Thanks for bringing this up. I remember it being discussed once before, but I don't think we acted on it. I agree that expression switch is an expression, and it should either yield a value or throw something; breaking out of the middle of an expression is not something we have, nor does it seem necessary. (Though I'm sure clever folks could come up with a good example where it would be convenient.) A sensible extension of this is no "return" from a switch expression either: int foo(int x) { return switch (x) { case 1 -> 2; case 2 -> 4; case 3 -> 8; default: return Integer.MAX_VALUE; } } Like conditionals, then, switch expressions would either yield a value (through breaking) or throw. This seems consistent, but...what happens when we nest a statement in a switch expression? void foo(int x, int y, int z) { TOP: switch (z) { case 1: int i = switch (x) { case 1 -> 2; case 2: switch (y) { case 1: return; default: break TOP; } } } } Do we disallow the "break TOP" and return in the inner switch? IOW, does the expression form a barrier through which control can only pass via break or exceptions? On 3/2/2018 9:30 AM, Remi Forax wrote: Hi all, as far as i remember, the current idea to differentiate between a break label and a break value is to let the compiler figure this out, i wonder if it's not simpler to disallow break label (and continue label) inside an expression switch. After all, an expression switch do not exist yet, so no backward compatibility issue, it may make some refactoring impossible but had the great advantage to do not allow a lot of puzzler codes like the one below. enum Result { ONE, MANY } Result result(String[] args) { ONE: for(String s: args) { return switch(s) { case "several": case "many": break MANY; case "one": break ONE; default: continue; }; } throw ...; } Rémi
Disallowing break label (and continue label) inside an expression switch
Hi all, as far as i remember, the current idea to differentiate between a break label and a break value is to let the compiler figure this out, i wonder if it's not simpler to disallow break label (and continue label) inside an expression switch. After all, an expression switch do not exist yet, so no backward compatibility issue, it may make some refactoring impossible but had the great advantage to do not allow a lot of puzzler codes like the one below. enum Result { ONE, MANY } Result result(String[] args) { ONE: for(String s: args) { return switch(s) { case "several": case "many": break MANY; case "one": break ONE; default: continue; }; } throw ...; } Rémi