GROOVY-11013, GROOVY-11072: apply type variable of SAM-type to `List<T>`
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
index 7f2dfe5..610516a 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -59,6 +59,7 @@
 import static org.codehaus.groovy.runtime.DefaultGroovyMethods.plus;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isUnboundedWildcard;
+import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.resolveClassNodeGenerics;
 
 /**
  * Utility methods to deal with parameterized types.
@@ -909,6 +910,7 @@
      *
      * @since 3.0.0
      */
+    @Deprecated(forRemoval = true, since = "5.0.0")
     public static Map<GenericsType, GenericsType> makeDeclaringAndActualGenericsTypeMap(final ClassNode declaringClass, final ClassNode actualReceiver) {
         return correlateTypeParametersAndTypeArguments(declaringClass, actualReceiver, false);
     }
@@ -924,10 +926,12 @@
      *
      * @since 3.0.0
      */
+    @Deprecated(forRemoval = true, since = "5.0.0")
     public static Map<GenericsType, GenericsType> makeDeclaringAndActualGenericsTypeMapOfExactType(final ClassNode declaringClass, final ClassNode actualReceiver) {
         return correlateTypeParametersAndTypeArguments(declaringClass, actualReceiver, true);
     }
 
+    @Deprecated(forRemoval = true, since = "5.0.0")
     private static Map<GenericsType, GenericsType> correlateTypeParametersAndTypeArguments(final ClassNode declaringClass, final ClassNode actualReceiver, final boolean tryToFindExactType) {
         ClassNode parameterizedType = findParameterizedTypeFromCache(declaringClass, actualReceiver, tryToFindExactType);
         if (parameterizedType != null && parameterizedType.isRedirectNode() && !parameterizedType.isGenericsPlaceHolder()) { // GROOVY-10166
@@ -1049,14 +1053,20 @@
      */
     public static Tuple2<ClassNode[], ClassNode> parameterizeSAM(final ClassNode samType) {
         MethodNode abstractMethod = ClassHelper.findSAM(samType);
+        ClassNode  declaringClass = abstractMethod.getDeclaringClass();
+        Map<GenericsType.GenericsTypeName, GenericsType> spec = extractPlaceholders(
+            samType.equals(declaringClass) ? samType : parameterizeType(samType, declaringClass));
 
-        Map<GenericsType, GenericsType> generics = makeDeclaringAndActualGenericsTypeMapOfExactType(abstractMethod.getDeclaringClass(), samType);
-        Function<ClassNode , ClassNode> resolver = t -> {
-            return t.isGenericsPlaceHolder() ? findActualTypeByGenericsPlaceholderName(t.getUnresolvedName(), generics) : t;
-        };
+        if (spec.isEmpty() && declaringClass.getGenericsTypes() != null) {
+            for (GenericsType tp : declaringClass.getGenericsTypes()) // apply erasure
+                spec.put(new GenericsType.GenericsTypeName(tp.getName()), erasure(tp));
+        } else {
+            // resolveClassNodeGenerics converts "T=? super Type" to Object, so convert "T=? super Type" to "T=Type"
+            spec.replaceAll((name, type) -> type.isWildcard() && type.getLowerBound() != null ? type.getLowerBound().asGenericsType() : type);
+        }
 
-        ClassNode[] parameterTypes = Arrays.stream(abstractMethod.getParameters()).map(Parameter::getType).map(resolver).toArray(ClassNode[]::new);
-        ClassNode returnType = resolver.apply(abstractMethod.getReturnType());
+        ClassNode[] parameterTypes = Arrays.stream(abstractMethod.getParameters()).map(p -> resolveClassNodeGenerics(spec, null, p.getType())).toArray(ClassNode[]::new);
+        ClassNode returnType = resolveClassNodeGenerics(spec, null, abstractMethod.getReturnType());
         return new Tuple2<>(parameterTypes, returnType);
     }
 
@@ -1068,6 +1078,7 @@
      *
      * @since 3.0.0
      */
+    @Deprecated(forRemoval = true, since = "5.0.0")
     public static ClassNode findActualTypeByGenericsPlaceholderName(final String placeholderName, final Map<GenericsType, GenericsType> genericsPlaceholderAndTypeMap) {
         Function<GenericsType, ClassNode> resolver = gt -> {
             if (gt.isWildcard()) {
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 d34c64f..df110a1 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
@@ -89,63 +89,39 @@
         return arguments;
     }
 
+    default ClassNode convertParameterType(final ClassNode parameterType, final ClassNode inferredType) {
+        return convertParameterType(parameterType, parameterType, inferredType);
+    }
+
     default ClassNode convertParameterType(final ClassNode targetType, final ClassNode parameterType, final ClassNode inferredType) {
         if (!getWrapper(inferredType).isDerivedFrom(getWrapper(parameterType))) {
             throw new RuntimeParserException("The inferred type[" + inferredType.redirect() + "] is not compatible with the parameter type[" + parameterType.redirect() + "]", parameterType);
         }
 
-        ClassNode type;
-        boolean isParameterTypePrimitive = isPrimitiveType(parameterType);
-        boolean isInferredTypePrimitive = isPrimitiveType(inferredType);
-        if (!isParameterTypePrimitive && isInferredTypePrimitive) {
-            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
-                // (1) java.lang.invoke.LambdaConversionException: Type mismatch for instantiated parameter 0: class java.lang.Integer is not a subtype of int
-                // (2) java.lang.BootstrapMethodError: bootstrap method initialization exception
-                type = inferredType;
-            } else {
+        ClassNode type = inferredType;
+        if (isPrimitiveType(parameterType)) {
+            if (!isPrimitiveType(inferredType)) {
+                // The non-primitive type and primitive type are not allowed to mix since Java 9+
+                // java.lang.invoke.LambdaConversionException: Type mismatch for instantiated parameter 0: class java.lang.Integer is not a subtype of int
+                type = getUnwrapper(inferredType);
+            }
+        } else if (isPrimitiveType(inferredType)) {
+            // GROOVY-9790: bootstrap method initialization exception raised when lambda parameter type is wrong
+            // (1) java.lang.invoke.LambdaConversionException: Type mismatch for instantiated parameter 0: class java.lang.Integer is not a subtype of int
+            // (2) java.lang.BootstrapMethodError: bootstrap method initialization exception
+            if (!(isDynamicTyped(parameterType) && isPrimitiveType(targetType)) // (1)
+                    && (parameterType.equals(getUnwrapper(parameterType)) || inferredType.equals(getWrapper(inferredType)))) { // (2)
                 // The non-primitive type and primitive type are not allowed to mix since Java 9+
                 // java.lang.invoke.LambdaConversionException: Type mismatch for instantiated parameter 0: int is not a subtype of class java.lang.Object
                 type = getWrapper(inferredType);
             }
-        } else if (isParameterTypePrimitive && !isInferredTypePrimitive) {
-            // The non-primitive type and primitive type are not allowed to mix since Java 9+
-            // java.lang.invoke.LambdaConversionException: Type mismatch for instantiated parameter 0: class java.lang.Integer is not a subtype of int
-            type = getUnwrapper(inferredType);
-        } else {
-            type = inferredType;
+        }
+        if (type.isGenericsPlaceHolder()) {
+            type = type.redirect();
         }
         return type;
     }
 
-    /**
-     * @deprecated use {@link #convertParameterType(ClassNode, ClassNode, ClassNode)} instead
-     */
-    @Deprecated
-    default ClassNode convertParameterType(final ClassNode parameterType, final ClassNode inferredType) {
-        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 = isPrimitiveType(parameterType);
-            boolean isInferredTypePrimitive = isPrimitiveType(inferredType);
-            ClassNode type;
-            if (!isParameterTypePrimitive && isInferredTypePrimitive) {
-                if (parameterType != getUnwrapper(parameterType) && inferredType != getWrapper(inferredType)) {
-                    type = inferredType;
-                } else {
-                    type = getWrapper(inferredType);
-                }
-            } else if (isParameterTypePrimitive && !isInferredTypePrimitive) {
-                type = getUnwrapper(inferredType);
-            } else {
-                type = inferredType;
-            }
-            return type;
-        }
-    }
-
     default Parameter prependParameter(final List<Parameter> parameterList, final String parameterName, final ClassNode parameterType) {
         Parameter parameter = new Parameter(parameterType, parameterName);
         parameter.setClosureSharedVariable(false);
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 faaead5..d28cfde 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
@@ -303,13 +303,11 @@
 
         if (inferredParamTypes != null) {
             for (int i = 0, n = parameters.length; i < n; i += 1) {
-                ClassNode inferredParamType = inferredParamTypes[i];
+                ClassNode inferredParamType = i < inferredParamTypes.length ? inferredParamTypes[i] : parameters[i].getType();
                 if (inferredParamType == null) continue;
-
                 Parameter parameter = parameters[i];
-                Parameter targetParameter = parameter;
 
-                ClassNode type = convertParameterType(targetParameter.getType(), parameter.getType(), inferredParamType);
+                ClassNode type = convertParameterType(parameter.getType(), inferredParamType);
                 parameter.setOriginType(type);
                 parameter.setType(type);
             }
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 fddbf43..3b4b14d 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1917,7 +1917,7 @@
         }
 
         if (type.getGenericsTypes()[0] != gt[0]) { // convert T to X
-            ClassNode cn = make(gt[0].getName()) , erasure = getCombinedBoundType(gt[0]);
+            ClassNode cn = make(gt[0].getName()) , erasure = getCombinedBoundType(gt[0]).redirect();
             cn.setGenericsPlaceHolder(true);
             cn.setGenericsTypes(gt);
             cn.setRedirect(erasure);
diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy
index 6a03bfe..790e289 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -774,17 +774,17 @@
     void testFunctionalInterface4() {
         assertScript shell, '''
             class Value<V> {
-                final V val
+                final V val;
                 Value(V v) {
                     this.val = v
                 }
                 String toString() {
                     val as String
                 }
-                def <T> Value<T> replace(Supplier<T> supplier) {
+                def <Out> Value<Out> replace(Supplier<Out> supplier) {
                     new Value<>(supplier.get())
                 }
-                def <T> Value<T> replace(Function<? super V, ? extends T> function) {
+                def <Out> Value<Out> replace(Function<? super V, ? extends Out> function) {
                     new Value<>(function.apply(val))
                 }
             }
@@ -806,6 +806,44 @@
         assert err =~ /Expected type java.util.List<java.lang.String> for lambda parameter: list/
     }
 
+    @Test // GROOVY-11013
+    void testFunctionalInterface6() {
+        assertScript shell, '''
+            interface I<T> {
+                def m(List<T> list_of_t)
+            }
+
+            I<String> face = (List<String> list) -> null
+        '''
+    }
+
+    @Test // GROOVY-11072
+    void testFunctionalInterface7() {
+        assertScript shell, '''
+            class Model {
+            }
+            class Table<T extends Model> {
+                interface ChunkReader<T> {
+                    void call(List<T> row)
+                }
+                void getAll(ChunkReader<T> reader) {
+                    List<T> chunk = []
+                    reader.call(chunk)
+                }
+            }
+            class TestModel extends Model {
+                int id = 0
+            }
+            class TestTable extends Table<TestModel> {
+            }
+
+            TestTable table = new TestTable()
+            table.getAll((List<TestModel> list) ->
+                list.each { TestModel tm -> println(tm.id) }
+            )
+        '''
+    }
+
     @Test
     void testFunctionWithUpdatingLocalVariable() {
         assertScript shell, '''