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 2022/07/23 01:53:53 UTC
[groovy] 02/02: GROOVY-10699: STC: `<>` ctor call closure/lambda parameter type checking
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 9c7bb5d34c43efd345b5664498ef81c6a6f4df66
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Jul 22 15:40:47 2022 -0500
GROOVY-10699: STC: `<>` ctor call closure/lambda parameter type checking
---
.../transform/stc/StaticTypeCheckingVisitor.java | 52 ++++++++++++----------
.../groovy/transform/stc/GenericsSTCTest.groovy | 50 ++++++++++++++++++++-
2 files changed, 78 insertions(+), 24 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 cd15553533..99fba116aa 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -2300,8 +2300,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
&& parameters.length == argumentTypes.length - 1) {
ctor = typeCheckMapConstructor(call, receiver, arguments);
} else {
- if (asBoolean(receiver.getGenericsTypes())) { // GROOVY-10283, GROOVY-10316, GROOVY-10482, GROOVY-10624, et al.
- Map<GenericsTypeName, GenericsType> context = extractPlaceHoldersVisibleToDeclaration(receiver, ctor, argumentList);
+ if (asBoolean(receiver.getGenericsTypes())) { // GROOVY-10283, GROOVY-10316, GROOVY-10482, GROOVY-10624
+ Map<GenericsTypeName, GenericsType> context = extractPlaceHolders(receiver, ctor.getDeclaringClass());
parameters = Arrays.stream(parameters).map(p -> new Parameter(applyGenericsContext(context, p.getType()), p.getName())).toArray(Parameter[]::new);
}
resolvePlaceholdersFromImplicitTypeHints(argumentTypes, argumentList, parameters);
@@ -2658,8 +2658,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
protected void addTypeCheckingInfoAnnotation(final MethodNode node) {
- // TypeChecked$TypeCheckingInfo can not be applied on constructors
- if (node instanceof ConstructorNode) return;
+ // TypeChecked$TypeCheckingInfo cannot be applied on constructors
+ if (node.isConstructor()) return;
// if a returned inferred type is available and no @TypeCheckingInfo is on node, then add an
// annotation to the method node
@@ -2943,23 +2943,30 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
doInferClosureParameterTypes(receiver, arguments, expression, method, value, conflictResolver, options);
}
} else if (isSAMType(target.getOriginType())) { // SAM-type coercion
- Map<GenericsTypeName, GenericsType> context = extractPlaceHoldersVisibleToDeclaration(receiver, method, arguments);
- GenericsType[] typeParameters = method instanceof ConstructorNode ? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context, method.getGenericsTypes());
+ boolean isConstructor = method.isConstructor();
+ boolean hasTypeArguments = asBoolean(receiver.getGenericsTypes());
+ Map<GenericsTypeName, GenericsType> context = isConstructor && !hasTypeArguments ? new HashMap<>() // GROOVY-10699
+ : extractPlaceHoldersVisibleToDeclaration(receiver, method, arguments);
+ GenericsType[] typeParameters = isConstructor ? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context, method.getGenericsTypes());
if (typeParameters != null) {
boolean typeParametersResolved = false;
// first check for explicit type arguments
- Expression emc = typeCheckingContext.getEnclosingMethodCall();
- if (emc instanceof MethodCallExpression) {
- MethodCallExpression mce = (MethodCallExpression) emc;
- if (mce.getArguments() == arguments) {
- GenericsType[] typeArguments = mce.getGenericsTypes();
- if (typeArguments != null) {
- int n = typeParameters.length;
- if (n == typeArguments.length) {
- typeParametersResolved = true;
- for (int i = 0; i < n; i += 1) {
- context.put(new GenericsTypeName(typeParameters[i].getName()), typeArguments[i]);
+ if (isConstructor) {
+ typeParametersResolved = hasTypeArguments;
+ } else {
+ Expression emc = typeCheckingContext.getEnclosingMethodCall();
+ if (emc instanceof MethodCallExpression) {
+ MethodCallExpression mce = (MethodCallExpression) emc;
+ if (mce.getArguments() == arguments) {
+ GenericsType[] typeArguments = mce.getGenericsTypes();
+ if (typeArguments != null) {
+ int n = typeParameters.length;
+ if (n == typeArguments.length) {
+ typeParametersResolved = true;
+ for (int i = 0; i < n; i += 1) {
+ context.put(new GenericsTypeName(typeParameters[i].getName()), typeArguments[i]);
+ }
}
}
}
@@ -4820,7 +4827,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
MethodNode stubbed;
- if ("<init>".equals(method.getName())) {
+ if (method.isConstructor()) {
stubbed = new ConstructorNode(
method.getModifiers(),
newParams,
@@ -4926,8 +4933,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
- if (!"<init>".equals(name) && !"<clinit>".equals(name)) {
- // lookup in DGM methods too
+ if (!name.endsWith("init>")) { // also search for extension methods
findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), receiver, name, args, methods);
}
methods = filterMethodsByVisibility(methods, typeCheckingContext.getEnclosingClassNode());
@@ -5295,7 +5301,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
* @param explicitTypeHints type arguments (optional), for example {@code Collections.<String>emptyList()}
*/
protected ClassNode inferReturnTypeGenerics(final ClassNode receiver, final MethodNode method, final Expression arguments, final GenericsType[] explicitTypeHints) {
- ClassNode returnType = method instanceof ConstructorNode ? method.getDeclaringClass() : method.getReturnType();
+ ClassNode returnType = method.isConstructor() ? method.getDeclaringClass() : method.getReturnType();
if (!GenericsUtils.hasUnresolvedGenerics(returnType)) {
// GROOVY-7538: replace "Type<?>" with "Type<? extends/super X>" for any "Type<T extends/super X>"
if (getGenericsWithoutArray(returnType) != null) returnType = boundUnboundedWildcards(returnType);
@@ -5309,9 +5315,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
return inferReturnTypeGenerics(receiver, extension, args, explicitTypeHints);
}
- Map<GenericsTypeName, GenericsType> context = method.isStatic() || method instanceof ConstructorNode
+ Map<GenericsTypeName, GenericsType> context = method.isStatic() || method.isConstructor()
? null : extractPlaceHoldersVisibleToDeclaration(receiver, method, arguments);
- GenericsType[] methodGenericTypes = method instanceof ConstructorNode ? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context, method.getGenericsTypes());
+ GenericsType[] methodGenericTypes = method.isConstructor() ? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context, method.getGenericsTypes());
// 1) resolve type parameters of method
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index eaa5a038e1..ee17a5d5f3 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -1154,7 +1154,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
class B<T1 extends Number, T2 extends A<C, ? extends T1>> {
T2 t
B(T2 t) {
- this.t = t
+ this.t = t // Cannot assign value of type A<C,? extends T1> to variable of type T2
}
}
class C {
@@ -1422,6 +1422,54 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-10699
+ void testDiamondInferrenceFromConstructor33() {
+ assertScript '''
+ class A<T> {
+ A(B<T> b_of_t) {
+ }
+ }
+ class B<T> {
+ B(T t) {
+ }
+ }
+
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ def type = node.getNodeMetaData(INFERRED_TYPE)
+ assert type.toString(false) == 'A<java.lang.String>'
+ })
+ def x = new A<>(new B<>('str'))
+ '''
+
+ assertScript '''import java.util.function.Consumer
+ class C<T> {
+ C(Consumer<T> consumer_of_t) {
+ consumer_of_t.accept(null)
+ }
+ }
+
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ def type = node.getNodeMetaData(INFERRED_TYPE)
+ assert type.toString(false) == 'C<java.lang.Object>' // TODO: String
+ })
+ def y = new C<>((String s) -> { print(s); }) // error: Expected type Object for lambda parameter: s
+ '''
+
+ assertScript '''import java.util.function.Supplier
+ class D<T> {
+ D(Supplier<T> supplier_of_t) {
+ T t = supplier_of_t.get()
+ }
+ }
+
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ def type = node.getNodeMetaData(INFERRED_TYPE)
+ assert type.toString(false) == 'D<java.lang.String>'
+ })
+ def z = new D<>(() -> 'str')
+ '''
+ }
+
// GROOVY-10280
void testTypeArgumentPropagation() {
assertScript '''