Re: Disallowing break label (and continue label) inside an expression switch

2018-03-27 Thread Gavin Bierman

> On 23 Mar 2018, at 20:51, Guy Steele  wrote:
> 
> 
>  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

2018-03-23 Thread Brian Goetz

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

2018-03-23 Thread Brian Goetz




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

2018-03-23 Thread John Rose
On Mar 23, 2018, at 1:33 PM, Brian Goetz  wrote:
> 
>> 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

2018-03-23 Thread Brian Goetz




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

2018-03-23 Thread John Rose
On Mar 23, 2018, at 1:20 PM, John Rose  wrote:
> 
> 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

2018-03-23 Thread Brian Goetz
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

2018-03-23 Thread Brian Goetz




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

2018-03-23 Thread John Rose
On Mar 23, 2018, at 11:45 AM, Brian Goetz  wrote:
> 
> 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

2018-03-23 Thread Guy Steele
See comments in two places below.

> On Mar 23, 2018, at 2:45 PM, Brian Goetz  wrote:
> 
> 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

2018-03-23 Thread Brian Goetz

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

2018-03-08 Thread Brian Goetz
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

2018-03-02 Thread Remi Forax
> 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

2018-03-02 Thread forax
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

2018-03-02 Thread John Rose
On Mar 2, 2018, at 8:12 AM, Brian Goetz  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.

(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

2018-03-02 Thread Brian Goetz
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

2018-03-02 Thread Remi Forax
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