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'
'''