8007462: Fix provisional applicability for method references
authormcimadamore
Fri, 15 Feb 2013 16:29:58 +0000
changeset 16294 0c291a3cd60d
parent 16293 bf5e87940aee
child 16295 8b76e466f3cb
8007462: Fix provisional applicability for method references Summary: Add speculative arity-based check to rule out potential candidates when stuck reference is passed to method Reviewed-by: jjg
langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
langtools/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java
langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java
langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties
langtools/test/tools/javac/diags/examples/IncompatibleArgTypesInMethodRef.java
langtools/test/tools/javac/lambda/TargetType60.java
langtools/test/tools/javac/lambda/TargetType60.out
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Feb 15 16:28:57 2013 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Feb 15 16:29:58 2013 +0000
@@ -2532,8 +2532,7 @@
         try {
             //attribute member reference qualifier - if this is a constructor
             //reference, the expected kind must be a type
-            Type exprType = attribTree(that.expr,
-                    env, new ResultInfo(that.getMode() == ReferenceMode.INVOKE ? VAL | TYP : TYP, Type.noType));
+            Type exprType = attribTree(that.expr, env, memberReferenceQualifierResult(that));
 
             if (that.getMode() == JCMemberReference.ReferenceMode.NEW) {
                 exprType = chk.checkConstructorRefType(that.expr, exprType);
@@ -2688,6 +2687,12 @@
             return;
         }
     }
+    //where
+        ResultInfo memberReferenceQualifierResult(JCMemberReference tree) {
+            //if this is a constructor reference, the expected kind must be a type
+            return new ResultInfo(tree.getMode() == ReferenceMode.INVOKE ? VAL | TYP : TYP, Type.noType);
+        }
+
 
     @SuppressWarnings("fallthrough")
     void checkReferenceCompatible(JCMemberReference tree, Type descriptor, Type refType, CheckContext checkContext, boolean speculativeAttr) {
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java	Fri Feb 15 16:28:57 2013 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java	Fri Feb 15 16:29:58 2013 +0000
@@ -68,6 +68,7 @@
     final JCDiagnostic.Factory diags;
     final Enter enter;
     final Infer infer;
+    final Resolve rs;
     final Log log;
     final Symtab syms;
     final TreeMaker make;
@@ -87,6 +88,7 @@
         diags = JCDiagnostic.Factory.instance(context);
         enter = Enter.instance(context);
         infer = Infer.instance(context);
+        rs = Resolve.instance(context);
         log = Log.instance(context);
         syms = Symtab.instance(context);
         make = TreeMaker.instance(context);
@@ -463,10 +465,12 @@
 
             ResultInfo resultInfo;
             InferenceContext inferenceContext;
+            Env<AttrContext> env;
 
             public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
                 this.resultInfo = resultInfo;
                 this.inferenceContext = deferredAttrContext.inferenceContext;
+                this.env = dt.env.dup(dt.tree, dt.env.info.dup());
                 dt.tree.accept(this);
                 dt.speculativeCache.put(deferredAttrContext.msym, stuckTree, deferredAttrContext.phase);
                 return Type.noType;
@@ -511,12 +515,30 @@
                     return;
                 } else {
                     try {
-                        //TODO: we should speculative determine if there's a match
-                        //based on arity - if yes, method is applicable.
                         types.findDescriptorType(pt);
                     } catch (Types.FunctionDescriptorLookupError ex) {
                         checkContext.report(null, ex.getDiagnostic());
                     }
+                    JCExpression exprTree = (JCExpression)attribSpeculative(tree.getQualifierExpression(), env,
+                            attr.memberReferenceQualifierResult(tree));
+                    ListBuffer<Type> argtypes = ListBuffer.lb();
+                    for (Type t : types.findDescriptorType(pt).getParameterTypes()) {
+                        argtypes.append(syms.errType);
+                    }
+                    JCMemberReference mref2 = new TreeCopier<Void>(make).copy(tree);
+                    mref2.expr = exprTree;
+                    Pair<Symbol, ?> lookupRes =
+                            rs.resolveMemberReference(tree, env, mref2, exprTree.type, tree.name, argtypes.toList(), null, true);
+                    switch (lookupRes.fst.kind) {
+                        //note: as argtypes are erroneous types, type-errors must
+                        //have been caused by arity mismatch
+                        case Kinds.ABSENT_MTH:
+                        case Kinds.WRONG_MTH:
+                        case Kinds.WRONG_MTHS:
+                        case Kinds.STATICERR:
+                        case Kinds.MISSING_ENCL:
+                           checkContext.report(null, diags.fragment("incompatible.arg.types.in.mref"));
+                    }
                 }
             }
         }
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Fri Feb 15 16:28:57 2013 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Fri Feb 15 16:29:58 2013 +0000
@@ -2501,10 +2501,10 @@
 
         //merge results
         Pair<Symbol, ReferenceLookupHelper> res;
