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/12/14 15:32:23 UTC

[groovy] branch master updated: STC: consistent parameter (aka target) type for functional expression

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 241c694d30 STC: consistent parameter (aka target) type for functional expression
241c694d30 is described below

commit 241c694d3091bde4ab206cf9133281e79acd143b
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Dec 14 09:15:25 2022 -0600

    STC: consistent parameter (aka target) type for functional expression
---
 .../asm/sc/AbstractFunctionalInterfaceWriter.java  | 56 +++++++----------
 .../classgen/asm/sc/StaticInvocationWriter.java    | 13 ++--
 ...icTypesBinaryExpressionMultiTypeDispatcher.java |  7 +--
 .../classgen/asm/sc/StaticTypesLambdaWriter.java   | 29 ++++-----
 ...StaticTypesMethodReferenceExpressionWriter.java | 15 ++---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 13 +++-
 .../groovy/transform/stc/StaticTypesMarker.java    | 63 ++++++++++++-------
 src/test/groovy/transform/stc/LambdaTest.groovy    | 70 ++++++++--------------
 .../transform/stc/MethodReferenceTest.groovy       |  8 +--
 9 files changed, 138 insertions(+), 136 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
index a6a5ba9f24..8e2d6d448e 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
@@ -18,12 +18,9 @@
  */
 package org.codehaus.groovy.classgen.asm.sc;
 
-import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.MethodNode;
 import org.codehaus.groovy.ast.Parameter;
-import org.codehaus.groovy.ast.expr.Expression;
-import org.codehaus.groovy.classgen.asm.BytecodeHelper;
 import org.codehaus.groovy.syntax.RuntimeParserException;
 import org.objectweb.asm.Handle;
 import org.objectweb.asm.Opcodes;
@@ -34,8 +31,10 @@ import java.util.List;
 
 import static org.codehaus.groovy.ast.ClassHelper.getUnwrapper;
 import static org.codehaus.groovy.ast.ClassHelper.getWrapper;
