8148354: Errors targeting functional interface intersection types
authorvromero
Fri, 18 May 2018 09:22:31 -0700
changeset 50181 f854b76b6a0c
parent 50180 ffa644980dff
child 50182 28cd297a1086
8148354: Errors targeting functional interface intersection types Reviewed-by: mcimadamore, dlsmith
src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java
src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java
test/langtools/tools/javac/T8148354/IntersectionFunctionalButComponentsNotTest.java
test/langtools/tools/javac/T8148354/IntersectionTypeBugTest.java
test/langtools/tools/javac/diags/examples.not-yet.txt
test/langtools/tools/javac/diags/examples/NotAnInterfaceComponent.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java	Fri May 18 14:51:06 2018 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java	Fri May 18 09:22:31 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -97,7 +97,6 @@
     JCDiagnostic.Factory diags;
     List<Warner> warnStack = List.nil();
     final Name capturedName;
-    private final FunctionDescriptorLookupError functionDescriptorLookupError;
 
     public final Warner noWarnings;
 
@@ -122,7 +121,6 @@
         capturedName = names.fromString("<captured wildcard>");
         messages = JavacMessages.instance(context);
         diags = JCDiagnostic.Factory.instance(context);
-        functionDescriptorLookupError = new FunctionDescriptorLookupError();
         noWarnings = new Warner(null);
     }
     // </editor-fold>
@@ -796,7 +794,7 @@
         }
 
         FunctionDescriptorLookupError failure(JCDiagnostic diag) {
-            return functionDescriptorLookupError.setMessage(diag);
+            return new FunctionDescriptorLookupError().setMessage(diag);
         }
     }
 
@@ -887,12 +885,12 @@
      * main purposes: (i) checking well-formedness of a functional interface;
      * (ii) perform functional interface bridge calculation.
      */
