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/04/25 09:19:22 UTC
[groovy] branch master updated: GROOVY-10053: apply object
expression generics to ref method 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 2415668 GROOVY-10053: apply object expression generics to ref method return type
2415668 is described below
commit 24156683dde9064bfa644c0ef4976b401504eb13
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Apr 25 04:16:28 2021 -0500
GROOVY-10053: apply object expression generics to ref method return type
---
.../transform/stc/StaticTypeCheckingVisitor.java | 46 ++++++++++++----------
.../groovy/transform/stc/GenericsSTCTest.groovy | 19 ++++++++-
.../asm/sc/GenericsStaticCompileTest.groovy | 18 +++++++++
3 files changed, 61 insertions(+), 22 deletions(-)
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 ece5068..b265462 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -2453,9 +2453,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
List<Receiver<String>> receivers = new ArrayList<>();
addReceivers(receivers, makeOwnerList(expression.getExpression()), false);
+ ClassNode receiverType = null;
List<MethodNode> candidates = EMPTY_METHODNODE_LIST;
for (Receiver<String> currentReceiver : receivers) {
- ClassNode receiverType = wrapTypeIfNecessary(currentReceiver.getType());
+ receiverType = wrapTypeIfNecessary(currentReceiver.getType());
candidates = findMethodsWithGenerated(receiverType, nameText);
if (isBeingCompiled(receiverType)) candidates.addAll(GROOVY_OBJECT_TYPE.getMethods(nameText));
@@ -2475,10 +2476,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
if (!candidates.isEmpty()) {
- candidates.stream().map(MethodNode::getReturnType)
- .reduce(WideningCategories::lowestUpperBound)
- .filter(returnType -> !returnType.equals(OBJECT_TYPE))
- .ifPresent(returnType -> storeType(expression, wrapClosureType(returnType)));
+ Map<GenericsTypeName, GenericsType> gts = GenericsUtils.extractPlaceholders(receiverType);
+ candidates.stream().map(candidate -> applyGenericsContext(gts, candidate.getReturnType()))
+ .reduce(WideningCategories::lowestUpperBound).ifPresent(returnType -> {
+ storeType(expression, wrapClosureType(returnType));
+ });
expression.putNodeMetaData(MethodNode.class, candidates);
} else if (!(expression instanceof MethodReferenceExpression)) {
ClassNode type = wrapTypeIfNecessary(getType(expression.getExpression()));
@@ -3688,12 +3690,12 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
- visitMethodCallArguments(receiver, new ArgumentListExpression(newArgumentExpressionList), true, selectedMethod);
+ visitMethodCallArguments(receiver, args(newArgumentExpressionList), true, selectedMethod);
- for (Integer methodReferenceParamIndex : methodReferenceParamIndexList) {
- LambdaExpression lambdaExpression = (LambdaExpression) newArgumentExpressionList.get(methodReferenceParamIndex);
- ClassNode[] argumentTypes = lambdaExpression.getNodeMetaData(CLOSURE_ARGUMENTS);
- argumentExpressions.get(methodReferenceParamIndex).putNodeMetaData(CLOSURE_ARGUMENTS, argumentTypes);
+ for (int index : methodReferenceParamIndexList) {
+ Expression lambdaExpression = newArgumentExpressionList.get(index);
+ Expression methodReferenceExpression = argumentExpressions.get(index);
+ methodReferenceExpression.putNodeMetaData(CLOSURE_ARGUMENTS, lambdaExpression.getNodeMetaData(CLOSURE_ARGUMENTS));
}
}
@@ -5442,15 +5444,17 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
ClassNode[] paramTypes = samSignature.get();
return options.stream().filter((MethodNode option) -> {
ClassNode[] types = collateMethodReferenceParameterTypes(source, option);
- if (types.length == paramTypes.length) {
- for (int i = 0, n = types.length; i < n; i += 1) {
- if (!types[i].isGenericsPlaceHolder() && !isAssignableTo(types[i], paramTypes[i])) {
- return false;
- }
+ final int n = types.length;
+ if (n != paramTypes.length) {
+ return false;
+ }
+ for (int i = 0; i < n; i += 1) {
+ // param type represents incoming argument type
+ if (!isAssignableTo(paramTypes[i], types[i])) {
+ return false;
}
- return true;
}
- return false;
+ return true;
}).findFirst().orElse(null); // TODO: order matches by param distance
}
@@ -5477,9 +5481,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
* Converts a closure type to the appropriate SAM type, which is used to
* infer return type generics.
*
- * @param closureType the inferred type of a closure {@code Closure<Type>}
- * @param samType the type into which the closure is coerced into
- * @return SAM type augmented using information from the argument expression
+ * @param expression closure, lambda, pointer or reference
+ * @param closureType the inferred type of {@code expression}
+ * @return {@code samType} augmented by argument expression information
*/
private static ClassNode convertClosureTypeToSAMType(final Expression expression, final ClassNode closureType, final MethodNode sam, final ClassNode samType, final Map<GenericsTypeName, GenericsType> placeholders) {
// use the generics information from Closure to further specify the type
@@ -5588,7 +5592,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
return declaringClass;
}
- private Map<GenericsTypeName, GenericsType> resolvePlaceHoldersFromDeclaration(final ClassNode receiver, final ClassNode declaration, final MethodNode method, final boolean isStaticTarget) {
+ private static Map<GenericsTypeName, GenericsType> resolvePlaceHoldersFromDeclaration(final ClassNode receiver, final ClassNode declaration, final MethodNode method, final boolean isStaticTarget) {
Map<GenericsTypeName, GenericsType> resolvedPlaceholders;
if (isStaticTarget && CLASS_Type.equals(receiver) &&
receiver.isUsingGenerics() &&
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 885c00b..944b1f8 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -236,7 +236,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- // GROOVY-10049
+ // GROOVY-10049, GROOVY-10053
void testReturnTypeInferenceWithMethodGenerics9() {
assertScript '''
def <X> Set<X> f(Class<X> x) {
@@ -250,6 +250,23 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
assert result == [ 42 ]
'''
+ ['n -> (N) n', '{ n -> (N) n }'].each { cast ->
+ assertScript """
+ Set<Number> f() {
+ Collections.<Number>singleton(42)
+ }
+ def <N extends Number> Set<N> g(Class<N> t) {
+ Set<N> result = new HashSet<>()
+ f().stream().filter(n -> t.isInstance(n))
+ .<N>map($cast).forEach(n -> result.add(n))
+ return result
+ }
+
+ def result = g(Integer)
+ assert result == [42] as Set
+ """
+ }
+
assertScript '''
def <T> String test(Iterable<T> iterable) {
Iterator<T> it = iterable.iterator()
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/GenericsStaticCompileTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/GenericsStaticCompileTest.groovy
index 4a599b2..d8c10e6 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/GenericsStaticCompileTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/GenericsStaticCompileTest.groovy
@@ -24,4 +24,22 @@ import groovy.transform.stc.GenericsSTCTest
* Unit tests for static compilation : generics.
*/
class GenericsStaticCompileTest extends GenericsSTCTest implements StaticCompilationTestSupport {
+
+ // GROOVY-10053
+ void testReturnTypeInferenceWithMethodGenericsSC() {
+ assertScript """
+ Set<Number> f() {
+ Collections.<Number>singleton(42)
+ }
+ def <N extends Number> Set<N> g(Class<N> t) {
+ Set<N> result = new HashSet<>()
+ f().stream().filter(n -> t.isInstance(n))
+ .<N>map(t::cast).forEach(n -> result.add(n))
+ return result
+ }
+
+ def result = g(Integer)
+ assert result == [42] as Set
+ """
+ }
}