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 2022/09/12 22:37:51 UTC

[groovy] branch GROOVY_2_5_X updated: GROOVY-5692, GROOVY-10006, GROOVY-10339: STC: multiple witnesses in call

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

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


The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
     new f96f6513ab GROOVY-5692, GROOVY-10006, GROOVY-10339: STC: multiple witnesses in call
f96f6513ab is described below

commit f96f6513abec4624042952abbd3d09ba9ffd83f9
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Sep 12 16:51:44 2022 -0500

    GROOVY-5692, GROOVY-10006, GROOVY-10339: STC: multiple witnesses in call
---
 .../groovy/ast/tools/WideningCategories.java       | 14 ++++++
 .../transform/stc/StaticTypeCheckingSupport.java   | 43 ++++++++----------
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 51 ++++++++++++----------
 3 files changed, 62 insertions(+), 46 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 5137bd8584..a4ea1aef6a 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
@@ -673,6 +673,20 @@ public class WideningCategories {
             return text;
         }
 
+        @Override
+        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
         public ClassNode getPlainNodeReference() {
             ClassNode[] intf = interfaces==null?null:new ClassNode[interfaces.length];
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 2e38604bbc..45ecca7e51 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -462,9 +462,6 @@ public abstract class StaticTypeCheckingSupport {
     static boolean isAssignableTo(ClassNode type, ClassNode toBeAssignedTo) {
         if (UNKNOWN_PARAMETER_TYPE == type) return true;
         if (type == toBeAssignedTo) return true;
-        if (toBeAssignedTo.redirect() == STRING_TYPE && type.redirect() == GSTRING_TYPE) {
-            return true;
-        }
         if (isPrimitiveType(toBeAssignedTo)) toBeAssignedTo = getWrapper(toBeAssignedTo);
         if (isPrimitiveType(type)) type = getWrapper(type);
         if (NUMBER_TYPES.containsKey(type.redirect()) && NUMBER_TYPES.containsKey(toBeAssignedTo.redirect())) {
@@ -481,7 +478,7 @@ public abstract class StaticTypeCheckingSupport {
             return true;
         }
         if (implementsInterfaceOrIsSubclassOf(type, toBeAssignedTo)) {
-            if (OBJECT_TYPE.equals(toBeAssignedTo)) return true;
+            if (toBeAssignedTo.equals(OBJECT_TYPE)) return true;
             if (toBeAssignedTo.isUsingGenerics()) {
                 // perform additional check on generics
                 // ? extends toBeAssignedTo
@@ -490,12 +487,9 @@ public abstract class StaticTypeCheckingSupport {
             }
             return true;
         }
-
-        //SAM check
         if (type.isDerivedFrom(CLOSURE_TYPE) && isSAMType(toBeAssignedTo)) {
             return true;
         }
-
         return false;
     }
 
@@ -1601,17 +1595,21 @@ public abstract class StaticTypeCheckingSupport {
                 continue;
             }
             if (!compatibleConnection(resolved, connection)) {
-                if (!(resolved.isPlaceholder() || resolved.isWildcard()) &&
-                        !fixedGenericsPlaceHolders.contains(entry.getKey()) &&
-                        compatibleConnection(connection, resolved)) {
-                    // we did for example find T=String and now check against
-                    // T=Object, which fails the first compatibleConnection check
-                    // but since T=Object works for both, the second one will pass
-                    // and we need to change the type for T to the more general one
-                    resolvedMethodGenerics.put(entry.getKey(), connection);
-                } else {
-                    return false;
+                if (!resolved.isPlaceholder() && !resolved.isWildcard()
+                        && !fixedGenericsPlaceHolders.contains(entry.getKey())) {
+                    // GROOVY-5692, GROOVY-10006: multiple witnesses
+                    if (compatibleConnection(connection, resolved)) {
+                        // was "T=Integer" and now is "T=Number" or "T=Object"
+                        resolvedMethodGenerics.put(entry.getKey(), connection);
+                        continue;
+                    } else if (!connection.isPlaceholder() && !connection.isWildcard()) {
+                        // combine "T=Integer" and "T=String" to produce "T=? extends Serializable & Comparable<...>"
+                        ClassNode lub = WideningCategories.lowestUpperBound(connection.getType(), resolved.getType());
+                        resolvedMethodGenerics.put(entry.getKey(), lub.asGenericsType());
+                        continue;
+                    }
                 }
+                return false; // incompatible
             }
         }
         return true;
@@ -1656,13 +1654,10 @@ public abstract class StaticTypeCheckingSupport {
         return type;
     }
 
-    static void applyGenericsConnections(
-            Map<GenericsTypeName, GenericsType> connections,
-            Map<GenericsTypeName, GenericsType> resolvedPlaceholders
-    ) {
-        if (connections == null) return;
-        int count = 0;
+    static void applyGenericsConnections(final Map<GenericsTypeName, GenericsType> connections, final Map<GenericsTypeName, GenericsType> resolvedPlaceholders) {
+        if (!asBoolean(connections)) return;
 
+        int count = 0;
         while (count++ < 10000) {
             boolean checkForMorePlaceholders = false;
             for (Map.Entry<GenericsTypeName, GenericsType> entry : resolvedPlaceholders.entrySet()) {
@@ -2081,11 +2076,11 @@ public abstract class StaticTypeCheckingSupport {
      * A DGM-like method which adds support for method calls which are handled
      * specifically by the Groovy compiler.
      */
+    @SuppressWarnings("unused")
     private static class ObjectArrayStaticTypesHelper {
         public static <T> T getAt(T[] arr, int index) {
             return null;
         }
-
         public static <T, U extends T> void putAt(T[] arr, int index, U object) {
         }
     }
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index cc6a225b0e..991dd48adf 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -323,6 +323,33 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    @NotYetImplemented // GROOVY-10339
+    void testReturnTypeInferenceWithMethodGenerics21() {
+        for (type in ['Character', 'Integer']) {
+            shouldFailWithMessages """
+                Character foo() {
+                }
+                def <T> T bar(T x, T y) {
+                }
+                $type z = bar(foo(), 1)
+            """,
+            'Cannot assign value of type'
+        }
+    }
+
+    // GROOVY-10339
+    void testReturnTypeInferenceWithMethodGenerics22() {
+        for (type in ['Comparable', 'Serializable']) {
+            assertScript """
+                String foo() {
+                }
+                def <T> T bar(T x, T y) {
+                }
+                $type z = bar(foo(), 1)
+            """
+        }
+    }
+
     // GROOVY-10749
     void testReturnTypeInferenceWithMethodGenerics29() {
         if (!GroovyAssert.isAtLeastJdk('1.8')) return
@@ -2348,7 +2375,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    @NotYetImplemented // GROOVY-5692, GROOVY-10006
+    // GROOVY-5692, GROOVY-10006
     void testCompatibleArgumentsForPlaceholders1() {
         assertScript '''
             def <T> T test(T one, T two) { }
@@ -2364,7 +2391,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    @NotYetImplemented // GROOVY-5692
+    // GROOVY-5692
     void testCompatibleArgumentsForPlaceholders2() {
         assertScript '''
             def <T> boolean test(T one, List<T> many) { }
@@ -2520,26 +2547,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         }
     }
 
-    void testIncompatibleGenericsForTwoArguments() {
-        shouldFailWithMessages '''
-            public <T> void printEqual(T arg1, T arg2) {
-                println arg1 == arg2
-            }
-            printEqual(1, 'foo')
-        ''',
-        '#printEqual(T, T) with arguments [int, java.lang.String]'
-    }
-
-    void testIncompatibleGenericsForTwoArgumentsUsingEmbeddedPlaceholder() {
-        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.List <java.lang.String>]'
-    }
-
     // GROOVY-9902: incomplete generics should not stop type checking
     void testIncompatibleArgumentsForPlaceholders3() {
         shouldFailWithMessages '''