-    public ClassSymbol makeFunctionalInterfaceClass(Env<AttrContext> env, Name name, List<Type> targets, long cflags) {
-        if (targets.isEmpty()) {
+    public ClassSymbol makeFunctionalInterfaceClass(Env<AttrContext> env, Name name, Type target, long cflags) {
+        if (target == null || target == syms.unknownType) {
             return null;
         }
-        Symbol descSym = findDescriptorSymbol(targets.head.tsym);
-        Type descType = findDescriptorType(targets.head);
+        Symbol descSym = findDescriptorSymbol(target.tsym);
+        Type descType = findDescriptorType(target);
         ClassSymbol csym = new ClassSymbol(cflags, name, env.enclClass.sym.outermostClass());
         csym.completer = Completer.NULL_COMPLETER;
         csym.members_field = WriteableScope.create(csym);
@@ -900,7 +898,9 @@
         csym.members_field.enter(instDescSym);
         Type.ClassType ctype = new Type.ClassType(Type.noType, List.nil(), csym);
         ctype.supertype_field = syms.objectType;
-        ctype.interfaces_field = targets;
+        ctype.interfaces_field = target.isIntersection() ?
+                directSupertypes(target) :
+                List.of(target);
         csym.type = ctype;
         csym.sourcefile = ((ClassSymbol)csym.owner).sourcefile;
         return csym;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Fri May 18 14:51:06 2018 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Fri May 18 09:22:31 2018 -0700
@@ -2656,12 +2656,29 @@
                  * the target will be updated to SAM
                  */
                 currentTarget = targetChecker.visit(currentTarget, that);
-                if (explicitParamTypes != null) {
-                    currentTarget = infer.instantiateFunctionalInterface(that,
-                            currentTarget, explicitParamTypes, resultInfo.checkContext);
+                if (!currentTarget.isIntersection()) {
+                    if (explicitParamTypes != null) {
+                        currentTarget = infer.instantiateFunctionalInterface(that,
+                                currentTarget, explicitParamTypes, resultInfo.checkContext);
+                    }
+                    currentTarget = types.removeWildcards(currentTarget);
+                    lambdaType = types.findDescriptorType(currentTarget);
+                } else {
+                    IntersectionClassType ict = (IntersectionClassType)currentTarget;
+                    ListBuffer<Type> components = new ListBuffer<>();
+                    for (Type bound : ict.getExplicitComponents()) {
+                        if (explicitParamTypes != null) {
+                            bound = infer.instantiateFunctionalInterface(that,
+                                    bound, explicitParamTypes, resultInfo.checkContext);
+                        }
+                        bound = types.removeWildcards(bound);
+                        components.add(bound);
+                    }
+                    currentTarget = types.makeIntersectionType(components.toList());
+                    currentTarget.tsym.flags_field |= INTERFACE;
+                    lambdaType = types.findDescriptorType(currentTarget);
                 }
-                currentTarget = types.removeWildcards(currentTarget);
-                lambdaType = types.findDescriptorType(currentTarget);
+
             } else {
                 currentTarget = Type.recoveryType;
                 lambdaType = fallbackDescriptorType(that);
@@ -2701,27 +2718,11 @@
             }
 
             public Type visitIntersectionClassType(IntersectionClassType ict, DiagnosticPosition pos) {
-                Symbol desc = types.findDescriptorSymbol(makeNotionalInterface(ict));
-                Type target = null;
-                for (Type bound : ict.getExplicitComponents()) {
-                    TypeSymbol boundSym = bound.tsym;
-                    if (bound.tsym == syms.objectType.tsym) {
-                        continue;
-                    }
-                    if (types.isFunctionalInterface(boundSym) &&
-                            types.findDescriptorSymbol(boundSym) == desc) {
-                        target = bound;
-                    } else if (!boundSym.isInterface() || (boundSym.flags() & ANNOTATION) != 0) {
-                        //bound must be an interface
-                        reportIntersectionError(pos, "not.an.intf.component", boundSym);
-                    }
-                }
-                return target != null ?
-                        target :
-                        ict.getExplicitComponents().head; //error recovery
+                types.findDescriptorSymbol(makeNotionalInterface(ict, pos));
+                return ict;
             }
 
-            private TypeSymbol makeNotionalInterface(IntersectionClassType ict) {
+            private TypeSymbol makeNotionalInterface(IntersectionClassType ict, DiagnosticPosition pos) {
                 ListBuffer<Type> targs = new ListBuffer<>();
                 ListBuffer<Type> supertypes = new ListBuffer<>();
                 for (Type i : ict.interfaces_field) {
@@ -2735,11 +2736,6 @@
                 notionalIntf.tsym.flags_field |= INTERFACE;
                 return notionalIntf.tsym;
             }
-
-            private void reportIntersectionError(DiagnosticPosition pos, String key, Object... args) {
-                resultInfo.checkContext.report(pos,
-                                               diags.fragment(Fragments.BadIntersectionTargetForFunctionalExpr(diags.fragment(key, args))));
-            }
         };
 
         private Type fallbackDescriptorType(JCExpression tree) {
@@ -3283,20 +3279,9 @@
                     inferenceContext -> setFunctionalInfo(env, fExpr, pt, inferenceContext.asInstType(descriptorType),
                     inferenceContext.asInstType(primaryTarget), checkContext));
         } else {
-            ListBuffer<Type> targets = new ListBuffer<>();
             if (pt.hasTag(CLASS)) {
-                if (pt.isCompound()) {
-                    targets.append(types.removeWildcards(primaryTarget)); //this goes first
-                    for (Type t : ((IntersectionClassType)pt()).interfaces_field) {
-                        if (t != primaryTarget) {
-                            targets.append(types.removeWildcards(t));
-                        }
-                    }
-                } else {
-                    targets.append(types.removeWildcards(primaryTarget));
-                }
+                fExpr.target = primaryTarget;
             }
-            fExpr.targets = targets.toList();
             if (checkContext.deferredAttrContext().mode == DeferredAttr.AttrMode.CHECK &&
                     pt != Type.recoveryType) {
                 //check that functional interface class is well-formed
@@ -3306,7 +3291,7 @@
                      * above.
                      */
                     ClassSymbol csym = types.makeFunctionalInterfaceClass(env,
-                            names.empty, List.of(fExpr.targets.head), ABSTRACT);
+                            names.empty, fExpr.target, ABSTRACT);
                     if (csym != null) {
                         chk.checkImplementations(env.tree, csym, csym);
                         try {
@@ -3317,7 +3302,7 @@
                             types.findDescriptorType(csym.type);
                         } catch (FunctionDescriptorLookupError err) {
                             resultInfo.checkContext.report(fExpr,
-                                    diags.fragment(Fragments.NoSuitableFunctionalIntfInst(fExpr.targets.head)));
+                                    diags.fragment(Fragments.NoSuitableFunctionalIntfInst(fExpr.target)));
                         }
                     }
                 } catch (Types.FunctionDescriptorLookupError ex) {
@@ -5185,8 +5170,8 @@
         @Override
         public void visitLambda(JCLambda that) {
             super.visitLambda(that);
-            if (that.targets == null) {
-                that.targets = List.nil();
+            if (that.target == null) {
+                that.target = syms.unknownType;
             }
         }
 
@@ -5197,8 +5182,8 @@
                 that.sym = new MethodSymbol(0, names.empty, dummyMethodType(),
                         syms.noSymbol);
             }
-            if (that.targets == null) {
-                that.targets = List.nil();
+            if (that.target == null) {
+                that.target = syms.unknownType;
             }
         }
     }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Fri May 18 14:51:06 2018 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Fri May 18 09:22:31 2018 -0700
@@ -72,6 +72,8 @@
 import javax.lang.model.element.ElementKind;
 import javax.lang.model.type.TypeKind;
 
+import com.sun.tools.javac.code.Type.IntersectionClassType;
+import com.sun.tools.javac.code.Types.FunctionDescriptorLookupError;
 import com.sun.tools.javac.main.Option;
 
 /**
@@ -929,7 +931,7 @@
                         : expressionNew();
 
                 JCLambda slam = make.Lambda(params.toList(), expr);
-                slam.targets = tree.targets;
+                slam.target = tree.target;
                 slam.type = tree.type;
                 slam.pos = tree.pos;
                 return slam;
@@ -1111,7 +1113,7 @@
             int refKind, Symbol refSym, List<JCExpression> indy_args) {
         JCFunctionalExpression tree = context.tree;
         //determine the static bsm args
-        MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
+        MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.target.tsym);
         List<Object> staticArgs = List.of(
                 typeToMethodType(samSym.type),
                 new Pool.MethodHandle(refKind, refSym, types),
@@ -1134,8 +1136,13 @@
 
         if (context.needsAltMetafactory()) {
             ListBuffer<Object> markers = new ListBuffer<>();
-            for (Type t : tree.targets.tail) {
-                if (t.tsym != syms.serializableType.tsym) {
+            List<Type> targets = tree.target.isIntersection() ?
+                    types.directSupertypes(tree.target) :
+                    List.nil();
+            for (Type t : targets) {
+                t = types.erasure(t);
+                if (t.tsym != syms.serializableType.tsym &&
+                    t.tsym != syms.objectType.tsym) {
                     markers.append(t.tsym);
                 }
             }
@@ -1903,13 +1910,13 @@
                 this.depth = frameStack.size() - 1;
                 this.prev = context();
                 ClassSymbol csym =
-                        types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.targets, ABSTRACT | INTERFACE);
+                        types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.target, ABSTRACT | INTERFACE);
                 this.bridges = types.functionalInterfaceBridges(csym);
             }
 
             /** does this functional expression need to be created using alternate metafactory? */
             boolean needsAltMetafactory() {
-                return tree.targets.length() > 1 ||
+                return tree.target.isIntersection() ||
                         isSerializable() ||
                         bridges.length() > 1;
             }
@@ -1919,12 +1926,7 @@
                 if (forceSerializable) {
                     return true;
                 }
-                for (Type target : tree.targets) {
-                    if (types.asSuper(target, syms.serializableType.tsym) != null) {
-                        return true;
-                    }
-                }
-                return false;
+                return types.asSuper(tree.target, syms.serializableType.tsym) != null;
             }
 
             /**
@@ -2416,7 +2418,7 @@
             }
 
             Type bridgedRefSig() {
-                return types.erasure(types.findDescriptorSymbol(tree.targets.head.tsym).type);
+                return types.erasure(types.findDescriptorSymbol(tree.target.tsym).type);
             }
         }
     }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java	Fri May 18 14:51:06 2018 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java	Fri May 18 09:22:31 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,8 @@
 import com.sun.tools.javac.code.Attribute.TypeCompound;
 import com.sun.tools.javac.code.Source.Feature;
 import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.code.Type.IntersectionClassType;
+import com.sun.tools.javac.code.Types.FunctionDescriptorLookupError;
 import com.sun.tools.javac.resources.CompilerProperties.Errors;
 import com.sun.tools.javac.tree.*;
 import com.sun.tools.javac.tree.JCTree.*;
@@ -593,7 +595,11 @@
             currentMethod = null;
             tree.params = translate(tree.params);
             tree.body = translate(tree.body, tree.body.type==null? null : erasure(tree.body.type));
-            tree.type = erasure(tree.type);
+            if (!tree.type.isIntersection()) {
+                tree.type = erasure(tree.type);
+            } else {
+                tree.type = types.erasure(types.findDescriptorSymbol(tree.type.tsym).owner.type);
+            }
             result = tree;
         }
         finally {
@@ -876,8 +882,11 @@
         } else {
             tree.expr = translate(tree.expr, receiverTarget);
         }
-
-        tree.type = erasure(tree.type);
+        if (!tree.type.isIntersection()) {
+            tree.type = erasure(tree.type);
+        } else {
+            tree.type = types.erasure(types.findDescriptorSymbol(tree.type.tsym).owner.type);
+        }
         if (tree.varargsElement != null)
             tree.varargsElement = erasure(tree.varargsElement);
         result = tree;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java	Fri May 18 14:51:06 2018 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java	Fri May 18 09:22:31 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -729,10 +729,10 @@
         }
 
         /** list of target types inferred for this functional expression. */
-        public List<Type> targets;
+        public Type target;
 
         public Type getDescriptorType(Types types) {
-            return targets.nonEmpty() ? types.findDescriptorType(targets.head) : types.createErrorType(null);
+            return target != null ? types.findDescriptorType(target) : types.createErrorType(null);
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/T8148354/IntersectionFunctionalButComponentsNotTest.java	Fri May 18 09:22:31 2018 -0700
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * bug 8148354
+ * @summary Errors targeting functional interface intersection types
+ * @compile IntersectionFunctionalButComponentsNotTest.java
+ */
+class IntersectionFunctionalButComponentsNotTest {
+    // nor A or B are functional interfaces but the intersection is
+    <T extends Object & A & B> void consume(T arg) { }
+
+    void foo() {
+        consume(System::gc);
+    }
+
+    interface C {
+        void c();
+    }
+
+    interface A extends C {
+        void a();
+    }
+
+    interface B extends C {
+        default void c() { }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/T8148354/IntersectionTypeBugTest.java	Fri May 18 09:22:31 2018 -0700
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * bug 8148354
+ * @summary Errors targeting functional interface intersection types
+ * @compile IntersectionTypeBugTest.java
+ */
+
+import java.io.Serializable;
+import java.util.function.Consumer;
+
+class IntersectionTypeBugTest {
+    <T extends Object & Serializable & Consumer<String>> void consume(final T cons, final String s) {}
+
+    void process(final String s) {}
+
+    public void foo() {
+        consume(this::process, "Hello World");
+    }
+
+    // another case
+    static class AnotherTest<T> {
+        void foo() {
+            Object r = (Object & Serializable & R<T>) () -> {};
+        }
+
+        interface R<I> {
+            void foo();
+        }
+    }
+}
--- a/test/langtools/tools/javac/diags/examples.not-yet.txt	Fri May 18 14:51:06 2018 +0200
+++ b/test/langtools/tools/javac/diags/examples.not-yet.txt	Fri May 18 09:22:31 2018 -0700
@@ -129,6 +129,8 @@
 compiler.err.preview.feature.disabled.classfile         # preview feature support: needs compilation against classfile
 compiler.warn.preview.feature.use.classfile             # preview feature support: needs compilation against classfile
 compiler.note.preview.plural.additional                 # preview feature support: diag test causes intermittent failures (see JDK-8201498)
+compiler.misc.bad.intersection.target.for.functional.expr  # currently not generated, should be removed?
+compiler.misc.not.an.intf.component
 
 # The following module-related messages will have to stay on the not-yet list for various reasons:
 compiler.warn.locn.unknown.file.on.module.path                # Never issued ATM (short circuited with an if (false))
--- a/test/langtools/tools/javac/diags/examples/NotAnInterfaceComponent.java	Fri May 18 14:51:06 2018 +0200
+++ b/test/langtools/tools/javac/diags/examples/NotAnInterfaceComponent.java	Fri May 18 09:22:31 2018 -0700
@@ -21,9 +21,9 @@
  * questions.
  */
 
+// key: compiler.misc.not.a.functional.intf.1
 // key: compiler.err.prob.found.req
-// key: compiler.misc.bad.intersection.target.for.functional.expr
-// key: compiler.misc.not.an.intf.component
+// key: compiler.misc.incompatible.abstracts
 
 class NotAnInterfaceComponent {
     Object o = (String & Runnable) ()-> { };