[ 
https://issues.apache.org/jira/browse/GROOVY-12289?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18107050#comment-18107050
 ] 

ASF GitHub Bot commented on GROOVY-12289:
-----------------------------------------

Copilot commented on code in PR #2826:
URL: https://github.com/apache/groovy/pull/2826#discussion_r3837895503


##########
src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java:
##########
@@ -4981,6 +4986,32 @@ && isOptimizedIntSwitch(selectorType, 
expression.getCaseStatements())) {
         }
     }
 
+    /**
+     * Reports a repeated constant case label in a switch expression. 
Sequential
+     * {@code isCase} semantics make the second arm dead code, and the 
optimized
+     * {@code tableswitch}/{@code lookupswitch} forms cannot represent it at 
all,
+     * so it is rejected here, uniformly for type-checked and 
statically-compiled
+     * code (GROOVY-12289). Labels compared are the same ones the optimizers 
key
+     * on: int-family, String and enum constants; anything else (GStrings, 
calls,
+     * regex or collection labels) cannot be proven duplicated statically and 
is
+     * left to sequential first-match-wins dispatch.
+     *
+     * @since 6.0.0
+     */
+    private void checkSwitchExpressionDuplicateLabels(final SwitchExpression 
expression) {
+        ClassNode enumType = 
unwrapEnumType(getType(expression.getExpression()));
+        Set<Object> seen = new HashSet<>();
+        for (CaseStatement caseStatement : expression.getCaseStatements()) {
+            Expression label = caseStatement.getExpression();
+            Object key = (enumType != null && enumType.isEnum()) ? 
enumConstantName(label, enumType) : null;
+            if (key == null) key = intConstant(label);
+            if (key == null) key = stringConstant(label);
+            if (key != null && !seen.add(key)) {

Review Comment:
   `enumConstantName` and `stringConstant` both produce `String` keys, so an 
enum label like `E.X` (key "X") can collide with a string label `"X"` in the 
same switch expression, incorrectly triggering a "Duplicate case label" error 
even though the labels are different expressions and the presence of a non-enum 
label already forces sequential `isCase` dispatch. Use a distinct key type for 
enum constants (e.g., pair enum type + name) to avoid cross-type collisions 
while still catching duplicates among enum constants.





> Switch expressions with duplicate case labels compile under @TypeChecked but 
> fail under @CompileStatic
> ------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12289
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12289
>             Project: Groovy
>          Issue Type: Bug
>            Reporter: Paul King
>            Assignee: Paul King
>            Priority: Minor
>
> Switch expressions with duplicate constant case labels compile under 
> {{@TypeChecked}} (and dynamic Groovy) but fail under {{@CompileStatic}}:
> {code:groovy}
> def m(int x) {
>     def r = switch (x) {
>         case 1 -> 'a'
>         case 1 -> 'b'   // dead code: first match wins
>         default -> 'c'
>     }
>     r
> }
> {code}
> Dynamic and {{@TypeChecked}}: compiles; sequential {{isCase}} semantics mean 
> the first matching arm wins. {{@CompileStatic}}: fails with {{Duplicate case 
> label: 1}}, raised from {{StaticTypesSwitchExpressionWriter}} when the 
> tableswitch/lookupswitch optimizer finds a repeated key. Reproduces for 
> int-family, String and enum constant labels.
> Two problems:
> # *Mode divergence.* Whether the code compiles should not depend on the 
> compilation mode (or on whether a bytecode optimizer happens to apply — 
> mixing a duplicated constant with a dynamic label, e.g. {{case 1; case 1; 
> case foo()}}, silently disabled the optimizer and compiled fine under 
> {{@CompileStatic}}).
> # *Broken error reporting.* The writer reports the error mid-codegen and then 
> continues, so ASM also reports a processing error on the truncated method — 
> the user sees a confusing secondary failure.
> *Fix:* detect repeated constant labels (int-family, String and enum constants 
> — the same keys the optimizers use, via the shared {{SwitchExpressionUtils}} 
> extractors) in {{StaticTypeCheckingVisitor}}, so {{@TypeChecked}} and 
> {{@CompileStatic}} both report {{[Static type checking] - Duplicate case 
> label: ...}} at the offending label. The static writer no longer errors: a 
> duplicate key just skips the optimizer like any other non-optimizable shape 
> and falls back to sequential first-match-wins dispatch. That path is only 
> reachable when type checking is bypassed ({{TypeCheckingMode.SKIP}} or a 
> type-checking extension), where dynamic semantics are the intent — previously 
> it crashed codegen.
> Unchanged: dynamic Groovy, switch *statements*, and non-constant labels 
> (GStrings, calls, ranges) — those cannot be proven duplicated statically.
> Note one deliberate tightening: duplicated constants mixed with dynamic 
> labels now error in both modes (previously accepted under {{@CompileStatic}} 
> because the optimizer bailed out silently).
> *Escape hatch:* a DSL that wants first-match-wins duplicate labels under 
> {{@TypeChecked}} can opt affected methods out via a type-checking extension 
> ({{beforeVisitMethod \{ mn -> handled = true \}}}); the method then compiles 
> and dispatches dynamically. This is method-granular — there is no per-switch 
> waiver. Covered by a new test using {{Groovy12289Extension.groovy}}.



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

Reply via email to