You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2026/03/31 22:25:53 UTC
(groovy) branch master updated (6e1947d1ba -> 308270cc97)
This is an automated email from the ASF dual-hosted git repository.
paulk pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
from 6e1947d1ba bump dependency metadata
new 3a4978d09f GROOVY-11888: STC: method resolution fails for UnionTypeClassNode due to premature covariant elimination
new 308270cc97 GROOVY-11888: STC: method resolution fails for UnionTypeClassNode due to premature covariant elimination
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../transform/stc/StaticTypeCheckingSupport.java | 13 +-
.../transform/stc/TypeInferenceSTCTest.groovy | 138 +++++++++++++++++++++
2 files changed, 146 insertions(+), 5 deletions(-)
(groovy) 01/02: GROOVY-11888: STC: method resolution fails for UnionTypeClassNode due to premature covariant elimination
Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 3a4978d09ff7d3c79e918f94dc523e5c3b43a929
Author: Paul King <pa...@asert.com.au>
AuthorDate: Mon Mar 30 11:09:48 2026 +1000
GROOVY-11888: STC: method resolution fails for UnionTypeClassNode due to premature covariant elimination
---
.../transform/stc/StaticTypeCheckingSupport.java | 6 +-
.../transform/stc/TypeInferenceSTCTest.groovy | 138 +++++++++++++++++++++
2 files changed, 142 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index cb84c11b1c..c7676a2ba4 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -953,8 +953,8 @@ public abstract class StaticTypeCheckingSupport {
// GROOVY-8965: type disjunction
boolean duckType = receiver instanceof UnionTypeClassNode;
- if (methods.size() > 1 && !first(methods).isConstructor())
- methods = removeCovariantsAndInterfaceEquivalents(methods, duckType);
+ if (!duckType && methods.size() > 1 && !first(methods).isConstructor())
+ methods = removeCovariantsAndInterfaceEquivalents(methods, false);
if (!duckType && argumentTypes == null) {
return asList(methods); // GROOVY-11683: no covariants or equivalents
@@ -965,6 +965,8 @@ public abstract class StaticTypeCheckingSupport {
var view = methods;
if (duckType) {
view = methods.stream().filter(m -> implementsInterfaceOrSubclassOf(rcvr, m.getDeclaringClass())).toList();
+ if (view.size() > 1 && !first(view).isConstructor())
+ view = removeCovariantsAndInterfaceEquivalents(view, true);
}
view = chooseBestMethods(rcvr, view, argumentTypes);
if (view.isEmpty()) {
diff --git a/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy b/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
index c6a9a17c1b..4adea4b051 100644
--- a/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
+++ b/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
@@ -431,6 +431,144 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase {
'Incompatible instanceof types: java.lang.Integer and java.lang.Long'
}
+ // GROOVY-7971: nested && within || — method calls on narrowed types within
+ // each && branch should work; the || produces a union for the body
+ @Test
+ void testInstanceOf18() {
+ assertScript '''
+ int test(Object x) {
+ if (x instanceof String && x.length() > 0 || x instanceof List && x.size() > 0) {
+ return 1
+ }
+ return 0
+ }
+ assert test('hello') == 1
+ assert test([1, 2, 3]) == 1
+ assert test('') == 0
+ assert test([]) == 0
+ assert test(42) == 0
+ '''
+ }
+
+ // GROOVY-7971: ternary with || instanceof in condition
+ @Test
+ void testInstanceOf19() {
+ assertScript '''
+ String test(Object x) {
+ (x instanceof String || x instanceof Integer) ? x.toString() : 'other'
+ }
+ assert test('hi') == 'hi'
+ assert test(42) == '42'
+ assert test(3.14) == 'other'
+ '''
+ }
+
+ // GROOVY-7971: negated || instanceof
+ @Test
+ void testInstanceOf20() {
+ assertScript '''
+ void test(Object x) {
+ if (!(x instanceof String || x instanceof Integer)) {
+ assert x != null
+ }
+ }
+ test('hello')
+ test(42)
+ test(3.14)
+ '''
+ }
+
+ // GROOVY-7971: chained || with 3+ instanceof checks
+ @Test
+ void testInstanceOf21() {
+ assertScript '''
+ void test(Object x) {
+ if (x instanceof String || x instanceof Integer || x instanceof List) {
+ assert "$x" != null // should be String|Integer|List
+ }
+ }
+ test('hello')
+ test(42)
+ test([1, 2])
+ '''
+ }
+
+ // GROOVY-7971: RHS of || should not see LHS instanceof narrowing
+ @Test
+ void testInstanceOf22() {
+ assertScript '''
+ void test(Number n) {
+ if (n instanceof Integer || n.doubleValue() > 0) {
+ assert "$n" != null // n should be Integer|Number
+ }
+ }
+ test(42)
+ test(1.5)
+ '''
+ }
+
+ // GROOVY-7971: closure shared variable with || instanceof
+ @Test
+ void testInstanceOf23() {
+ assertScript '''
+ void test(Object x) {
+ if (x instanceof String || x instanceof Integer) {
+ def c = { -> x.toString() }
+ assert c() != null
+ }
+ }
+ test('hello')
+ test(42)
+ '''
+ }
+
+ // GROOVY-11888: method resolution on union type — toString() is on Object
+ // and should be found via (String|List) union
+ @Test
+ void testInstanceOf24() {
+ assertScript '''
+ void test(Object x) {
+ if (x instanceof String || x instanceof List) {
+ assert x.toString() != null
+ }
+ }
+ test('hello')
+ test([1, 2])
+ '''
+ }
+
+ // GROOVY-7971: negated || instanceof — re-check instanceof in else branch
+ @Test @org.junit.jupiter.api.Disabled('requires instanceof compatibility fix for UnionTypeClassNode')
+ void testInstanceOf25() {
+ assertScript '''
+ void test(Object x) {
+ if (!(x instanceof String || x instanceof Integer)) {
+ assert x != null
+ } else {
+ assert x instanceof String || x instanceof Integer
+ }
+ }
+ test('hello')
+ test(42)
+ test(3.14)
+ '''
+ }
+
+ // GROOVY-11888: method resolution on union type — intValue() is on Number
+ // and should be found via (Integer|Number) union
+ @Test
+ void testInstanceOf26() {
+ assertScript '''
+ void test(Number n) {
+ if (n instanceof Integer || n.intValue() > 0) {
+ assert n.intValue() >= 0
+ }
+ }
+ test(42)
+ test(1.5)
+ '''
+ }
+
// GROOVY-5226
@Test
void testNestedInstanceOf1() {
(groovy) 02/02: GROOVY-11888: STC: method resolution fails for UnionTypeClassNode due to premature covariant elimination
Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 308270cc972cc761301f0c304c937fa4beeda11f
Author: Paul King <pa...@asert.com.au>
AuthorDate: Mon Mar 30 11:09:48 2026 +1000
GROOVY-11888: STC: method resolution fails for UnionTypeClassNode due to premature covariant elimination
---
.../groovy/transform/stc/StaticTypeCheckingSupport.java | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index c7676a2ba4..2122c91778 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -953,11 +953,11 @@ public abstract class StaticTypeCheckingSupport {
// GROOVY-8965: type disjunction
boolean duckType = receiver instanceof UnionTypeClassNode;
- if (!duckType && methods.size() > 1 && !first(methods).isConstructor())
- methods = removeCovariantsAndInterfaceEquivalents(methods, false);
-
- if (!duckType && argumentTypes == null) {
- return asList(methods); // GROOVY-11683: no covariants or equivalents
+ if (!duckType) {
+ if (methods.size() > 1 && !first(methods).isConstructor())
+ methods = removeCovariantsAndInterfaceEquivalents(methods, false);
+ if (argumentTypes == null)
+ return asList(methods); // GROOVY-11683: no covariants or equivalents
}
Set<MethodNode> bestMethods = new HashSet<>(); // choose best method(s) for each possible receiver
@@ -965,8 +965,9 @@ public abstract class StaticTypeCheckingSupport {
var view = methods;
if (duckType) {
view = methods.stream().filter(m -> implementsInterfaceOrSubclassOf(rcvr, m.getDeclaringClass())).toList();
- if (view.size() > 1 && !first(view).isConstructor())
+ if (view.size() > 1) { // GROOVY-11888
view = removeCovariantsAndInterfaceEquivalents(view, true);
+ }
}
view = chooseBestMethods(rcvr, view, argumentTypes);
if (view.isEmpty()) {