This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 9b3eeb9e22da47c5650a187fcb8e1b23936321e5
Author: Eric Milles <[email protected]>
AuthorDate: Sat Dec 27 15:09:20 2025 -0600

    GROOVY-10702: STC: multiple `instanceof` and sparse property access
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 13 ++++-
 .../transform/stc/TypeInferenceSTCTest.groovy      | 65 ++++++++++++++--------
 2 files changed, 53 insertions(+), 25 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 5b5e709a82..fec219cd57 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1653,9 +1653,18 @@ out:    if ((samParameterTypes.length == 1 && 
isOrImplements(samParameterTypes[0
         List<Receiver<String>> receivers = new ArrayList<>();
         addReceivers(receivers, makeOwnerList(objectExpression), 
pexp.isImplicitThis());
 
-        for (Receiver<String> receiver : receivers) {
+    on: for (Receiver<String> receiver : receivers) {
             ClassNode receiverType = receiver.getType();
-
+            if (receiverType instanceof UnionTypeClassNode ut) {
+                for (ClassNode type : ut.getDelegates()) { // GROOVY-8965, 
GROOVY-10702
+                    var copy = (PropertyExpression) 
pexp.transformExpression(ex -> ex);
+                    copy.setObjectExpression(varX("_", type));
+                    if (!existsProperty(copy, readMode, null))
+                        continue on;
+                }
+                pexp.putNodeMetaData(DYNAMIC_RESOLUTION, Boolean.TRUE);
+                return true;
+            }
             if (receiverType.isArray() && propertyName.equals("length")) {
                 pexp.putNodeMetaData(READONLY_PROPERTY, Boolean.TRUE);
                 storeType(pexp, int_TYPE);
diff --git a/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy 
b/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
index 94d38ad2c0..884423da79 100644
--- a/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
+++ b/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
@@ -403,17 +403,17 @@ class TypeInferenceSTCTest extends 
StaticTypeCheckingTestCase {
                int foo() { 1 }
             }
             class B {
-               int foo2() { 2 }
+               int bar() { 2 }
             }
 
             def o = new A()
-            int result = o instanceof A ? o.foo() : (o instanceof B ? o.foo2() 
: 3)
+            int result = o instanceof A ? o.foo() : (o instanceof B ? o.bar() 
: 3)
             assert result == 1
             o = new B()
-            result = o instanceof A ? o.foo() : (o instanceof B ? o.foo2() : 3)
+            result = o instanceof A ? o.foo() : (o instanceof B ? o.bar() : 3)
             assert result == 2
             o = new Object()
-            result = o instanceof A ? o.foo() : (o instanceof B ? o.foo2() : 3)
+            result = o instanceof A ? o.foo() : (o instanceof B ? o.bar() : 3)
             assert result == 3
         '''
     }
@@ -421,22 +421,43 @@ class TypeInferenceSTCTest extends 
StaticTypeCheckingTestCase {
     void testMultipleInstanceOf1() {
         assertScript '''
             class A {
-                void bar() {
+                void m() {
                 }
             }
             class B {
-                void bar() {
+                void m() {
                 }
             }
 
             void test(o) {
                 if (o instanceof A || o instanceof B) {
-                    o.bar()
+                    o.m()
                 }
             }
             test(new A())
             test(new B())
         '''
+
+        assertScript '''
+            class A {
+                float getP() {
+                    return 1.0
+                }
+            }
+            class B {
+                short getP() {
+                    return 2
+                }
+            }
+
+            def foo(o) {
+                if (o instanceof A || o instanceof B) {
+                    return o.p
+                }
+            }
+            assert foo(new A()) == 1
+            assert foo(new B()) == 2
+        '''
     }
 
     void testMultipleInstanceOf2() {
@@ -465,19 +486,17 @@ class TypeInferenceSTCTest extends 
StaticTypeCheckingTestCase {
 
     // GROOVY-8965
     void testMultipleInstanceOf4() {
-        for (o in ['o', '((Number) o)']) {
-            assertScript """
-                def foo(o) {
-                    if (o instanceof Integer || o instanceof Double) {
-                        ${o}.floatValue() // ClassCastException
-                    }
+        assertScript '''
+            def foo(o) {
+                if (o instanceof Integer || o instanceof Double) {
+                    o.floatValue() // ClassCastException
                 }
-                def bar = foo(1.1d)
-                assert bar == 1.1f
-                def baz = foo(1)
-                assert baz == 1
-            """
-        }
+            }
+            def bar = foo(1.1d)
+            assert bar == 1.1f
+            def baz = foo(1)
+            assert baz == 1
+        '''
     }
 
     // GROOVY-11559
@@ -1312,7 +1331,7 @@ class TypeInferenceSTCTest extends 
StaticTypeCheckingTestCase {
             }
         ''',
         'No such property: canonicalName for class: java.lang.Object'
-/*
+
         shouldFailWithMessages '''
             void test(something) {
                 switch (something) {
@@ -1320,12 +1339,12 @@ class TypeInferenceSTCTest extends 
StaticTypeCheckingTestCase {
                   case File:
                     something.toString()
                   default:
-                    something.canonicalName // TODO: GROOVY-10702
+                    something.canonicalName // GROOVY-10702
                 }
             }
         ''',
-        'No such property: canonicalName for class: java.lang.Object'
-*/
+        'No such property: canonicalName for class: (java.lang.Class | 
java.io.File | java.lang.Object)'
+
         shouldFailWithMessages '''
             void test(something) {
                 switch (something) {

Reply via email to