-import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_FUNCTIONAL_INTERFACE_TYPE;
-import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PARAMETER_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.isDynamicTyped;
+import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
+import static org.codehaus.groovy.classgen.asm.BytecodeHelper.getClassInternalName;
+import static org.codehaus.groovy.classgen.asm.BytecodeHelper.getMethodDescriptor;
 
 /**
  * Represents functional interface writer which contains some common methods to complete generating bytecode
@@ -43,21 +42,12 @@ import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PARAMETER_TYPE
  */
 public interface AbstractFunctionalInterfaceWriter {
 
-    default ClassNode getFunctionalInterfaceType(final Expression expression) {
-        ClassNode type = expression.getNodeMetaData(PARAMETER_TYPE);
-        if (type == null) {
-            type = expression.getNodeMetaData(INFERRED_FUNCTIONAL_INTERFACE_TYPE);
-        }
-        return type;
-    }
+    default String createMethodDescriptor(final MethodNode method) {
+        Class<?> returnType = method.getReturnType().getTypeClass();
+        Class<?>[] parameterTypes = Arrays.stream(method.getParameters())
+                .map(p -> p.getType().getTypeClass()).toArray(Class[]::new);
 
-    default String createMethodDescriptor(final MethodNode abstractMethodNode) {
-        return BytecodeHelper.getMethodDescriptor(
-                abstractMethodNode.getReturnType().getTypeClass(),
-                Arrays.stream(abstractMethodNode.getParameters())
-                        .map(e -> e.getType().getTypeClass())
-                        .toArray(Class[]::new)
-        );
+        return getMethodDescriptor(returnType, parameterTypes);
     }
 
     default Handle createBootstrapMethod(final boolean isInterface, final boolean serializable) {
@@ -87,13 +77,13 @@ public interface AbstractFunctionalInterfaceWriter {
 
         arguments[1] = new Handle(
                 insn, // H_INVOKESTATIC or H_INVOKEVIRTUAL or H_INVOKEINTERFACE (GROOVY-9853)
-                BytecodeHelper.getClassInternalName(methodOwner.getName()),
+                getClassInternalName(methodOwner.getName()),
                 methodNode.getName(),
-                BytecodeHelper.getMethodDescriptor(methodNode),
+                getMethodDescriptor(methodNode),
                 methodOwner.isInterface());
 
         arguments[2] = Type.getType(
-                BytecodeHelper.getMethodDescriptor(methodNode.getReturnType(), parameters));
+                getMethodDescriptor(methodNode.getReturnType(), parameters));
 
         return arguments;
     }
@@ -104,10 +94,10 @@ public interface AbstractFunctionalInterfaceWriter {
         }
 
         ClassNode type;
-        boolean isParameterTypePrimitive = ClassHelper.isPrimitiveType(parameterType);
-        boolean isInferredTypePrimitive = ClassHelper.isPrimitiveType(inferredType);
+        boolean isParameterTypePrimitive = isPrimitiveType(parameterType);
+        boolean isInferredTypePrimitive = isPrimitiveType(inferredType);
         if (!isParameterTypePrimitive && isInferredTypePrimitive) {
-            if (ClassHelper.isDynamicTyped(parameterType) && ClassHelper.isPrimitiveType(targetType) // (1)
+            if (isDynamicTyped(parameterType) && isPrimitiveType(targetType) // (1)
                     || !parameterType.equals(getUnwrapper(parameterType)) && !inferredType.equals(getWrapper(inferredType)) // (2)
             ) {
                 // GROOVY-9790: bootstrap method initialization exception raised when lambda parameter type is wrong
@@ -134,20 +124,20 @@ public interface AbstractFunctionalInterfaceWriter {
      */
     @Deprecated
     default ClassNode convertParameterType(final ClassNode parameterType, final ClassNode inferredType) {
-        if (!ClassHelper.getWrapper(inferredType.redirect()).isDerivedFrom(ClassHelper.getWrapper(parameterType.redirect()))) {
+        if (!getWrapper(inferredType.redirect()).isDerivedFrom(getWrapper(parameterType.redirect()))) {
             throw new RuntimeParserException("The inferred type[" + inferredType.redirect() + "] is not compatible with the parameter type[" + parameterType.redirect() + "]", parameterType);
         } else {
-            boolean isParameterTypePrimitive = ClassHelper.isPrimitiveType(parameterType);
-            boolean isInferredTypePrimitive = ClassHelper.isPrimitiveType(inferredType);
+            boolean isParameterTypePrimitive = isPrimitiveType(parameterType);
+            boolean isInferredTypePrimitive = isPrimitiveType(inferredType);
             ClassNode type;
             if (!isParameterTypePrimitive && isInferredTypePrimitive) {
-                if (parameterType != ClassHelper.getUnwrapper(parameterType) && inferredType != ClassHelper.getWrapper(inferredType)) {
+                if (parameterType != getUnwrapper(parameterType) && inferredType != getWrapper(inferredType)) {
                     type = inferredType;
                 } else {
-                    type = ClassHelper.getWrapper(inferredType);
+                    type = getWrapper(inferredType);
                 }
             } else if (isParameterTypePrimitive && !isInferredTypePrimitive) {
-                type = ClassHelper.getUnwrapper(inferredType);
+                type = getUnwrapper(inferredType);
             } else {
                 type = inferredType;
             }
@@ -155,11 +145,11 @@ public interface AbstractFunctionalInterfaceWriter {
         }
     }
 
-    default Parameter prependParameter(final List<Parameter> methodParameterList, final String parameterName, final ClassNode parameterType) {
+    default Parameter prependParameter(final List<Parameter> parameterList, final String parameterName, final ClassNode parameterType) {
         Parameter parameter = new Parameter(parameterType, parameterName);
         parameter.setClosureSharedVariable(false);
         parameter.setOriginType(parameterType);
-        methodParameterList.add(0, parameter);
+        parameterList.add(0, parameter);
         return parameter;
     }
 }
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
index cd02b265c6..545b23180e 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
@@ -35,7 +35,9 @@ import org.codehaus.groovy.ast.expr.ConstantExpression;
 import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
 import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.expr.ExpressionTransformer;
+import org.codehaus.groovy.ast.expr.LambdaExpression;
 import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.MethodReferenceExpression;
 import org.codehaus.groovy.ast.expr.PropertyExpression;
 import org.codehaus.groovy.ast.expr.SpreadExpression;
 import org.codehaus.groovy.ast.expr.TupleExpression;
@@ -515,10 +517,13 @@ public class StaticInvocationWriter extends InvocationWriter {
         return ClassHelper.getWrapper(argumentType).isDerivedFrom(ClassHelper.getWrapper(parameterType));
     }
 
-    private void visitArgument(final Expression argumentExpr, final ClassNode parameterType) {
-        argumentExpr.putNodeMetaData(StaticTypesMarker.PARAMETER_TYPE, parameterType);
-        argumentExpr.visit(controller.getAcg());
-        if (!isNullConstant(argumentExpr)) {
+    private void visitArgument(final Expression argument, final ClassNode parameterType) {
+        if (argument instanceof LambdaExpression || argument instanceof MethodReferenceExpression) {
+            // ensure target type available to the argument expression's bytecode generator
+            argument.getNodeMetaData(StaticTypesMarker.PARAMETER_TYPE, x -> parameterType);
+        }
+        argument.visit(controller.getAcg());
+        if (!isNullConstant(argument)) {
             controller.getOperandStack().doGroovyCast(parameterType);
         }
     }
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
index fd002236da..f742067051 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
@@ -78,8 +78,8 @@ import static org.codehaus.groovy.transform.sc.StaticCompilationVisitor.ARRAYLIS
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isAssignment;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.inferLoopElementType;
 import static org.codehaus.groovy.transform.stc.StaticTypesMarker.DIRECT_METHOD_CALL_TARGET;
-import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_FUNCTIONAL_INTERFACE_TYPE;
 import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PARAMETER_TYPE;
 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
 import static org.objectweb.asm.Opcodes.DADD;
 import static org.objectweb.asm.Opcodes.DCONST_1;
@@ -201,9 +201,8 @@ public class StaticTypesBinaryExpressionMultiTypeDispatcher extends BinaryExpres
             }
         } else {
             Expression rightExpression = expression.getRightExpression();
-            if (rightExpression instanceof LambdaExpression || rightExpression instanceof MethodReferenceExpression) {
-                rightExpression.putNodeMetaData(INFERRED_FUNCTIONAL_INTERFACE_TYPE, leftExpression.getNodeMetaData(INFERRED_TYPE));
-            }
+            if (rightExpression instanceof LambdaExpression || rightExpression instanceof MethodReferenceExpression)
+                rightExpression.getNodeMetaData(PARAMETER_TYPE, x -> leftExpression.getNodeMetaData(INFERRED_TYPE));
         }
         // GROOVY-5620: spread-safe operator on LHS is not supported
         if (leftExpression instanceof PropertyExpression
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
index 9b9fb1d638..5d5d7f0e4c 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
@@ -19,6 +19,7 @@
 package org.codehaus.groovy.classgen.asm.sc;
 
 import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.CodeVisitorSupport;
 import org.codehaus.groovy.ast.ConstructorNode;
@@ -41,7 +42,6 @@ import org.codehaus.groovy.classgen.asm.OperandStack;
 import org.codehaus.groovy.classgen.asm.WriterController;
 import org.codehaus.groovy.classgen.asm.WriterControllerFactory;
 import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
-import org.codehaus.groovy.transform.stc.StaticTypesMarker;
 import org.objectweb.asm.MethodVisitor;
 
 import java.util.HashMap;
@@ -56,7 +56,6 @@ import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.SERIALIZABLE_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.SERIALIZEDLAMBDA_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.findSAM;
 import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
 import static org.codehaus.groovy.ast.tools.ClosureUtils.getParametersSafe;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
@@ -65,6 +64,8 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PARAMETER_TYPE;
 import static org.objectweb.asm.Opcodes.ACC_FINAL;
 import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
@@ -86,8 +87,8 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
     private static final String IS_GENERATED_CONSTRUCTOR = "__IS_GENERATED_CONSTRUCTOR";
     private static final String LAMBDA_SHARED_VARIABLES = "__LAMBDA_SHARED_VARIABLES";
 
-    private final StaticTypesClosureWriter staticTypesClosureWriter;
     private final Map<Expression, ClassNode> lambdaClassNodes = new HashMap<>();
+    private final StaticTypesClosureWriter staticTypesClosureWriter;
 
     public StaticTypesLambdaWriter(final WriterController controller) {
         super(controller);
@@ -96,19 +97,16 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
 
     @Override
     public void writeLambda(final LambdaExpression expression) {
-        ClassNode functionalInterface = getFunctionalInterfaceType(expression);
-        if (functionalInterface == null || !functionalInterface.isInterface()) {
+        // functional interface target is required for native lambda generation
+        ClassNode  functionalType = expression.getNodeMetaData(PARAMETER_TYPE);
+        MethodNode abstractMethod = ClassHelper.findSAM(functionalType);
+        if (abstractMethod == null || !functionalType.isInterface()) {
+            // generate bytecode for closure
             super.writeLambda(expression);
             return;
         }
 
-        MethodNode abstractMethod = findSAM(functionalInterface.redirect());
-        if (abstractMethod == null) {
-            super.writeLambda(expression);
-            return;
-        }
-
-        if (!expression.isSerializable() && functionalInterface.implementsInterface(SERIALIZABLE_TYPE)) {
+        if (!expression.isSerializable() && functionalType.implementsInterface(SERIALIZABLE_TYPE)) {
             expression.setSerializable(true);
         }
 
@@ -130,7 +128,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
         MethodVisitor mv = controller.getMethodVisitor();
         mv.visitInvokeDynamicInsn(
                 abstractMethod.getName(),
-                createAbstractMethodDesc(functionalInterface.redirect(), lambdaClass),
+                createAbstractMethodDesc(functionalType.redirect(), lambdaClass),
                 createBootstrapMethod(enclosingClass.isInterface(), expression.isSerializable()),
                 createBootstrapMethodArguments(createMethodDescriptor(abstractMethod), H_INVOKEVIRTUAL, lambdaClass, lambdaMethod, lambdaMethod.getParameters(), expression.isSerializable())
         );
@@ -138,8 +136,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
             mv.visitTypeInsn(CHECKCAST, "java/io/Serializable");
         }
 
-        OperandStack operandStack = controller.getOperandStack();
-        operandStack.replace(functionalInterface.redirect(), 1);
+        controller.getOperandStack().replace(functionalType.redirect(), 1);
     }
 
     private static Parameter[] createDeserializeLambdaMethodParams() {
@@ -295,7 +292,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
         for (int i = 0, n = parameters.length; i < n; i += 1) {
             Parameter targetParameter = targetParameters[i];
             Parameter parameter = parameters[i];
-            ClassNode inferredType = parameter.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
+            ClassNode inferredType = parameter.getNodeMetaData(INFERRED_TYPE);
             if (inferredType != null) {
                 ClassNode type = convertParameterType(targetParameter.getType(), parameter.getType(), inferredType);
                 parameter.setOriginType(type);
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
index c1804e74c1..d318fbcc57 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
@@ -74,9 +74,10 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
 
     @Override
     public void writeMethodReferenceExpression(final MethodReferenceExpression methodReferenceExpression) {
-        ClassNode  functionalInterfaceType = getFunctionalInterfaceType(methodReferenceExpression);
-        MethodNode abstractMethod = ClassHelper.findSAM(functionalInterfaceType);
-        if (abstractMethod == null || !functionalInterfaceType.isInterface()) {
+        // functional interface target is required for native method reference generation
+        ClassNode  functionalType = methodReferenceExpression.getNodeMetaData(StaticTypesMarker.PARAMETER_TYPE);
+        MethodNode abstractMethod = ClassHelper.findSAM(functionalType);
+        if (abstractMethod == null || !functionalType.isInterface()) {
             // generate the default bytecode -- most likely a method closure
             super.writeMethodReferenceExpression(methodReferenceExpression);
             return;
@@ -104,7 +105,7 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
         }
 
         validate(methodReferenceExpression, typeOrTargetRefType, methodRefName, methodRefMethod, parametersWithExactType,
-                resolveClassNodeGenerics(extractPlaceholders(functionalInterfaceType), null, abstractMethod.getReturnType()));
+                resolveClassNodeGenerics(extractPlaceholders(functionalType), null, abstractMethod.getReturnType()));
 
         if (isExtensionMethod(methodRefMethod)) {
             ExtensionMethodNode extensionMethodNode = (ExtensionMethodNode) methodRefMethod;
@@ -162,7 +163,7 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
         }
 
         String methodName = abstractMethod.getName();
-        String methodDesc = BytecodeHelper.getMethodDescriptor(functionalInterfaceType.redirect(),
+        String methodDesc = BytecodeHelper.getMethodDescriptor(functionalType.redirect(),
                 isClassExpression ? Parameter.EMPTY_ARRAY : new Parameter[]{new Parameter(typeOrTargetRefType, "__METHODREF_EXPR_INSTANCE")});
 
         Handle bootstrapMethod = createBootstrapMethod(classNode.isInterface(), false);
@@ -177,9 +178,9 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
         controller.getMethodVisitor().visitInvokeDynamicInsn(methodName, methodDesc, bootstrapMethod, bootstrapArgs);
 
         if (isClassExpression) {
-            controller.getOperandStack().push(functionalInterfaceType);
+            controller.getOperandStack().push(functionalType);
         } else {
-            controller.getOperandStack().replace(functionalInterfaceType, 1);
+            controller.getOperandStack().replace(functionalType, 1);
         }
     }
 
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 2e38ae20bd..c833106456 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -320,6 +320,7 @@ import static org.codehaus.groovy.transform.stc.StaticTypesMarker.DYNAMIC_RESOLU
 import static org.codehaus.groovy.transform.stc.StaticTypesMarker.IMPLICIT_RECEIVER;
 import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_RETURN_TYPE;
 import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PARAMETER_TYPE;
 import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PV_FIELDS_ACCESS;
 import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PV_FIELDS_MUTATION;
 import static org.codehaus.groovy.transform.stc.StaticTypesMarker.PV_METHODS_ACCESS;
@@ -984,6 +985,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 LambdaExpression lambda = constructLambdaExpressionForMethodReference(target, (MethodReferenceExpression) source);
 
                 inferParameterAndReturnTypesOfClosureOnRHS(target, lambda);
+                source.putNodeMetaData(PARAMETER_TYPE, lambda.getNodeMetaData(PARAMETER_TYPE));
                 source.putNodeMetaData(CLOSURE_ARGUMENTS, Arrays.stream(lambda.getParameters()).map(Parameter::getType).toArray(ClassNode[]::new));
             }
         }
@@ -1016,6 +1018,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             addStaticTypeError("Wrong number of parameters for method target " + toMethodParametersString(findSAM(lhsType).getName(), samParameterTypes), rhsExpression);
         }
 
+        rhsExpression.putNodeMetaData(PARAMETER_TYPE, lhsType);
         storeInferredReturnType(rhsExpression, typeInfo.getV2());
     }
 
@@ -2816,9 +2819,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         for (int i = 0; i < nExpressions; i += 1) {
             Expression expression = expressions.get(i);
             if (visitClosures == (expression instanceof ClosureExpression || expression instanceof MethodPointerExpression)) {
+                ClassNode targetType = null;
                 if (visitClosures && nthParameter != -1) { // GROOVY-10636: vargs call
                     Parameter target = parameters[Math.min(i, nthParameter)];
-                    ClassNode targetType = target.getType();
+                    targetType = target.getType();
                     if (targetType.isArray() && i >= nthParameter)
                         targetType = targetType.getComponentType();
 
@@ -2846,6 +2850,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 }
                 expression.visit(this);
                 expression.removeNodeMetaData(DELEGATION_METADATA);
+
+                MethodNode sam = findSAM(targetType);
+                if (sam != null) {
+                    Map<GenericsTypeName, GenericsType> context = extractPlaceHoldersVisibleToDeclaration(receiver, selectedMethod, arguments);
+                    targetType = applyGenericsContext(context, targetType);
+                    expression.putNodeMetaData(PARAMETER_TYPE, targetType);
+                }
             }
             if (i == 0 && parameters.length > 0 && expression instanceof MapExpression) {
                 checkNamedParamsAnnotation(parameters[0], (MapExpression) expression);
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java
index 56077e007b..fa66212a7f 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java
@@ -19,28 +19,47 @@
 package org.codehaus.groovy.transform.stc;
 
 /**
- * This enumeration is used by the AST transformations which rely on static type checking, either
- * to store or to retrieve information from AST node metadata. The values of this enumeration are
- * used as metadata keys.
+ * This enumeration is used by the AST transformations which rely on static type
+ * checking, either to store or to retrieve information from AST node metadata.
+ * The values of this enumeration are used as metadata keys.
  */
 public enum StaticTypesMarker {
-    INFERRED_TYPE, // used to store type information on class nodes
-    DECLARATION_INFERRED_TYPE, // in flow analysis, represents the type of the declaration node lhs
-    INFERRED_RETURN_TYPE, // used to store inferred return type for methods and closures
-    CLOSURE_ARGUMENTS, // used to store closure argument types on a variable expression
-    READONLY_PROPERTY, // used to tell that a property expression refers to a readonly property
-    INITIAL_EXPRESSION, // used to store the default expression for a parameter
-    DIRECT_METHOD_CALL_TARGET, // used to store the MethodNode a MethodCallExpression should target
-    DELEGATION_METADATA, // used to store the delegation strategy and delegate type of a closure when declared with @DelegatesTo
-    IMPLICIT_RECEIVER, // if the receiver is implicit but not "this", store the name of the receiver (delegate or owner)
-    PV_FIELDS_ACCESS, // set of private fields that are accessed from closures or inner classes
-    PV_FIELDS_MUTATION, // set of private fields that are set from closures or inner classes
-    PV_METHODS_ACCESS, // set of private methods that are accessed from closures or inner classes
-    DYNAMIC_RESOLUTION, // call recognized by a type checking extension as a dynamic method call
-    SUPER_MOP_METHOD_REQUIRED, // used to store the list of MOP methods that still have to be generated
-    PARAMETER_TYPE, // used to store the parameter type information of method invocation on an expression
-    INFERRED_FUNCTIONAL_INTERFACE_TYPE, // used to store the function interface type information on an expression
-    CONSTRUCTED_LAMBDA_EXPRESSION, // used to store the constructed lambda expression for method reference and constructor reference
-    SWITCH_CONDITION_EXPRESSION_TYPE, // used to store the condition expression type of the switch-case statement
-    TYPE // used to store the result of `org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.getType`
+    /** used to store type information on class nodes */
+    INFERRED_TYPE,
+    /** in flow analysis, represents the type of the declaration node LHS */
+    DECLARATION_INFERRED_TYPE,
+    /** used to store inferred return type for methods and closures */
+    INFERRED_RETURN_TYPE,
+    /** used to store closure argument types on a variable expression */
+    CLOSURE_ARGUMENTS,
+    /** used to tell that a property expression refers to a readonly property */
+    READONLY_PROPERTY,
+    /** used to store the default expression for a parameter */
+    INITIAL_EXPRESSION,
+    /** used to store the MethodNode a MethodCallExpression should target */
+    DIRECT_METHOD_CALL_TARGET,
+    /** used to store the delegation strategy and delegate type of a closure derived from {@link groovy.lang.DelegatesTo DelegatesTo} metadata */
+    DELEGATION_METADATA,
+    /** if the receiver is implicit but not "this", store the name of the receiver (delegate or owner) */
+    IMPLICIT_RECEIVER,
+    /** set of private fields that are accessed from closures or inner classes */
+    PV_FIELDS_ACCESS,
+    /** set of private fields that are set from closures or inner classes */
+    PV_FIELDS_MUTATION,
+    /** set of private methods that are accessed from closures or inner classes */
+    PV_METHODS_ACCESS,
+    /** call recognized by a type checking extension as a dynamic method call */
+    DYNAMIC_RESOLUTION,
+    /** used to store the list of MOP methods that still have to be generated */
+    SUPER_MOP_METHOD_REQUIRED,
+    /** used to store the parameter type information of method invocation on an expression */
+    PARAMETER_TYPE,
+    /** used to store the function interface type information on an expression */
+    INFERRED_FUNCTIONAL_INTERFACE_TYPE,
+    /** used to store the constructed lambda expression for method reference and constructor reference */
+    CONSTRUCTED_LAMBDA_EXPRESSION,
+    /** used to store the condition expression type of the switch-case statement */
+    SWITCH_CONDITION_EXPRESSION_TYPE,
+    /** used to store the result of {@link StaticTypeCheckingVisitor#getType} */
+    TYPE
 }
diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy
index c117fbac24..3380975445 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -717,60 +717,48 @@ final class LambdaTest {
     @Test
     void testFunctionalInterface1() {
         assertScript shell, '''
-            class Test1 {
-                static main(args) {
-                    p()
-                }
-
-                static void p() {
-                    SamCallable c = (int e) -> e
-                    assert 1 == c(1)
-                }
+            interface SamCallable {
+                int call(int i)
             }
 
-            interface SamCallable {
-                int call(int p)
+            void p() {
+                SamCallable c = (int x) -> x
+                assert c(1) == 1
             }
+
+            p()
         '''
     }
 
     @Test
     void testFunctionalInterface2() {
         assertScript shell, '''
-            class Test1 {
-                static main(args) {
-                    p()
-                }
-
-                static void p() {
-                    SamCallable c = e -> e
-                    assert 1 == c(1)
-                }
+            interface SamCallable {
+                int call(int i)
             }
 
-            interface SamCallable {
-                int call(int p)
+            void p() {
+                SamCallable c = x -> x
+                assert c(1) == 1
             }
+
+            p()
         '''
     }
 
     @Test
     void testFunctionalInterface3() {
         assertScript shell, '''
-            class Test1 {
-                static main(args) {
-                    p()
-                }
-
-                static void p() {
-                    SamCallable c = (int e) -> e // This is actually a closure(not a native lambda), because "Functional interface SamCallable is not an interface"
-                    assert 1 == c(1)
-                }
+            abstract class SamCallable {
+                abstract int call(int i)
             }
 
-            abstract class SamCallable {
-                abstract int call(int p)
+            void p() {
+                SamCallable c = (int x) -> x // this is a closure, not a native lambda
+                assert c(1) == 1
             }
+
+            p()
         '''
     }
 
@@ -1179,11 +1167,7 @@ final class LambdaTest {
             class C implements Serializable {
                 private static final long serialVersionUID = -1L
                 String s = 'a'
-                SerializableFunction<Integer, String> f
-
-                {
-                    f = (Integer i) -> s + i
-                }
+                transient SerializableFunction<Integer, String> f = (Integer i) -> s + i
 
                 byte[] test() {
                     def out = new ByteArrayOutputStream()
@@ -1213,11 +1197,7 @@ final class LambdaTest {
 
             class C {
                 String s = 'a'
-                SerializableFunction<Integer, String> f
-
-                {
-                    f = (Integer i) -> s + i
-                }
+                SerializableFunction<Integer, String> f = (Integer i) -> s + i
 
                 byte[] test() {
                     def out = new ByteArrayOutputStream()
@@ -1357,7 +1337,7 @@ final class LambdaTest {
             package tests.lambda
 
             class C implements Serializable {
-                private static final long serialVersionUID = -1L;
+                private static final long serialVersionUID = -1L
                 private String s = 'a'
 
                 byte[] test() {
@@ -1420,7 +1400,7 @@ final class LambdaTest {
             package tests.lambda
 
             class C implements Serializable {
-                private static final long serialVersionUID = -1L;
+                private static final long serialVersionUID = -1L
                 private String getS() { 'a' }
 
                 byte[] test() {
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index 79dcbf2bcd..29cc338a05 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -741,7 +741,7 @@ final class MethodReferenceTest {
         assertScript shell, '''
             @CompileStatic
             void test() {
-                def f = Math::abs // No explicit type defined, so it is actually a method closure. We can make it smarter in a later version.
+                def f = Math::abs // No explicit type defined, so it is actually a method closure
                 def result = [1, -2, 3].stream().map(f).collect(Collectors.toList())
                 assert [1, 2, 3] == result
             }
@@ -845,7 +845,7 @@ final class MethodReferenceTest {
             @CompileStatic
             void test() {
                 def result = [{}, {}, {}].stream().map(Thread::startDaemon).collect(Collectors.toList())
-                assert result.every(e -> e instanceof Thread)
+                assert result.every { it instanceof Thread }
             }
 
             test()
@@ -871,10 +871,10 @@ final class MethodReferenceTest {
             @CompileStatic
             void test() {
                 def result = [{}, {}, {}].stream().map(Thread::startDaemon).collect(Collectors.toList())
-                assert result.every(e -> e instanceof Thread)
+                assert result.every { it instanceof Thread }
 
                 result = [{}, {}, {}].stream().map(Thread::startDaemon).collect(Collectors.toList())
-                assert result.every(e -> e instanceof Thread)
+                assert result.every { it instanceof Thread }
             }
 
             test()