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/01/30 20:43:22 UTC

[groovy] branch GROOVY_3_0_X updated (098937b -> 287a174)

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a change to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git.


    from 098937b  GROOVY-10236: add test case
     new 0864af2  GROOVY-10336: STC: handle method reference(s) supplied to variadic param
     new f08dc2f  GROOVY-10352: class model: load anno enum value as property expression
     new c7a3ae3  GROOVY-10375: SC: fix implicit-this access to non-static member of outer
     new 287a174  GROOVY-10379: interface default methods in `ClassNode#hasPossibleMethod`

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../java/org/codehaus/groovy/ast/ClassNode.java    |   7 ++
 .../classgen/asm/sc/StaticTypesLambdaWriter.java   |  27 +++--
 .../transform/stc/StaticTypeCheckingVisitor.java   |  45 +++++----
 .../org/codehaus/groovy/vmplugin/v8/Java8.java     |   3 +
 src/test/groovy/bugs/Groovy8964.groovy             |  51 ++++++++++
 src/test/groovy/transform/stc/LambdaTest.groovy    | 110 ++++++++++-----------
 .../transform/stc/MethodReferenceTest.groovy       |  20 ++++
 7 files changed, 169 insertions(+), 94 deletions(-)

[groovy] 02/04: GROOVY-10352: class model: load anno enum value as property expression

Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit f08dc2f59c8a915455649c81d9f70c929c1a8778
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Nov 9 09:48:39 2021 -0600

    GROOVY-10352: class model: load anno enum value as property expression
---
 src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
index 73b3833..6700777 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
@@ -399,6 +399,9 @@ public class Java8 implements VMPlugin {
         if (value instanceof Class)
             return new ClassExpression(ClassHelper.makeWithoutCaching((Class<?>)value));
 
+        if (value instanceof Enum)
+            return new PropertyExpression(new ClassExpression(ClassHelper.makeWithoutCaching(value.getClass())), value.toString());
+
         if (value.getClass().isArray()) {
             ListExpression elementExprs = new ListExpression();
             int len = Array.getLength(value);

[groovy] 01/04: GROOVY-10336: STC: handle method reference(s) supplied to variadic param

Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 0864af26c811c4b866960d49d5d516d201be3089
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Nov 1 10:37:29 2021 -0500

    GROOVY-10336: STC: handle method reference(s) supplied to variadic param
    
    Conflicts:
    	src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 45 +++++++++++-----------
 .../transform/stc/MethodReferenceTest.groovy       | 20 ++++++++++
 2 files changed, 42 insertions(+), 23 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 206b6bc..453040d 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -3629,39 +3629,38 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
 
         Parameter[] parameters = selectedMethod.getParameters();
+        final int nthParameter = parameters.length - 1;
 
-        List<Integer> methodReferenceParamIndexList = new LinkedList<>();
-        List<Expression> newArgumentExpressionList = new LinkedList<>();
+        List<Integer> methodReferencePositions = new LinkedList<>();
+        List<Expression> newArgumentExpressions = new LinkedList<>();
         for (int i = 0, n = argumentExpressions.size(); i < n; i += 1) {
             Expression argumentExpression = argumentExpressions.get(i);
             if (!(argumentExpression instanceof MethodReferenceExpression)) {
-                newArgumentExpressionList.add(argumentExpression);
-                continue;
-            }
-
-            Parameter param = parameters[i];
-            ClassNode paramType = param.getType();
+                newArgumentExpressions.add(argumentExpression);
+            } else {
+                Parameter param = parameters[Math.min(i, nthParameter)]; // GROOVY-10336
+                ClassNode paramType = param.getType();
+                if (i >= nthParameter && paramType.isArray())
+                    paramType = paramType.getComponentType();
 
-            if (!isFunctionalInterface(paramType.redirect())) {
-                addError("The argument is a method reference, but the parameter type is not a functional interface", argumentExpression);
-                newArgumentExpressionList.add(argumentExpression);
-                continue;
+                if (!isFunctionalInterface(paramType.redirect())) {
+                    addError("The argument is a method reference, but the parameter type is not a functional interface", argumentExpression);
+                    newArgumentExpressions.add(argumentExpression);
+                } else {
+                    methodReferencePositions.add(i);
+                    newArgumentExpressions.add(constructLambdaExpressionForMethodReference(paramType));
+                }
             }
-
-            LambdaExpression constructedLambdaExpression = constructLambdaExpressionForMethodReference(paramType);
-
-            newArgumentExpressionList.add(constructedLambdaExpression);
-            methodReferenceParamIndexList.add(i);
         }
 
-        if (methodReferenceParamIndexList.isEmpty()) return; // GROOVY-10269
+        if (methodReferencePositions.isEmpty()) return; // GROOVY-10269
 
-        visitMethodCallArguments(receiver, new ArgumentListExpression(newArgumentExpressionList), true, selectedMethod);
+        visitMethodCallArguments(receiver, args(newArgumentExpressions), 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 : methodReferencePositions) {
+            Expression lambdaExpression = newArgumentExpressions.get(index);
+            Expression methodReferenceExpression = argumentExpressions.get(index);
+            methodReferenceExpression.putNodeMetaData(CLOSURE_ARGUMENTS, lambdaExpression.getNodeMetaData(CLOSURE_ARGUMENTS));
         }
     }
 
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index 70ed133..7afcd0e 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -692,4 +692,24 @@ final class MethodReferenceTest extends GroovyTestCase {
 
         assert err =~ /The argument is a method reference, but the parameter type is not a functional interface/
     }
+
+    // GROOVY-10336
+    void testNotFunctionalInterface2() {
+        def err = shouldFail '''
+            import java.util.function.Supplier
+
+            class C {
+                Integer m() { 1 }
+            }
+            @groovy.transform.CompileStatic
+            void test() {
+                Supplier<Long> outer = () -> {
+                    Closure<Long> inner = (Object o, Supplier<Integer> s) -> 2L
+                    inner(new Object(), new C()::m)
+                }
+            }
+        '''
+
+        assert err =~ /The argument is a method reference, but the parameter type is not a functional interface/
+    }
 }

