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/05/08 20:15:28 UTC

[groovy] branch master updated: GROOVY-8409, GROOVY-10067: STC: method generics sentinel for return type

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 5d7c2bc  GROOVY-8409, GROOVY-10067: STC: method generics sentinel for return type
5d7c2bc is described below

commit 5d7c2bcd135c92250ec087972b4ce0341bd3b493
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat May 8 15:09:46 2021 -0500

    GROOVY-8409, GROOVY-10067: STC: method generics sentinel for return type
    
    - avoid issue with type paramter reuse within target or calling context
---
 .../transform/stc/StaticTypeCheckingSupport.java   | 14 +++---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 40 +++++++++-------
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 54 +++++++++++++---------
 3 files changed, 61 insertions(+), 47 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 de4b243..bb99761 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -471,12 +471,11 @@ public abstract class StaticTypeCheckingSupport {
             }
             return true;
         }
-        // SAM check
-        if (type.isDerivedFrom(CLOSURE_TYPE) && isSAMType(toBeAssignedTo)) {
-            return true;
+        // GROOVY-10067: unresolved argument like "N extends Number" for parameter like "Integer"
+        if (type.isGenericsPlaceHolder() && type.getUnresolvedName().charAt(0) == '#') {
+            return type.getGenericsTypes()[0].isCompatibleWith(toBeAssignedTo);
         }
-
-        return false;
+        return (type.isDerivedFrom(CLOSURE_TYPE) && isSAMType(toBeAssignedTo));
     }
 
     static boolean isVargs(final Parameter[] parameters) {
@@ -1594,7 +1593,7 @@ public abstract class StaticTypeCheckingSupport {
                 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 == oldValue || name.getName().charAt(0) == '#') continue;
                     if (newValue == null) {
                         entry.setValue(newValue = applyGenericsContext(connections, oldValue));
                         if (!checkForMorePlaceholders) {
@@ -1886,7 +1885,8 @@ public abstract class StaticTypeCheckingSupport {
         if (gt[0].isPlaceholder()) { // convert T to X
             if (type.getGenericsTypes()[0] == gt[0]) {
                 return type; // nothing to do
-            } else if (!hasNonTrivialBounds(gt[0])) {
+            } else if (!hasNonTrivialBounds(gt[0])
+                    || type.getGenericsTypes()[0] == gt[0].getNodeMetaData(GenericsType.class)) {
                 ClassNode cn = make(gt[0].getName());
                 cn.setRedirect(type);
                 cn.setGenericsTypes(gt);
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 171b963..130a4b6 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1498,12 +1498,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
                 FieldNode field = current.getDeclaredField(propertyName);
                 if (field == null) {
-                    if (current.getSuperClass() != null) {
-                        queue.addFirst(current.getUnresolvedSuperClass());
-                    }
-                    for (ClassNode face : current.getAllInterfaces()) {
-                        queue.add(GenericsUtils.parameterizeType(current, face));
-                    }
+                    if (current.getSuperClass() != null)
+                        queue.addFirst(current.getSuperClass());
+                    Collections.addAll(queue, current.getInterfaces());
                 }
 
                 // in case of a lookup on Class we look for instance methods on Class
@@ -1602,11 +1599,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             }
 
             // GROOVY-5568: the property may be defined by DGM
-            List<ClassNode> dgmReceivers = new ArrayList<>(2);
-            dgmReceivers.add(receiverType);
-            if (isPrimitiveType(receiverType))
-                dgmReceivers.add(getWrapper(receiverType));
-            for (ClassNode dgmReceiver : dgmReceivers) {
+            for (ClassNode dgmReceiver : isPrimitiveType(receiverType) ? new ClassNode[]{receiverType, getWrapper(receiverType)} : new ClassNode[]{receiverType}) {
                 List<MethodNode> methods = findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), dgmReceiver, "get" + capName, ClassNode.EMPTY_ARRAY);
                 for (MethodNode method : findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), dgmReceiver, "is" + capName, ClassNode.EMPTY_ARRAY)) {
                     if (Boolean_TYPE.equals(getWrapper(method.getReturnType()))) methods.add(method);
@@ -2414,7 +2407,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         typeCheckingContext.isInStaticContext = oldStaticContext;
         for (Parameter parameter : getParametersSafe(expression)) {
             typeCheckingContext.controlStructureVariables.remove(parameter);
-            // GROOVY-10071: visit param default argument expression if present
+            // GROOVY-10072: visit param default argument expression if present
             visitInitialExpression(parameter.getInitialExpression(), varX(parameter), parameter);
         }
     }
@@ -2645,7 +2638,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         ClassNode[] args = getArgumentTypes(argumentList);
 
         try {
-
             // method call receivers are :
             //   - possible "with" receivers
             //   - the actual receiver as found in the method call expression
@@ -5245,13 +5237,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
         Map<GenericsTypeName, GenericsType> context = method.isStatic() || method instanceof ConstructorNode
                                             ? null : extractPlaceHolders(null, receiver, getDeclaringClass(method, arguments));
-        GenericsType[] methodGenericTypes = method instanceof ConstructorNode ? method.getDeclaringClass().getGenericsTypes() : method.getGenericsTypes();
+        GenericsType[] methodGenericTypes = method instanceof ConstructorNode ? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context, method.getGenericsTypes());
 
         // 1) resolve type parameters of method
 
         if (methodGenericTypes != null) {
             Map<GenericsTypeName, GenericsType> resolvedPlaceholders = new HashMap<>();
-            for (GenericsType gt : applyGenericsContext(context, methodGenericTypes)) resolvedPlaceholders.put(new GenericsTypeName(gt.getName()), gt);
+            for (GenericsType gt : methodGenericTypes) resolvedPlaceholders.put(new GenericsTypeName(gt.getName()), gt);
             applyGenericsConnections(extractGenericsConnectionsFromArguments(methodGenericTypes, method.getParameters(), arguments, explicitTypeHints), resolvedPlaceholders);
 
             returnType = applyGenericsContext(resolvedPlaceholders, returnType);
@@ -5321,6 +5313,20 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             extractGenericsConnectionsForSuperClassAndInterfaces(connections, resolvedPlaceholders);
         }
 
+        for (GenericsType gt : methodGenericTypes) {
+            // GROOVY-8049, GROOVY-10067, et al.: provide "no type witness" mapping for param
+            resolvedPlaceholders.computeIfAbsent(new GenericsTypeName(gt.getName()), gtn -> {
+                GenericsType xxx = new GenericsType(ClassHelper.makeWithoutCaching("#"),
+                        applyGenericsContext(resolvedPlaceholders, gt.getUpperBounds()),
+                        applyGenericsContext(resolvedPlaceholders, gt.getLowerBound()));
+                xxx.getType().setRedirect(gt.getType().redirect());
+                xxx.putNodeMetaData(GenericsType.class, gt);
+                xxx.setName("#" + gt.getName());
+                xxx.setPlaceholder(true);
+                return xxx;
+            });
+        }
+
         return resolvedPlaceholders;
     }
 
@@ -5362,11 +5368,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             // connect E:T from source to E:Type from target
             for (GenericsType placeholder : aNode.getGenericsTypes()) {
                 for (Map.Entry<GenericsTypeName, GenericsType> e : source.entrySet()) {
-                    if (e.getValue() == placeholder) {
+                    if (e.getValue().getNodeMetaData(GenericsType.class) == placeholder) {
                         Optional.ofNullable(target.get(e.getKey()))
                             // skip "f(g())" for "f(T<String>)" and "<U extends Number> U g()"
                             .filter(gt -> isAssignableTo(gt.getType(), placeholder.getType()))
-                            .ifPresent(gt -> linked.put(new GenericsTypeName(placeholder.getName()), gt));
+                            .ifPresent(gt -> linked.put(new GenericsTypeName(e.getValue().getName()), gt));
                         break;
                     }
                 }
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 740972e..0f0b962 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -387,12 +387,12 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             g((Integer) getNumber())
 
             i = getNumber()
-          //f(getNumber())
-          //g(getNumber())
+            f(getNumber())
+            g(getNumber())
 
             i = number
-          //f(number)
-          //g(number)
+            f(number)
+            g(number)
         '''
     }
 
@@ -424,7 +424,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         ['R', 'S', 'T', 'U'].each { t -> // BiFunction uses R, T and U
             assertScript """
                 def <$t> $t applyFunction(java.util.function.BiFunction<Date, URL, $t> action) {
-                    $t result = action.apply(new Date(), new URL('http://www.example.com'))
+                    $t result = action.apply(new Date(), new URL('https://www.example.com'))
                     return result
                 }
 
@@ -436,6 +436,24 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         }
     }
 
+    // GROOVY-8409, GROOVY-10067
+    void testReturnTypeInferenceWithMethodGenerics15() {
+        shouldFailWithMessages '''
+            List<CharSequence> list = ['x'].collect() // GROOVY-10074
+        ''',
+        'Incompatible generic argument types. Cannot assign java.util.List<java.lang.String> to: java.util.List<java.lang.CharSequence>'
+
+        shouldFailWithMessages '''
+            List<CharSequence> list = ['x'].stream().toList() // TODO: fix type param bound of StreamGroovyMethods#toList(Stream<T>)
+        ''',
+        'Incompatible generic argument types. Cannot assign java.util.List<java.lang.String> to: java.util.List<java.lang.CharSequence>'
+
+        assertScript '''
+            import static java.util.stream.Collectors.toList
+            List<CharSequence> list = ['x'].stream().collect(toList())
+        '''
+    }
+
     void testDiamondInferrenceFromConstructor1() {
         assertScript '''
             class Foo<U> {
@@ -1296,35 +1314,27 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     void testShouldUseMethodGenericType3() {
         ['', 'static'].each { mods ->
             assertScript """
-                $mods void setM(List<String> strings) {
-                }
-                void test() {
-                    m = Collections.emptyList() // Cannot assign value of type List<T> to variable of List<String>
-                }
-                test()
-            """
-            assertScript """
-                $mods void setM(Collection<String> strings) {
+                $mods void setX(List<String> strings) {
                 }
                 void test() {
-                    m = Collections.emptyList()
+                    x = Collections.emptyList()
                 }
                 test()
             """
             assertScript """
-                $mods void setM(Iterable<String> strings) {
+                $mods void setX(Collection<String> strings) {
                 }
                 void test() {
-                    m = Collections.emptyList()
+                    x = Collections.emptyList()
                 }
                 test()
             """
 
             shouldFailWithMessages """
-                $mods void setM(List<String> strings) {
+                $mods void setX(List<String> strings) {
                 }
                 void test() {
-                    m = Collections.<Integer>emptyList()
+                    x = Collections.<Integer>emptyList()
                 }
             """, 'Incompatible generic argument types. Cannot assign java.util.List<java.lang.Integer> to: java.util.List<java.lang.String>'
         }
@@ -3319,11 +3329,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     // GROOVY-7804
     void testParameterlessClosureToGenericSAMTypeArgumentCoercion() {
         assertScript '''
-            interface Supplier<T> {
-                public <T> T get()
-            }
+            interface Supplier<T> { public T get() }
 
-            static <T> T doGet(Supplier<T> supplier) { supplier.get() }
+            def <T> T doGet(Supplier<T> supplier) { supplier.get() }
 
             assert doGet { -> 'foo' } == 'foo'
         '''