blackdrag commented on code in PR #2922:
URL: https://github.com/apache/groovy/pull/2922#discussion_r3988013411


##########
src/main/java/org/codehaus/groovy/classgen/asm/SwitchExpressionWriter.java:
##########
@@ -213,12 +215,41 @@ protected void writeDefaultOrThrow(final SwitchExpression 
expression, final int
         if (defaultStatement != null && !defaultStatement.isEmpty()) {
             defaultStatement.visit(controller.getAcg());
             if (maybeFallsThrough(defaultStatement)) {
-                throwUnmatchedSelector(selectorIndex, selectorType);
+                writeUnmatchedSelector(expression, selectorIndex, 
selectorType);
             }
             return;
         }
         if (completeEnum) {
             throwIncompatibleClassChangeError();
+        } else {
+            writeUnmatchedSelector(expression, selectorIndex, selectorType);
+        }
+    }
+
+    /**
+     * Whether an unmatched selector completes the expression with {@code null}
+     * rather than throwing: a switch in implicit-return position 
(GROOVY-12399).
+     *
+     * @since 6.0.0
+     */
+    protected static boolean unmatchedYieldsNull(final SwitchExpression 
expression) {
+        return 
Boolean.TRUE.equals(expression.getNodeMetaData(UNMATCHED_YIELDS_NULL));
+    }
+
+    /**
+     * Completes the expression for an unmatched selector: {@code null} (cast
+     * to the result type) for a switch in implicit-return position, otherwise
+     * an {@code IllegalStateException} (GROOVY-12399).
+     */
+    private void writeUnmatchedSelector(final SwitchExpression expression, 
final int selectorIndex, final ClassNode selectorType) {
+        if (unmatchedYieldsNull(expression)) {
+            CompileStack.SwitchExpressionContext context = 
controller.getCompileStack().requireSwitchExpressionContext();
+            OperandStack operandStack = controller.getOperandStack();
+            controller.getMethodVisitor().visitInsn(ACONST_NULL);
+            operandStack.push(ClassHelper.OBJECT_TYPE);
+            operandStack.doGroovyCast(context.resultType);

Review Comment:
   this will call a quite complex path for what should be a cast at bytecode 
level. I suggest to not to use operandStack for this. operandStack is good when 
you need to handle variables and their types, especially if they may be a 
reference or not. But here it is a simple "load null, cast to X" operation.



##########
src/test/groovy/org/codehaus/groovy/classgen/Groovy12255.groovy:
##########
@@ -1045,11 +1045,12 @@ final class Groovy12255 {
 
     @Test
     void typeCheckedUnmatchedIsError() {
-        // exhaustiveness is a static type-checker error
+        // exhaustiveness is a static type-checker error (in expression 
position;
+        // implicit-return position yields null when unmatched, GROOVY-12399)
         def err = shouldFail(MultipleCompilationErrorsException, '''
             @groovy.transform.TypeChecked
             def meth(int a) {
-                switch (a) {
+                return switch (a) {

Review Comment:
   Why add "return"?



##########
src/test/groovy/org/codehaus/groovy/classgen/Groovy12255.groovy:
##########
@@ -537,7 +537,7 @@ final class Groovy12255 {
             enum Flag { ON, OFF }
 
             String m(Flag f) {
-                switch (f) {
+                return switch (f) {

Review Comment:
   Why add "return"?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to