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
The following commit(s) were added to refs/heads/master by this push: new deeb858cc8 GROOVY-10633, GROOVY-10662: STC: do not mix generics contexts deeb858cc8 is described below commit deeb858cc89fe8fee68bff00ef12eeb152720360 Author: Eric Milles <eric.mil...@thomsonreuters.com> AuthorDate: Tue Jun 21 09:39:09 2022 -0500 GROOVY-10633, GROOVY-10662: STC: do not mix generics contexts --- .../transform/stc/StaticTypeCheckingSupport.java | 70 +++++++++------------- .../groovy/transform/stc/GenericsSTCTest.groovy | 40 +++++++++++++ 2 files changed, 68 insertions(+), 42 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 dd687fa3a3..f68572e192 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java @@ -1603,53 +1603,39 @@ public abstract class StaticTypeCheckingSupport { static void applyGenericsConnections(final Map<GenericsTypeName, GenericsType> connections, final Map<GenericsTypeName, GenericsType> resolvedPlaceholders) { if (connections == null || connections.isEmpty()) return; - int count = 0; - while (count++ < 10000) { - boolean checkForMorePlaceholders = false; - for (Map.Entry<GenericsTypeName, GenericsType> entry : resolvedPlaceholders.entrySet()) { - // entry could be T=T, T=T extends U, T=V, T=String, T=? extends String, etc. - GenericsType oldValue = entry.getValue(); - if (oldValue.isPlaceholder()) { // T=T or V, not T=String or ? ... - GenericsTypeName name = new GenericsTypeName(oldValue.getName()); - GenericsType newValue = connections.get(name); // find "V" in T=V - if (newValue == oldValue) continue; - if (newValue == null) { - newValue = connections.get(entry.getKey()); - if (newValue != null) { // GROOVY-10315, GROOVY-10317 - newValue = getCombinedGenericsType(oldValue, newValue); - } + for (Map.Entry<GenericsTypeName, GenericsType> entry : resolvedPlaceholders.entrySet()) { + // entry could be T=T, T=T extends U, T=V, T=String, T=? extends String, etc. + GenericsType oldValue = entry.getValue(); + if (oldValue.isPlaceholder()) { // T=T or V, not T=String or ? ... + GenericsTypeName name = new GenericsTypeName(oldValue.getName()); + GenericsType newValue = connections.get(name); // find "V" in T=V + if (newValue == oldValue) continue; + if (newValue == null) { + newValue = connections.get(entry.getKey()); + if (newValue != null) { // GROOVY-10315, GROOVY-10317 + newValue = getCombinedGenericsType(oldValue, newValue); } - if (newValue == null) { - entry.setValue(newValue = applyGenericsContext(connections, oldValue)); - if (!checkForMorePlaceholders) { - checkForMorePlaceholders = !equalIncludingGenerics(oldValue, newValue); - } - } else if (!newValue.isPlaceholder() || newValue != resolvedPlaceholders.get(name)) { - // GROOVY-6787: Don't override the original if the replacement doesn't respect the bounds otherwise - // the original bounds are lost, which can result in accepting an incompatible type as an argument! - ClassNode replacementType = extractType(newValue); - ClassNode suitabilityType = !replacementType.isGenericsPlaceHolder() - ? replacementType : Optional.ofNullable(replacementType.getGenericsTypes()) - .map(gts -> extractType(gts[0])).orElse(replacementType.redirect()); - - if (oldValue.isCompatibleWith(suitabilityType)) { - if (newValue.isWildcard() && newValue.getLowerBound() == null && newValue.getUpperBounds() == null) { - // GROOVY-9998: apply upper/lower bound for unknown - entry.setValue(replacementType.asGenericsType()); - } else { - entry.setValue(newValue); - } - if (!checkForMorePlaceholders && newValue.isPlaceholder()) { - checkForMorePlaceholders = !equalIncludingGenerics(oldValue, newValue); - } + } + if (newValue == null) { + entry.setValue(newValue = applyGenericsContext(connections, oldValue)); + } else if (!newValue.isPlaceholder() || newValue != resolvedPlaceholders.get(name)) { + // GROOVY-6787: Don't override the original if the replacement doesn't respect the bounds otherwise + // the original bounds are lost, which can result in accepting an incompatible type as an argument! + ClassNode replacementType = extractType(newValue); + ClassNode suitabilityType = !replacementType.isGenericsPlaceHolder() + ? replacementType : Optional.ofNullable(replacementType.getGenericsTypes()) + .map(gts -> extractType(gts[0])).orElse(replacementType.redirect()); + + if (oldValue.isCompatibleWith(suitabilityType)) { + if (newValue.isWildcard() && newValue.getLowerBound() == null && newValue.getUpperBounds() == null) { + // GROOVY-9998: apply upper/lower bound for unknown + entry.setValue(replacementType.asGenericsType()); + } else { + entry.setValue(newValue); } } } } - if (!checkForMorePlaceholders) break; - } - if (count >= 10000) { - throw new GroovyBugError("unable to handle generics in " + resolvedPlaceholders + " with connections " + connections); } } diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy index eeda172da0..e61f98320e 100644 --- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy +++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy @@ -1382,6 +1382,46 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase { ''' } + // GROOVY-10662 + void testDiamondInferrenceFromConstructor31() { + assertScript ''' + class A<X, T> { + A(T t, X x) {} + void m(X x) {} + } + class B<T extends Number> { + void test() { + T t = (T) null + Character c = 'c' + def a = new A<>(c, t) + a.m((T) null) // Cannot find matching method A#m(T) + } + } + new B<Integer>().test() + ''' + } + + // GROOVY-10633 + void testDiamondInferrenceFromConstructor32() { + assertScript ''' + class A<T, Y> { + public B<Y> f + A(B<Y> b_of_y, T t) { + this.f = b_of_y + } + } + class B<T> { + void m(T t) { + } + } + <T extends Number> void test() { + def x = new B<T>() + new A<>(x, '').f.m((T) null) + } + test() + ''' + } + // GROOVY-10280 void testTypeArgumentPropagation() { assertScript '''