[groovy] 03/04: GROOVY-10375: SC: fix implicit-this access to non-static member of outer

Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit c7a3ae3afe1da8b591878e7305f50f852dc39df2
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Nov 18 13:31:38 2021 -0600

    GROOVY-10375: SC: fix implicit-this access to non-static member of outer
    
    Conflicts:
    	src/test/groovy/transform/stc/LambdaTest.groovy
---
 .../classgen/asm/sc/StaticTypesLambdaWriter.java   |  27 +++--
 src/test/groovy/transform/stc/LambdaTest.groovy    | 110 ++++++++++-----------
 2 files changed, 66 insertions(+), 71 deletions(-)

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 be6e8f5..2f55f9e 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
@@ -22,7 +22,6 @@ import org.codehaus.groovy.GroovyBugError;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.CodeVisitorSupport;
 import org.codehaus.groovy.ast.ConstructorNode;
-import org.codehaus.groovy.ast.GroovyCodeVisitor;
 import org.codehaus.groovy.ast.InnerClassNode;
 import org.codehaus.groovy.ast.MethodNode;
 import org.codehaus.groovy.ast.Parameter;
@@ -33,8 +32,6 @@ import org.codehaus.groovy.ast.expr.LambdaExpression;
 import org.codehaus.groovy.ast.expr.VariableExpression;
 import org.codehaus.groovy.ast.stmt.BlockStatement;
 import org.codehaus.groovy.ast.stmt.Statement;
-import org.codehaus.groovy.ast.tools.ClosureUtils;
-import org.codehaus.groovy.ast.tools.GeneralUtils;
 import org.codehaus.groovy.classgen.BytecodeInstruction;
 import org.codehaus.groovy.classgen.BytecodeSequence;
 import org.codehaus.groovy.classgen.asm.BytecodeHelper;
