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()