You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2015/11/28 20:58:05 UTC

incubator-groovy git commit: GROOVY-7538: Bound unbounded wildcards using the type declaration (closes #197)

Repository: incubator-groovy
Updated Branches:
  refs/heads/master 167708deb -> 4e3f1703e


GROOVY-7538: Bound unbounded wildcards using the type declaration (closes #197)

If there is a generic type declared with bounds, for example

    class NumberList<T extends Number> implements List<T>

but used with an unbounded wildcard

    NumberList<?> getList()

the bounds are actually implicit and need to be taken into account for the
static type checks, to be able to use the type of the parameter directly:

    getList()[0].longValue()

The type of the element is known to be Number, not Object.


Project: http://git-wip-us.apache.org/repos/asf/incubator-groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-groovy/commit/4e3f1703
Tree: http://git-wip-us.apache.org/repos/asf/incubator-groovy/tree/4e3f1703
Diff: http://git-wip-us.apache.org/repos/asf/incubator-groovy/diff/4e3f1703

Branch: refs/heads/master
Commit: 4e3f1703edaa34c617f48dcb47f884e55a36de20
Parents: 167708d
Author: Frank Pavageau <fp...@ekino.com>
Authored: Thu Nov 26 16:12:09 2015 +0100
Committer: pascalschumacher <pa...@gmx.net>
Committed: Sat Nov 28 20:57:28 2015 +0100

----------------------------------------------------------------------
 .../stc/StaticTypeCheckingSupport.java          | 48 ++++++++++++++++++++
 .../stc/StaticTypeCheckingVisitor.java          |  4 +-
 .../groovy/transform/stc/GenericsSTCTest.groovy |  2 +-
 .../classgen/asm/sc/bugs/Groovy7538Bug.groovy   |  3 --
 4 files changed, 52 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/4e3f1703/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index 7e0aa4a..4760cb3 100644
--- a/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1804,6 +1804,54 @@ public abstract class StaticTypeCheckingSupport {
         return map;
     }
 
+    /**
+     * Apply the bounds from the declared type when the using type simply declares a parameter as an unbounded wildcard.
+     *
+     * @param type A parameterized type
+     * @return A parameterized type with more precise wildcards
+     */
+    static ClassNode boundUnboundedWildcards(ClassNode type) {
+        if (type.isArray()) {
+            return boundUnboundedWildcards(type.getComponentType()).makeArray();
+        }
+        ClassNode target = type.redirect();
+        if (target == null || type == target || !isUsingGenericsOrIsArrayUsingGenerics(target)) return type;
+        ClassNode newType = type.getPlainNodeReference();
+        newType.setGenericsPlaceHolder(type.isGenericsPlaceHolder());
+        newType.setGenericsTypes(boundUnboundedWildcards(type.getGenericsTypes(), target.getGenericsTypes()));
+        return newType;
+    }
+
+    private static GenericsType[] boundUnboundedWildcards(GenericsType[] usage, GenericsType[] declaration) {
+        GenericsType[] newGts = new GenericsType[usage.length];
+        for (int i = 0; i < usage.length; i++) {
+            newGts[i] = boundUnboundedWildcard(usage[i], declaration[i]);
+        }
+        return newGts;
+    }
+
+    private static GenericsType boundUnboundedWildcard(GenericsType gt, GenericsType spec) {
+        if (isUnboundedWildcard(gt)) {
+            ClassNode base = ClassHelper.makeWithoutCaching("?");
+            // The bounds on the declared type are at least as good as the ones on an unbounded wildcard, since it has
+            // none!
+            GenericsType newGt = new GenericsType(base, spec.getUpperBounds(), spec.getLowerBound());
+            newGt.setWildcard(true);
+            return newGt;
+        }
+        return gt;
+    }
+
+    private static boolean isUnboundedWildcard(GenericsType gt) {
+        if (gt.isWildcard() && gt.getLowerBound() == null) {
+            ClassNode[] upperBounds = gt.getUpperBounds();
+            return upperBounds == null ||
+                    upperBounds.length == 0 ||
+                    (upperBounds.length == 1 && OBJECT_TYPE.equals(upperBounds[0]));
+        }
+        return false;
+    }
+
     static Map<String, GenericsType> extractGenericsParameterMapOfThis(MethodNode mn) {
         if (mn==null) return null;
         Map<String, GenericsType> map = getGenericsParameterMapOfThis(mn.getDeclaringClass());

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/4e3f1703/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 4c6ba4e..604aea4 100644
--- a/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -4238,7 +4238,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             GenericsUtils.extractPlaceholders(receiver, resolvedPlaceholders);
         }
         resolvePlaceholdersFromExplicitTypeHints(method, explicitTypeHints, resolvedPlaceholders);
-        if (resolvedPlaceholders.isEmpty()) return returnType;
+        if (resolvedPlaceholders.isEmpty()) {
+            return boundUnboundedWildcards(returnType);
+        }
         Map<String, GenericsType> placeholdersFromContext = extractGenericsParameterMapOfThis(typeCheckingContext.getEnclosingMethod());
         applyGenericsConnections(placeholdersFromContext,resolvedPlaceholders);
 

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/4e3f1703/src/test/groovy/transform/stc/GenericsSTCTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index f69e55a..6b3ab57 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -494,7 +494,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             }
         }
         new ClassB()
-        ''', 'Cannot call <X> groovy.transform.stc.GenericsSTCTest$ClassA <Long>#bar(java.lang.Class <Long>) with arguments [java.lang.Class <java.lang.Object extends java.lang.Object>]'
+        ''', 'Cannot call <X> groovy.transform.stc.GenericsSTCTest$ClassA <Long>#bar(java.lang.Class <Long>) with arguments [java.lang.Class <? extends java.lang.Object>]'
     }
 
     // GROOVY-5516

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/4e3f1703/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7538Bug.groovy
----------------------------------------------------------------------
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7538Bug.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7538Bug.groovy
index 22feb69..d07caf3 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7538Bug.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7538Bug.groovy
@@ -18,19 +18,16 @@
  */
 package org.codehaus.groovy.classgen.asm.sc.bugs
 
-import groovy.transform.NotYetImplemented
 import groovy.transform.stc.StaticTypeCheckingTestCase
 import org.codehaus.groovy.classgen.asm.sc.StaticCompilationTestSupport
 
 class Groovy7538Bug extends StaticTypeCheckingTestCase implements StaticCompilationTestSupport {
-    @NotYetImplemented
     void testFluentSubTypeToSuperType() {
         assertScript '''import org.codehaus.groovy.classgen.asm.sc.bugs.support.Groovy7538Support
             Groovy7538Support.assertThat("true").isNotEmpty().isNotEqualTo("false")
         '''
     }
 
-    @NotYetImplemented
     void testFluentSuperTypeToSubType() {
         assertScript '''import org.codehaus.groovy.classgen.asm.sc.bugs.support.Groovy7538Support
             Groovy7538Support.assertThat("true").isNotEqualTo("false").isNotEmpty()