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)