[ 
https://issues.apache.org/jira/browse/GROOVY-12408?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Paul King closed GROOVY-12408.
------------------------------

> Compile implicit-return arrow switches as switch statements
> -----------------------------------------------------------
>
>                 Key: GROOVY-12408
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12408
>             Project: Groovy
>          Issue Type: Improvement
>            Reporter: Paul King
>            Assignee: Paul King
>            Priority: Major
>             Fix For: 7.0.0-alpha-1, 6.0.0-RC-3
>
>
> GROOVY-12399 restored Java's rule that position, not arm shape, decides 
> whether a {{switch}} produces a value, but made a special case of a 
> {{switch}} that is the last statement of a body, keeping it an expression 
> there. This removes that special case: such a {{switch}} is a statement like 
> any other, and Groovy's implicit return supplies the value from the arm 
> bodies through {{ReturnAdder}}, exactly as it does for a trailing {{if}} or 
> colon-form {{switch}}.
> The resulting rule is one sentence: *a {{switch}} whose value is not used is 
> a statement, wherever it appears*; ask for expression semantics with {{return 
> switch}} or by assigning the result.
> Net -151 lines, 110 of them the parse-tree walk that existed only to 
> recognise the special case.
> This is safe now because the two things a {{SwitchStatement}} lacked have 
> since been given to it: GROOVY-12405 (jump-table dispatch) and GROOVY-12406 
> (duplicate-label diagnostic). Without those, this change would have moved the 
> most idiomatic Groovy form to a linear chain of {{isCase}} calls and silently 
> dropped a diagnostic.
> h2. Behaviour
> Measured on released Groovy 5.1.2 and 6.0.0-RC-1, and on master with this 
> change. Java columns from {{javac}} / {{javap}} on JDK 25. "n/a" means Java 
> has no counterpart; "not expressible" means {{javac}} rejects the shape.
> Master has passed through intermediate states as GROOVY-12399, GROOVY-12405 
> and GROOVY-12406 landed, and this change undoes part of the first. No 
> released Groovy exhibits any intermediate state, since all four land together 
> in 7.0.0.
> h3. Statement position
> Restored to 5.1.2 on every row.
> ||Case||Java 25||5.1.2||RC-1||with this change||
> |Value discarded, unmatched|no-op|no-op|throws|no-op|
> |No default, type-checked|compiles|compiles|compile error|compiles|
> |Enum, partial labels, unmatched|no-op|no-op|throws|no-op|
> |Enum, null selector|NPE|no-op|throws|no-op|
> |Enum covering every constant, constant added later|no-op|no-op|ICCE|no-op|
> |Nested in a non-last colon case without {{break}}|no value there|next case's 
> value|throws|next case's value|
> h3. Implicit-return position
> Java has no such position, so 5.1.2 is the only reference. Every row matches 
> it except the primitive one.
> ||Case||Java 25||5.1.2||RC-1||with this change||
> |Reference return type, unmatched|n/a|null|throws|null|
> |In a closure|n/a|null|throws|null|
> |No default, type-checked|n/a|compiles|compile error|compiles|
> |Enum, constant added later|n/a|null|ICCE|null|
> |{{case label}}, declared-{{Object}} label, statically compiled|n/a|dynamic 
> {{isCase}}|static {{isCase}}|dynamic {{isCase}}|
> |{{int}} return type, unmatched|rejects: missing 
> return|GroovyCastException|throws|0|
> The {{int}} row is the one deliberate departure from 5.1.2, and it is a 
> correction: every other way a method falls off its end already yielded the 
> type's default, and the arrow {{switch}} was the sole shape in the language 
> that threw instead.
> h3. Expression position
> Unchanged from RC-1 throughout. These are the GROOVY-12255 decisions and 
> nothing here revisits them.
> ||Case||Java 25||5.1.2||RC-1||with this change||
> |Unmatched at runtime|must be exhaustive|null|throws|throws|
> |Non-exhaustive, type-checked|compile error|compiles|compile error|compile 
> error|
> |Enum, null selector|NPE|null|throws|throws|
> |Enum, constant added later|MatchException|null|ICCE|ICCE|
> |{{case label}}, declared-{{Object}} label, statically compiled|n/a|dynamic 
> {{isCase}}|static {{isCase}}|static {{isCase}}|
> h3. Nested arrow arms
> The inner {{switch}} follows whatever its own {{switch}} is. Java cannot 
> express any of these, since a switch expression is not a statement there.
> ||The switch owning the arm is||Java 25||5.1.2||RC-1||with this change||
> |A statement whose value is discarded|not expressible|no-op|throws|no-op|
> |In implicit-return position|not expressible|null|throws|null|
> |A {{return switch}}|not expressible|null|throws|throws|
> h3. Diagnostics and dispatch
> ||Case||Java 25||5.1.2||RC-1||with this change||
> |Duplicate label, statement|compile error|accepted|compile error|compile 
> error|
> |Duplicate label, expression|compile error|accepted|compile error|compile 
> error|
> |Dispatch: arrow statement, implicit return, {{return switch}}|jump 
> table|closure call|jump table|jump table|
> |Dispatch: colon statement|jump table|{{isCase}} chain|{{isCase}} chain|jump 
> table|
> h3. Still rejected everywhere
> {{return}}, {{break}} and {{continue}} inside a statement-position arrow arm 
> remain a parse error in 5.1.2, in RC-1 and on master. Java allows all three. 
> This predates 6.0, is unchanged here, and wants its own ticket.
> h2. Dynamic versus {{@TypeChecked}} and {{@CompileStatic}}
> Every row above was also run in all three modes. All agree except these 
> three, so the position rules themselves are mode-independent.
> ||Case||dynamic||{{@TypeChecked}}||{{@CompileStatic}}||
> |Duplicate label, statement or expression|first match wins|compile 
> error|compile error|
> |Non-exhaustive expression, unmatched|throws at runtime|compile error|compile 
> error|
> |{{case label}}, declared-{{Object}} label, expression position|dynamic 
> {{isCase}}|dynamic {{isCase}}|static {{isCase}}|
> The first two are the same thing: a diagnostic the type checker can give 
> where dynamic Groovy only discovers the problem at runtime. The third is a 
> code generation choice rather than a checking one, which is why 
> {{@TypeChecked}} sides with dynamic. It is the one remaining inconsistency in 
> the design, and in statement position it no longer arises.
> h2. What the change does
> ||Change||Where||
> |A {{switch}} that is an expression statement is a {{SwitchStatement}}, with 
> no exception for the last statement of a 
> body|{{AstBuilder.visitCommandExprAlt}}, {{isSwitchValueUsed}}|
> |Deleted: the walk that recognised implicit-return position, about 110 
> lines|{{isImplicitReturnPosition}}, {{returnsValue}}, {{isLastStatement}}, 
> {{isLenientPosition}}, {{isColonArm}}|
> |Deleted: the node-metadata marker and its three readers, so an unmatched 
> switch expression always throws and exhaustiveness always 
> applies|{{SwitchExpressionUtils}}, {{SwitchExpressionWriter}}, 
> {{StaticTypesSwitchExpressionWriter}}, {{StaticTypeCheckingVisitor}}|
> |Kept: the recursion that makes a {{switch}} nested in an arrow arm follow 
> its own {{switch}}|{{isSwitchValueUsed}}, {{isSwitchUsedAsStatement}}|
> |Documentation brought in line with the rule|{{COMPATIBILITY.md}}, 
> {{core-semantics.adoc}}|
> The kept recursion is the one part of the position logic that has to survive: 
> an arm of a {{switch}} that is itself a statement produces nothing, so a 
> {{switch}} nested there is a statement too, however deeply nested.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to