@@ -47,7 +44,6 @@ import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
 import org.codehaus.groovy.transform.stc.StaticTypesMarker;
 import org.objectweb.asm.MethodVisitor;
 
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -63,8 +59,10 @@ import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.findSAM;
 import static org.codehaus.groovy.ast.ClassHelper.isGeneratedFunction;
 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;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.cloneParams;
 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;
@@ -150,18 +148,19 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
         return new Parameter[]{new Parameter(SERIALIZEDLAMBDA_TYPE, "serializedLambda")};
     }
 
-    private static boolean isAccessingInstanceMembersOfEnclosingClass(final MethodNode syntheticLambdaMethodNode) {
+    private static boolean isAccessingInstanceMembersOfEnclosingClass(final MethodNode lambdaMethod) {
         boolean[] result = new boolean[1];
 
-        GroovyCodeVisitor visitor = new CodeVisitorSupport() {
+        ClassNode enclosingClass = lambdaMethod.getDeclaringClass().getOuterClass();
+
+        lambdaMethod.getCode().visit(new CodeVisitorSupport() {
             @Override
             public void visitVariableExpression(final VariableExpression expression) {
-                if (expression.isThisExpression()) {
+                if (expression.isThisExpression() || enclosingClass.equals(expression.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER))) {
                     result[0] = true;
                 }
             }
-        };
-        syntheticLambdaMethodNode.getCode().visit(visitor);
+        });
 
         return result[0];
     }
@@ -286,7 +285,7 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
                 "doCall",
                 ACC_PUBLIC,
                 abstractMethod.getReturnType(),
-                Arrays.copyOf(parametersWithExactType, parametersWithExactType.length),
+                parametersWithExactType.clone(),
                 ClassNode.EMPTY_ARRAY,
                 expression.getCode()
         );
@@ -297,10 +296,10 @@ public class StaticTypesLambdaWriter extends LambdaWriter implements AbstractFun
         return doCallMethod;
     }
 