-        if (unboundSym.kind != MTH) {
+        if (!lookupSuccess(unboundSym)) {
             res = new Pair<Symbol, ReferenceLookupHelper>(boundSym, boundLookupHelper);
             env.info.pendingResolutionPhase = boundEnv.info.pendingResolutionPhase;
-        } else if (boundSym.kind == MTH) {
+        } else if (lookupSuccess(boundSym)) {
             res = new Pair<Symbol, ReferenceLookupHelper>(ambiguityError(boundSym, unboundSym), boundLookupHelper);
             env.info.pendingResolutionPhase = boundEnv.info.pendingResolutionPhase;
         } else {
@@ -2514,6 +2514,10 @@
 
         return res;
     }
+    //private
+        boolean lookupSuccess(Symbol s) {
+            return s.kind == MTH || s.kind == AMBIGUOUS;
+        }
 
     /**
      * Helper for defining custom method-like lookup logic; a lookup helper
@@ -2706,9 +2710,11 @@
 
         UnboundMethodReferenceLookupHelper(JCMemberReference referenceTree, Name name, Type site,
                 List<Type> argtypes, List<Type> typeargtypes, MethodResolutionPhase maxPhase) {
-            super(referenceTree, name,
-                    site.isRaw() ? types.asSuper(argtypes.head, site.tsym) : site,
-                    argtypes.tail, typeargtypes, maxPhase);
+            super(referenceTree, name, site, argtypes.tail, typeargtypes, maxPhase);
+            Type asSuperSite = types.asSuper(argtypes.head, site.tsym);
+            if (site.isRaw() && !asSuperSite.isErroneous()) {
+                this.site = asSuperSite;
+            }
         }
 
         @Override
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Feb 15 16:28:57 2013 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Feb 15 16:29:58 2013 +0000
@@ -724,6 +724,9 @@
 compiler.misc.incompatible.arg.types.in.lambda=\
     incompatible parameter types in lambda expression
 
+compiler.misc.incompatible.arg.types.in.mref=\
+    incompatible parameter types in method reference
+
 compiler.err.new.not.allowed.in.annotation=\
     ''new'' not allowed in an annotation
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/IncompatibleArgTypesInMethodRef.java	Fri Feb 15 16:29:58 2013 +0000
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2013, 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.
+ */
+
+// key: compiler.err.cant.apply.symbol
+// key: compiler.misc.infer.no.conforming.assignment.exists
+// key: compiler.misc.incompatible.arg.types.in.mref
+
+class IncompatibleArgTypesInMethodRef {
+    interface SAM<X> {
+        void m(X x);
+    }
+
+    void g(String s, Integer i) { }
+
+    <Z> void m(SAM<Z> s) { }
+
+    void test() {
+        m(this::g);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/TargetType60.java	Fri Feb 15 16:29:58 2013 +0000
@@ -0,0 +1,51 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8007462
+ * @summary Fix provisional applicability for method references
+ * @compile/fail/ref=TargetType60.out -XDrawDiagnostics TargetType60.java
+ */
+class TargetType60 {
+
+    interface Sam0 {
+        void m();
+    }
+
+    interface Sam1<X> {
+        void m(X x);
+    }
+
+    interface Sam2<X,Y> {
+        void m(X x, Y y);
+    }
+
+    void m0() { }
+    void m1(String s) { }
+    void m2(String s1, String s2) { }
+
+    void m01() { }
+    void m01(String s) { }
+
+    void m012() { }
+    void m012(String s) { }
+    void m012(String s1, String s2) { }
+
+    static String g(Sam0 s) { return null; }
+    static <U> U g(Sam1<U> s) { return null; }
+    static <U> U g(Sam2<U,String> s) { return null; }
+
+    void testBound() {
+        String s1 = g(this::m0); //ok - resolves to g(Sam0)
+        String s2 = g(this::m1); //ok - resolves to g(Sam1)
+        String s3 = g(this::m2); //ok - resolves to g(Sam2)
+        String s4 = g(this::m01);//ambiguous (g(Sam0), g(Sam1) apply)
+        String s5 = g(this::m012);//ambiguous (g(Sam0), g(Sam1), g(Sam2) apply)
+    }
+
+    static void testUnbound() {
+        TargetType60 s1 = g(TargetType60::m0); //ok - resolves to g(Sam1)
+        TargetType60 s2 = g(TargetType60::m1); //ok - resolves to g(Sam2)
+        TargetType60 s3 = g(TargetType60::m2); //none is applicable
+        TargetType60 s4 = g(TargetType60::m01);//ambiguous (g(Sam1), g(Sam2) apply)
+        TargetType60 s5 = g(TargetType60::m012);//ambiguous (g(Sam1), g(Sam2) apply)
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/TargetType60.out	Fri Feb 15 16:29:58 2013 +0000
@@ -0,0 +1,6 @@
+TargetType60.java:40:21: compiler.err.ref.ambiguous: g, kindname.method, g(TargetType60.Sam0), TargetType60, kindname.method, <U>g(TargetType60.Sam1<U>), TargetType60
+TargetType60.java:41:21: compiler.err.ref.ambiguous: g, kindname.method, <U>g(TargetType60.Sam1<U>), TargetType60, kindname.method, <U>g(TargetType60.Sam2<U,java.lang.String>), TargetType60
+TargetType60.java:47:27: compiler.err.cant.apply.symbols: kindname.method, g, @1308,{(compiler.misc.inapplicable.method: kindname.method, TargetType60, g(TargetType60.Sam0), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.invalid.mref: kindname.method, (compiler.misc.cant.apply.symbol: kindname.method, m2, java.lang.String,java.lang.String, compiler.misc.no.args, kindname.class, TargetType60, (compiler.misc.arg.length.mismatch))))),(compiler.misc.inapplicable.method: kindname.method, TargetType60, <U>g(TargetType60.Sam1<U>), (compiler.misc.infer.no.conforming.assignment.exists: U, (compiler.misc.incompatible.arg.types.in.mref))),(compiler.misc.inapplicable.method: kindname.method, TargetType60, <U>g(TargetType60.Sam2<U,java.lang.String>), (compiler.misc.infer.no.conforming.assignment.exists: U, (compiler.misc.incompatible.arg.types.in.mref)))}
+TargetType60.java:48:27: compiler.err.ref.ambiguous: g, kindname.method, <U>g(TargetType60.Sam1<U>), TargetType60, kindname.method, <U>g(TargetType60.Sam2<U,java.lang.String>), TargetType60
+TargetType60.java:49:27: compiler.err.ref.ambiguous: g, kindname.method, <U>g(TargetType60.Sam1<U>), TargetType60, kindname.method, <U>g(TargetType60.Sam2<U,java.lang.String>), TargetType60
+5 errors