You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2021/04/07 20:34:36 UTC
[groovy] 01/01: GROOVY-10006: STC: unify types of multiple type
witnesses in method call
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY-10006
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 0298550430621493d1ef7a5b85b32e7c3654d7d1
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Apr 7 15:31:48 2021 -0500
GROOVY-10006: STC: unify types of multiple type witnesses in method call
---
.../groovy/ast/tools/WideningCategories.java | 46 ++++++++++++++--------
.../transform/stc/StaticTypeCheckingSupport.java | 19 ++++++---
.../groovy/transform/stc/GenericsSTCTest.groovy | 38 ++++++++++--------
3 files changed, 63 insertions(+), 40 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java b/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
index a0b7c70..6f15e6b 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
@@ -52,7 +52,6 @@ import static org.codehaus.groovy.ast.ClassHelper.isNumberType;
import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
-import static org.codehaus.groovy.ast.GenericsType.GenericsTypeName;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
@@ -138,21 +137,21 @@ public class WideningCategories {
}
/**
* It is of a long category, if the provided type is a
- * long, its wrapper or if it is a long category.
+ * long, its wrapper or if it is a long category.
*/
public static boolean isLongCategory(ClassNode type) {
return type==long_TYPE || isIntCategory(type);
}
/**
* It is of a BigInteger category, if the provided type is a
- * long category or a BigInteger.
+ * long category or a BigInteger.
*/
public static boolean isBigIntCategory(ClassNode type) {
return type==BigInteger_TYPE || isLongCategory(type);
}
/**
* It is of a BigDecimal category, if the provided type is a
- * BigInteger category or a BigDecimal.
+ * BigInteger category or a BigDecimal.
*/
public static boolean isBigDecCategory(ClassNode type) {
return type==BigDecimal_TYPE || isBigIntCategory(type);
@@ -301,13 +300,13 @@ public class WideningCategories {
ClassNode superClass = source.getUnresolvedSuperClass();
// copy generic type information if available
if (superClass!=null && superClass.isUsingGenerics()) {
- Map<GenericsTypeName, GenericsType> genericsTypeMap = GenericsUtils.extractPlaceholders(source);
+ Map<GenericsType.GenericsTypeName, GenericsType> genericsTypeMap = GenericsUtils.extractPlaceholders(source);
GenericsType[] genericsTypes = superClass.getGenericsTypes();
if (genericsTypes!=null) {
GenericsType[] copyTypes = new GenericsType[genericsTypes.length];
for (int i = 0; i < genericsTypes.length; i++) {
GenericsType genericsType = genericsTypes[i];
- GenericsTypeName gtn = new GenericsTypeName(genericsType.getName());
+ GenericsType.GenericsTypeName gtn = new GenericsType.GenericsTypeName(genericsType.getName());
if (genericsType.isPlaceholder() && genericsTypeMap.containsKey(gtn)) {
copyTypes[i] = genericsTypeMap.get(gtn);
} else {
@@ -474,7 +473,7 @@ public class WideningCategories {
Collections.addAll(interfaces, node.getInterfaces());
extractInterfaces(node.getSuperClass(), interfaces);
}
-
+
/**
* Given the list of interfaces implemented by two class nodes, returns the list of the most specific common
* implemented interfaces.
@@ -614,19 +613,19 @@ public class WideningCategories {
compileTimeClassNode = upper.equals(OBJECT_TYPE) && interfaces.length>0?interfaces[0]:upper;
this.name = name;
usesGenerics = upper.isUsingGenerics();
- List<GenericsType[]> genericsTypesList = new LinkedList<GenericsType[]>();
+ List<GenericsType[]> genericsTypesList = new LinkedList<>();
genericsTypesList.add(upper.getGenericsTypes());
- for (ClassNode anInterface : interfaces) {
+ for (ClassNode anInterface : interfaces) {
usesGenerics |= anInterface.isUsingGenerics();
genericsTypesList.add(anInterface.getGenericsTypes());
- for (MethodNode methodNode : anInterface.getMethods()) {
+ for (MethodNode methodNode : anInterface.getMethods()) {
MethodNode method = addMethod(methodNode.getName(), methodNode.getModifiers(), methodNode.getReturnType(), methodNode.getParameters(), methodNode.getExceptions(), methodNode.getCode());
method.setDeclaringClass(anInterface); // important for static compilation!
}
- }
+ }
setUsingGenerics(usesGenerics);
if (usesGenerics) {
- List<GenericsType> asArrayList = new ArrayList<GenericsType>();
+ List<GenericsType> asArrayList = new ArrayList<>();
for (GenericsType[] genericsTypes : genericsTypesList) {
if (genericsTypes!=null) {
Collections.addAll(asArrayList, genericsTypes);
@@ -646,7 +645,12 @@ public class WideningCategories {
}
public String getLubName() {
- return this.name;
+ return name;
+ }
+
+ @Override
+ public String getText() {
+ return text;
}
@Override
@@ -662,14 +666,22 @@ public class WideningCategories {
@Override
public int hashCode() {
int result = super.hashCode();
-// result = 31 * result + (compileTimeClassNode != null ? compileTimeClassNode.hashCode() : 0);
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
@Override
- public String getText() {
- return text;
+ public GenericsType asGenericsType() {
+ ClassNode[] ubs;
+ if (upper.equals(OBJECT_TYPE)) {
+ ubs = interfaces; // Object is implicit
+ } else {
+ ubs = new ClassNode[interfaces.length + 1]; ubs[0] = upper;
+ System.arraycopy(interfaces, 0, ubs, 1, interfaces.length);
+ }
+ GenericsType gt = new GenericsType(ClassHelper.makeWithoutCaching("?"), ubs, null);
+ gt.setWildcard(true);
+ return gt;
}
@Override
@@ -724,7 +736,7 @@ public class WideningCategories {
}
return true;
}
-
+
/**
* Determines if the source class implements an interface or subclasses the target type.
* This method takes the {@link org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode lowest
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 265ed6c..d0e73ea 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1505,13 +1505,20 @@ public abstract class StaticTypeCheckingSupport {
if (!compatibleConnection(resolved, candidate)) {
if (!resolved.isPlaceholder() && !resolved.isWildcard()
- && !fixedPlaceHolders.contains(entry.getKey())
- && compatibleConnection(candidate, resolved)) {
- // was "T=Integer" and now is "T=Number" or "T=Object"
- resolvedMethodGenerics.put(entry.getKey(), candidate);
- } else {
- return true; // incompatible
+ && !fixedPlaceHolders.contains(entry.getKey())) {
+ // GROOVY-5692, GROOVY-10006: multiple witnesses
+ if (compatibleConnection(candidate, resolved)) {
+ // was "T=Integer" and now is "T=Number" or "T=Object"
+ resolvedMethodGenerics.put(entry.getKey(), candidate);
+ continue;
+ } else if (!candidate.isPlaceholder() && !candidate.isWildcard()) {
+ // combine "T=Integer" and "T=String" to produce "T=? extends Serializable & Comparable<...>"
+ ClassNode lub = WideningCategories.lowestUpperBound(candidate.getType(), resolved.getType());
+ resolvedMethodGenerics.put(entry.getKey(), lub.asGenericsType());
+ continue;
+ }
}
+ return true; // incompatible
}
}
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index ac1f8a7..13f4988 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -1581,27 +1581,31 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- // GROOVY-5692
- void testIncompatibleArgumentsForPlaceholders1() {
- shouldFailWithMessages '''
- public <T> void printEqual(T arg1, T arg2) {
- println arg1 == arg2
- }
- printEqual(1, 'foo')
- ''', '#printEqual(T, T) with arguments [int, java.lang.String]'
+ // GROOVY-5692, GROOVY-10006
+ void testCompatibleArgumentsForPlaceholders1() {
+ assertScript '''
+ def <T> T test(T one, T two) { }
+ def result = test(1,"II")
+ '''
+ assertScript '''
+ def <T> T test(T one, T two, T three) { }
+ def result = test(1,"II",Class)
+ '''
+ assertScript '''
+ def <T extends Number> T test(T one, T two) { }
+ def result = test(1L,2G)
+ '''
}
// GROOVY-5692
- void testIncompatibleArgumentsForPlaceholders2() {
- shouldFailWithMessages '''
- public <T> void printEqual(T arg1, List<T> arg2) {
- println arg1 == arg2
- }
- printEqual(1, ['foo'])
- ''', '#printEqual(T, java.util.List <T>) with arguments [int, java.util.ArrayList <java.lang.String>]'
+ void testCompatibleArgumentsForPlaceholders2() {
+ assertScript '''
+ def <T> boolean test(T one, List<T> many) { }
+ test(1,["II","III"])
+ '''
}
- void testIncompatibleArgumentsForPlaceholders3() {
+ void testIncompatibleArgumentsForPlaceholders1() {
shouldFailWithMessages '''
def <T extends Number> T test(T one, T two) { }
test(1,"II")
@@ -1610,7 +1614,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
// GROOVY-9902: incomplete generics should not stop type checking
- void testIncompatibleArgumentsForPlaceholders4() {
+ void testIncompatibleArgumentsForPlaceholders2() {
shouldFailWithMessages '''
class Holder<Unknown> {
TypedProperty<Number, Unknown> numberProperty = prop(Number)