-    private Parameter[] createParametersWithExactType(final LambdaExpression expression, MethodNode abstractMethod) {
-        Parameter[] targetParameters = GeneralUtils.cloneParams(abstractMethod.getParameters());
-        Parameter[] parameters = ClosureUtils.getParametersSafe(expression);
-        for (int i = 0; i < parameters.length; i++) {
+    private Parameter[] createParametersWithExactType(final LambdaExpression expression, final MethodNode abstractMethod) {
+        Parameter[] targetParameters = cloneParams(abstractMethod.getParameters());
+        Parameter[] parameters = getParametersSafe(expression);
+        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);
diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy
index 77d1adc..202ab7d 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -1577,13 +1577,12 @@ final class LambdaTest {
                 private String c() { 'a' }
 
                 byte[] p() {
-                        def out = new ByteArrayOutputStream()
-                        SerializableFunction<Integer, String> f = (Integer e) -> c() + e
-                        out.withObjectOutputStream {
-                            it.writeObject(f)
-                        }
-
-                        return out.toByteArray()
+                    def out = new ByteArrayOutputStream()
+                    SerializableFunction<Integer, String> f = (Integer i) -> c() + i
+                    out.withObjectOutputStream {
+                        it.writeObject(f)
+                    }
+                    out.toByteArray()
                 }
 
                 static void main(String[] args) {
@@ -1609,13 +1608,12 @@ final class LambdaTest {
                 private String c() { 'a' }
 
                 byte[] p() {
-                        def out = new ByteArrayOutputStream()
-                        SerializableFunction<Integer, String> f = (Integer e) -> c() + e
-                        out.withObjectOutputStream {
-                            it.writeObject(f)
-                        }
-
-                        return out.toByteArray()
+                    def out = new ByteArrayOutputStream()
+                    SerializableFunction<Integer, String> f = (Integer i) -> c() + i
+                    out.withObjectOutputStream {
+                        it.writeObject(f)
+                    }
+                    out.toByteArray()
                 }
 
                 static void main(String[] args) {
@@ -1866,17 +1864,6 @@ final class LambdaTest {
     }
 
     @Test
-    void testScriptWithExistingMainCS() { // GROOVY-9146
-        assertScript '''
-            @groovy.transform.CompileStatic
-            static void main(args) {
-                java.util.function.Function<String, String> lower = String::toLowerCase
-                assert lower.toString().contains('$$Lambda$')
-            }
-        '''
-    }
-
-    @Test
     void testDeserializeNestedLambda4() {
         assertScript '''
             import java.util.function.Function
@@ -1884,51 +1871,60 @@ final class LambdaTest {
             interface SerializableFunction<T, R> extends Function<T, R>, Serializable {}
 
             @groovy.transform.CompileStatic
-            class Test1 {
-                static p() {
-                        def out1 = new ByteArrayOutputStream()
-                        SerializableFunction<Integer, String> f1 = (Integer e) -> 'a' + e
-                        out1.withObjectOutputStream {
-                            it.writeObject(f1)
-                        }
-                        def result1 = out1.toByteArray()
+            class C {
+                static test() {
+                    def out1 = new ByteArrayOutputStream()
+                    SerializableFunction<Integer, String> f1 = (Integer i) -> 'a' + i
+                    out1.withObjectOutputStream {
+                        it.writeObject(f1)
+                    }
 
-                        def out2 = new ByteArrayOutputStream()
-                        SerializableFunction<Integer, String> f2 = (Integer e) -> 'b' + e
-                        out2.withObjectOutputStream {
-                            it.writeObject(f2)
-                        }
-                        def result2 = out2.toByteArray()
+                    def out2 = new ByteArrayOutputStream()
+                    SerializableFunction<Integer, String> f2 = (Integer i) -> 'b' + i
+                    out2.withObjectOutputStream {
+                        it.writeObject(f2)
+                    }
 
-                        // nested lambda expression
-                        def out3 = new ByteArrayOutputStream()
-                        SerializableFunction<Integer, String> f3 = (Integer e) -> {
-                            SerializableFunction<Integer, String> nf = ((Integer ne) -> 'n' + ne)
-                            'c' + nf(e)
-                        }
-                        out3.withObjectOutputStream {
-                            it.writeObject(f3)
-                        }
-                        def result3 = out3.toByteArray()
+                    // nested lambda expression
+                    def out3 = new ByteArrayOutputStream()
+                    SerializableFunction<Integer, String> f3 = (Integer i) -> {
+                        SerializableFunction<Integer, String> nf = (Integer j) -> 'c' + j
+                        nf(i) + 'c'
+                    }
+                    out3.withObjectOutputStream {
+                        it.writeObject(f3)
+                    }
 
-                        return [result1, result2, result3]
+                    [out1.toByteArray(), out2.toByteArray(), out3.toByteArray()]
                 }
             }
 
-            def (byte[] serializedLambdaBytes1, byte[] serializedLambdaBytes2, byte[] serializedLambdaBytes3) = Test1.p()
-            new ByteArrayInputStream(serializedLambdaBytes1).withObjectInputStream(Test1.class.classLoader) {
+            def (serializedLambdaBytes1, serializedLambdaBytes2, serializedLambdaBytes3) = C.test()
+
+            new ByteArrayInputStream(serializedLambdaBytes1).withObjectInputStream(this.class.classLoader) {
                 SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
-                assert 'a1' == f.apply(1)
+                assert f.apply(1) == 'a1'
             }
 
-            new ByteArrayInputStream(serializedLambdaBytes2).withObjectInputStream(Test1.class.classLoader) {
+            new ByteArrayInputStream(serializedLambdaBytes2).withObjectInputStream(this.class.classLoader) {
                 SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
-                assert 'b1' == f.apply(1)
+                assert f.apply(1) == 'b1'
             }
 
-            new ByteArrayInputStream(serializedLambdaBytes3).withObjectInputStream(Test1.class.classLoader) {
+            new ByteArrayInputStream(serializedLambdaBytes3).withObjectInputStream(this.class.classLoader) {
                 SerializableFunction<Integer, String> f = (SerializableFunction<Integer, String>) it.readObject()
-                assert 'cn1' == f.apply(1)
+                assert f.apply(1) == 'c1c'
+            }
+        '''
+    }
+
+    @Test // GROOVY-9146
+    void testScriptWithExistingMainCS() {
+        assertScript '''
+            @groovy.transform.CompileStatic
+            static void main(args) {
+                java.util.function.Function<String, String> lower = String::toLowerCase
+                assert lower.toString().contains('$$Lambda$')
             }
         '''
     }

[groovy] 04/04: GROOVY-10379: interface default methods in `ClassNode#hasPossibleMethod`

Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 287a174e26c927f23b8237c983de8b551ab280ca
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Nov 23 12:13:14 2021 -0600

    GROOVY-10379: interface default methods in `ClassNode#hasPossibleMethod`
---
 .../java/org/codehaus/groovy/ast/ClassNode.java    |  7 +++
 src/test/groovy/bugs/Groovy8964.groovy             | 51 ++++++++++++++++++++++
 2 files changed, 58 insertions(+)

diff --git a/src/main/java/org/codehaus/groovy/ast/ClassNode.java b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
index 896b769..4769125 100644
--- a/src/main/java/org/codehaus/groovy/ast/ClassNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
@@ -1264,6 +1264,13 @@ public class ClassNode extends AnnotatedNode implements Opcodes {
                     return true;
                 }
             }
+            for (ClassNode in : cn.getAllInterfaces()) {
+                for (MethodNode mn : in.getDeclaredMethods(name)) {
+                    if (mn.isDefault() && hasCompatibleNumberOfArgs(mn, count)) {
+                        return true;
+                    }
+                }
+            }
         }
 
         return false;
diff --git a/src/test/groovy/bugs/Groovy8964.groovy b/src/test/groovy/bugs/Groovy8964.groovy
index 0a0e13a..df154e8 100644
--- a/src/test/groovy/bugs/Groovy8964.groovy
+++ b/src/test/groovy/bugs/Groovy8964.groovy
@@ -18,6 +18,8 @@
  */
 package groovy.bugs
 
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
 import org.junit.Test
 
 import static groovy.test.GroovyAssert.assertScript
@@ -77,6 +79,55 @@ final class Groovy8964 {
         '''
     }
 
+    @Test // GROOVY-10379
+    void testInstanceMethodNotMaskedByStaticMethodWithSameNumberOfArgs3() {
+        def config = new CompilerConfiguration(
+            targetDirectory: File.createTempDir(),
+            jointCompilationOptions: [memStub: true]
+        )
+
+        def parentDir = File.createTempDir()
+        try {
+            new File(parentDir, 'p').mkdir()
+
+            def a = new File(parentDir, 'p/A.java')
+            a.write '''
+                package p;
+                public abstract class A implements I {
+                    public static String m(Number n) { return "number"; }
+                }
+            '''
+            def b = new File(parentDir, 'p/I.java')
+            b.write '''
+                package p;
+                public interface I {
+                    default String m(String s) { return "string"; }
+                }
+            '''
+            def c = new File(parentDir, 'Main.groovy')
+            c.write '''
+                @groovy.transform.CompileStatic
+                class C extends p.A {
+                    void test() {
+                        String result = m('') // GroovyCastException: Cannot cast object 'class C' with class 'java.lang.Class' to class 'p.I'
+                        assert result == 'string'
+                    }
+                }
+                new C().test()
+            '''
+
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources(a, b, c)
+            cu.compile()
+
+            loader.loadClass('Main').getDeclaredConstructor().newInstance().run()
+        } finally {
+            config.targetDirectory.deleteDir()
+            parentDir.deleteDir()
+        }
+    }
+
     static abstract class A {
         static void m(Integer i) {}
         protected void m(String s) {}