changeset 751:9a1ae3fc0a88

Target-type inference fixes: *) Outcome of target-type inference depends on the context in which the lambda is used (assignment vs. method invocation context) when it shouldn't *) Trial attribution of lambda body leaves stale results in AST in case of overload resolution which leads to bad resolution and spurious diagnostics *) Spurious inference errors when synthetic types of lambda parameters depend on uninferred generic method type-args
author mcimadamore
date Fri, 12 Nov 2010 12:49:12 +0000
parents f5c76a5c84e3
children 81019709553d
files src/share/classes/com/sun/tools/javac/code/Type.java src/share/classes/com/sun/tools/javac/comp/Attr.java src/share/classes/com/sun/tools/javac/comp/Check.java src/share/classes/com/sun/tools/javac/comp/Infer.java src/share/classes/com/sun/tools/javac/comp/Resolve.java test/tools/javac/lambda/TargetType01.out test/tools/javac/lambda/TargetType15.java test/tools/javac/lambda/TargetType16.java test/tools/javac/lambda/TargetType16.out
diffstat 9 files changed, 175 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/code/Type.java	Thu Nov 11 14:47:39 2010 +0000
+++ b/src/share/classes/com/sun/tools/javac/code/Type.java	Fri Nov 12 12:49:12 2010 +0000
@@ -27,6 +27,7 @@
 
 import com.sun.tools.javac.util.*;
 import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.comp.Resolve;
 
 import javax.lang.model.type.*;
 
@@ -1226,7 +1227,9 @@
          * Replaces this ForAll's typevars with a set of concrete Java types
          * and returns the instantiated generic type. Subclasses should override
          * in order to check that the list of types is a valid instantiation
-         * of the ForAll's typevars.
+         * of the ForAll's typevars. Since the same ForAll can be instantiated
+         * multiple times (because of overload resolution) and extra-parameter
+         * is used to indicate whether the instantiation is final or not.
          *
          * @param actuals list of actual types
          * @param types types instance
@@ -1234,8 +1237,16 @@
          * by types in actuals
          */
         public Type inst(List<Type> actuals, Type to, Types types) {
+            return inst(actuals, to, types, InstantiationPhase.CHECK);
+        }
+        public Type inst(List<Type> actuals, Type to, Types types, InstantiationPhase phase) {
             return types.subst(qtype, tvars, actuals);
         }
+        //where
+        public enum InstantiationPhase {
+            RESOLUTION,
+            CHECK;
+        }
 
         /**
          * Kind of type-constraint derived during type inference
--- a/src/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Nov 11 14:47:39 2010 +0000
+++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Nov 12 12:49:12 2010 +0000
@@ -1807,6 +1807,13 @@
             if (tree.constructor != null && tree.constructor.kind == MTH)
                 owntype = clazztype;
         }
+        if (tree.constructorType != null &&
+                tree.constructorType.getReturnType().tag == FORALL) {
+            //we need to 'close' open type-variables in the constructor type
+            //this can happen if the constructor is passed a lambda expression
+            tree.constructorType.asMethodType(types).restype =
+                    check(tree, tree.constructorType.getReturnType(), VAL, pkind, pt);
+        }
         result = check(tree, owntype, VAL, pkind, pt);
         chk.validate(tree.typeargs, localEnv);
     }
@@ -1836,11 +1843,11 @@
                     }
                 }
                 @Override
-                public Type inst(List<Type> inferred, Type to, Types types) throws Infer.NoInstanceException {
+                public Type inst(List<Type> inferred, Type to, Types types, ForAll.InstantiationPhase phase) throws Infer.NoInstanceException {
                     // check that inferred bounds conform to their bounds
                     infer.checkWithinBounds(tvars,
                            types.subst(tvars, tvars, inferred), Warner.noWarnings);
-                    return super.inst(inferred, to, types);
+                    return super.inst(inferred, to, types, phase);
                 }
             };
         } else {
@@ -2220,11 +2227,12 @@
         }
         return that.type = new ForAll(tvars.toList(), ft) {
             @Override
-            public Type inst(List<Type> actuals, Type to, Types types) {
-                if (needsReturnTypeInferred) {
-                    actuals = actuals.tail;
-                }
-                if (that.type.tag == FORALL) {
+            public Type inst(List<Type> actuals, Type to, Types types, ForAll.InstantiationPhase phase) {
+                if (phase == ForAll.InstantiationPhase.CHECK) {
+                    that.type = types.subst(this, tvars, actuals);
+                    if (needsReturnTypeInferred) {
+                        actuals = actuals.tail;
+                    }
                     //now that we have a target type 'to' and a set of instantiated types
                     //for each of the unknown lambda parameter types, we can proceed
                     //with the second phase of lambda attribution (see below).
@@ -2235,8 +2243,10 @@
                         }
                     }
                     attribLambda(that, to);
+                    return that.type;
+                } else {
+                    return super.inst(actuals, to, types, phase);
                 }
-                return to;
             }
         };
     }
@@ -2409,7 +2419,7 @@
 
         that.targetType = superType;
         that.sym = localEnv.enclMethod.sym;
-        return samOrFunctionType;
+        return superType;
     }
     
     public void visitParens(JCParens tree) {
@@ -2697,9 +2707,9 @@
                             return rs.getConstraints(tv, ck);
                         }
                         @Override
-                        public Type inst(List<Type> actuals, Type to, Types types) {
-                            rs.inst(actuals, to, types);
-                            return super.inst(actuals, to, types);
+                        public Type inst(List<Type> actuals, Type to, Types types, ForAll.InstantiationPhase phase) {
+                            rs.inst(actuals, to, types, phase);
+                            return super.inst(actuals, to, types, phase);
                         }
                     };
                 }
--- a/src/share/classes/com/sun/tools/javac/comp/Check.java	Thu Nov 11 14:47:39 2010 +0000
+++ b/src/share/classes/com/sun/tools/javac/comp/Check.java	Fri Nov 12 12:49:12 2010 +0000
@@ -450,7 +450,7 @@
             return pt;
         } else {
             try {
-                return infer.instantiateExpr(env, t, pt, warn);
+                return infer.instantiateExpr(env, t, pt, warn, ForAll.InstantiationPhase.CHECK);
             } catch (Infer.NoInstanceException ex) {
                 if (ex.isAmbiguous) {
                     JCDiagnostic d = ex.getDiagnostic();
--- a/src/share/classes/com/sun/tools/javac/comp/Infer.java	Thu Nov 11 14:47:39 2010 +0000
+++ b/src/share/classes/com/sun/tools/javac/comp/Infer.java	Fri Nov 12 12:49:12 2010 +0000
@@ -25,7 +25,6 @@
 
 package com.sun.tools.javac.comp;
 
-import com.sun.tools.javac.tree.JCTree;
 import com.sun.tools.javac.tree.JCTree.JCTypeCast;
 import com.sun.tools.javac.util.*;
 import com.sun.tools.javac.util.List;
@@ -36,6 +35,7 @@
 import com.sun.tools.javac.tree.JCTree;
 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
 import com.sun.tools.javac.util.JCDiagnostic;
+import java.util.EnumSet;
 
 import static com.sun.tools.javac.code.TypeTags.*;
 
@@ -292,6 +292,11 @@
     public Type instantiateExpr(Env<AttrContext> env, ForAll that,
                                 Type to,
                                 Warner warn) throws InferenceException {
+        return instantiateExpr(env, that, to, warn, ForAll.InstantiationPhase.CHECK);
+    }
+    public Type instantiateExpr(Env<AttrContext> env, ForAll that,
+                                Type to,
+                                Warner warn, ForAll.InstantiationPhase phase) throws InferenceException {
         List<Type> undetvars = Type.map(that.tvars, fromTypeVarFun);
         for (List<Type> l = undetvars; l.nonEmpty(); l = l.tail) {
             UndetVar uv = (UndetVar) l.head;
@@ -310,9 +315,22 @@
             types.boxedClass(to).type :
             to; //this is a hack
         Type qtype1 = types.subst(that.qtype, that.tvars, undetvars);
-        boolean works = types.isFunctionType(qtype1) && to2.tag == CLASS ?
-            types.isConvertible(env, qtype1, to2, warn) :
-            types.isSubtype(qtype1, to2);
+        boolean works = false;
+        if (types.isFunctionType(qtype1) && to2.tag == CLASS) {
+            //we need conversion - not subtyping (function type needs SAM conversion)
+            works = types.isConvertible(env, qtype1, to2, warn);
+            //inference of function types should take into account lower bounds
+            //(because of contravarance of function type argument types)
+            for (List<Type> l = undetvars; l.nonEmpty(); l = l.tail) {
+                UndetVar uv = (UndetVar) l.head;
+                minimizeInst(uv, warn);
+                if (uv.inst.tag == BOT) {
+                    uv.inst = null;
+                }
+            }
+        } else {
+            works = types.isSubtype(qtype1, to2);
+        }
         if (!works) {
             throw unambiguousNoInstanceException
                 .setMessage("infer.no.conforming.instance.exists",
@@ -330,7 +348,7 @@
                     that.tvars,
                     instaniateAsUninferredVars(undetvars, that.tvars));
         }
-        return chk.checkType(warn.pos(), env, that.inst(targs, to, types), to);
+        return chk.checkType(warn.pos(), env, that.inst(targs, to, types, phase), to);
     }
     //where
     private List<Type> instaniateAsUninferredVars(List<Type> undetvars, List<Type> tvars) {
@@ -366,6 +384,7 @@
         final ListBuffer<Type> argUndetvars = ListBuffer.lb();
         List<Type> undetvars = Type.map(tvars, fromTypeVarFun);
         List<Type> formals = mt.argtypes;
+        boolean forcePostCheck = false;
         //need to capture exactly once - otherwise subsequent
         //applicability checks might fail
         ListBuffer<Type> capturedArgs = ListBuffer.lb();
@@ -386,6 +405,7 @@
                 Type actualNoCapture = actualsNoCapture.head.baseType();
                 Type undetFormal = types.subst(formal, tvars, undetvars);
                 if (actual.tag == FORALL) {
+                    forcePostCheck = true;
                     //improvement - go ahead with method conversion check but create
                     //a 'fake' actual arg, where tvars are replaced with undet var
                     final ForAll fa = (ForAll)actual;
@@ -442,6 +462,7 @@
 
 
                     if (actual.tag == FORALL) {
+                        forcePostCheck = true;
                         //improvement - go ahead with method conversion check but create
                         //a 'fake' actual arg, where tvars are replaced with undet var
                         final ForAll fa = (ForAll)actual;
@@ -519,7 +540,7 @@
 
         mt = (MethodType)types.subst(mt, tvars, insttypes.toList());
 
-        if (!restvars.isEmpty()) {
+        if (!restvars.isEmpty() || forcePostCheck) {
             // if there are uninstantiated variables,
             // quantify result type with them
             final List<Type> inferredTypes = insttypes.toList();
@@ -529,11 +550,11 @@
             mt2.restype = new ForAll(restvars.toList(), mt.restype) {
                 @Override
                 public List<Type> getConstraints(TypeVar tv, ConstraintKind ck) {
-                    return Infer.this.getConstraints(restundet.toList(), tv, ck, all_tvars, inferredTypes);
+                    return Infer.this.getConstraints(restundet.toList(), tv, EnumSet.of(ck), all_tvars, inferredTypes);
                 }
 
                 @Override
-                public Type inst(List<Type> inferred, Type to, Types types) throws NoInstanceException {
+                public Type inst(List<Type> inferred, Type to, Types types, ForAll.InstantiationPhase phase) throws NoInstanceException {
                     List<Type> inferred2 = inferred;
                     for (Type t : restundet.toList()) {
                         //this has the side-effect of instantiating any
@@ -545,7 +566,7 @@
                     }
                     List<Type> formals = types.subst(mt2.argtypes, tvars, inferred);
                     if (!rs.argumentsAcceptable(env, capturedArgs2, formals,
-                           allowBoxing, useVarargs, warn)) {
+                           allowBoxing, useVarargs, warn, phase)) {
                       // inferred method is not applicable
                       throw invalidInstanceException.setMessage("inferred.do.not.conform.to.params", formals, argtypes);
                     }
@@ -558,11 +579,12 @@
                     if (env.tree.getTag() == JCTree.APPLY) {
                         ((JCMethodInvocation)env.tree).meth.type = types.subst(mt2, tvars, inferred);
                     }
-                    return super.inst(inferred, to, types);
+                    return super.inst(inferred, to, types, phase);
             }};
             return mt2;
         }
-        else if (!rs.argumentsAcceptable(env, capturedArgs.toList(), types.subst(mt.getParameterTypes(), tvars, insttypes.toList()), allowBoxing, useVarargs, warn)) {
+        else if (!rs.argumentsAcceptable(env, capturedArgs.toList(), types.subst(mt.getParameterTypes(), tvars, insttypes.toList()),
+                allowBoxing, useVarargs, warn, ForAll.InstantiationPhase.RESOLUTION)) {
             // inferred method is not applicable
             throw invalidInstanceException.setMessage("inferred.do.not.conform.to.params", mt.getParameterTypes(), argtypes);
         }
@@ -576,27 +598,44 @@
         return new ForAll(fa.tvars, fa.qtype) {
             @Override
             public List<Type> getConstraints(TypeVar tv, ConstraintKind ck) {
-                return Infer.this.getConstraints(undetvars, tv, ck, fa.tvars, undetvars);
+                return Infer.this.getConstraints(undetvars, tv, EnumSet.of(ck), fa.tvars, undetvars);
             }
             @Override
-            public Type inst(List<Type> actuals, Type to, Types types) {
-                return fa.inst(actuals, to, types);
+            public Type inst(List<Type> actuals, Type to, Types types, ForAll.InstantiationPhase phase) {
+                return fa.inst(actuals, to, types, phase);
             }
         };
     }
 
-    private List<Type> getConstraints(List<Type> undetvars, TypeVar tv, ConstraintKind ck, List<Type> tvars, List<Type> inferred) {
+    private List<Type> getConstraints(List<Type> undetvars, TypeVar tv, EnumSet<ConstraintKind> constraints, List<Type> tvars, List<Type> inferred) {
         for (Type t : undetvars) {
             UndetVar uv = (UndetVar)t;
             if (uv.qtype == tv) {
-                switch (ck) {
-                    case EXTENDS: return uv.hibounds.appendList(types.subst(types.getBounds(tv), tvars, inferred));
-                    case SUPER: return uv.lobounds;
-                    case EQUAL: {
-                        while (uv.inst != null && uv.inst.tag == UNDETVAR) {
-                            uv = (UndetVar)uv.inst;
+                for (ConstraintKind ck : constraints) {
+                    switch (ck) {
+                        case EXTENDS:
+                            ListBuffer<Type> hibounds = ListBuffer.lb();
+                            for (Type t2 : uv.hibounds) {
+                                hibounds.appendList(t2.tag == UNDETVAR ?
+                                    getConstraints(List.of(t2), tv, EnumSet.of(ck, ConstraintKind.EQUAL), tvars, inferred) :
+                                    uv.hibounds);
+                            }
+                            return types.subst(hibounds.appendList(types.getBounds(tv)).toList(), tvars, inferred);
+                        case SUPER:
+                            ListBuffer<Type> lobounds = ListBuffer.lb();
+                            for (Type t2 : uv.lobounds) {
+                                lobounds.appendList(t2.tag == UNDETVAR ?
+                                        getConstraints(List.of(t2), tv, EnumSet.of(ck, ConstraintKind.EQUAL), tvars, inferred) :
+                                        uv.lobounds);
+                            }
+                            return types.subst(lobounds.toList(), tvars, inferred);
+                        case EQUAL: {
+                            if (uv.inst == null) return List.nil();
+                            else
+                                return uv.inst.tag == UNDETVAR ?
+                                    getConstraints(List.of(uv.inst), tv, EnumSet.of(ck), tvars, inferred) :
+                                    List.of(types.subst(uv.inst, tvars, inferred));
                         }
-                        return uv.inst != null ? List.of(uv.inst) : List.<Type>nil();
                     }
                 }
             }
@@ -611,10 +650,10 @@
      */
     public Type instantiateArg(Env<AttrContext> env, ForAll that,
                                 Type to,
-                                Warner warn) throws InferenceException {
+                                Warner warn, ForAll.InstantiationPhase phase) throws InferenceException {
         Type inferredArg = null;
         try {
-            inferredArg = instantiateExpr(env, that, to, warn);
+            inferredArg = instantiateExpr(env, that, to, warn, phase);
         }
         catch (Infer.InferenceException e) {
             inferredArg = syms.errType;
--- a/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Thu Nov 11 14:47:39 2010 +0000
+++ b/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Fri Nov 12 12:49:12 2010 +0000
@@ -385,7 +385,7 @@
                                     warn);
 
         checkRawArgumentsAcceptable(env, argtypes, mt.getParameterTypes(),
-                                allowBoxing, useVarargs, warn);
+                                allowBoxing, useVarargs, warn, ForAll.InstantiationPhase.RESOLUTION);
         return mt;
     }
 
@@ -422,8 +422,16 @@
                                 boolean allowBoxing,
                                 boolean useVarargs,
                                 Warner warn) {
+        return argumentsAcceptable(env, argtypes, formals, allowBoxing, useVarargs, warn, ForAll.InstantiationPhase.CHECK);
+    }
+    boolean argumentsAcceptable(Env<AttrContext> env, List<Type> argtypes,
+                                List<Type> formals,
+                                boolean allowBoxing,
+                                boolean useVarargs,
+                                Warner warn,
+                                ForAll.InstantiationPhase phase) {
 	try {
-            checkRawArgumentsAcceptable(env, argtypes, formals, allowBoxing, useVarargs, warn);
+            checkRawArgumentsAcceptable(env, argtypes, formals, allowBoxing, useVarargs, warn, phase);
             return true;
         } catch (InapplicableMethodException ex) {
             return false;
@@ -433,7 +441,8 @@
                                 List<Type> formals,
                                 boolean allowBoxing,
                                 boolean useVarargs,
-                                Warner warn) {
+                                Warner warn,
+                                ForAll.InstantiationPhase phase) {
         if (argtypes == Type.noTypes) return;
         Type varargsFormal = useVarargs ? formals.last() : null;
         if (varargsFormal == null &&
@@ -443,7 +452,7 @@
 
         while (argtypes.nonEmpty() && formals.head != varargsFormal) {
             Type actual = argtypes.head.tag == FORALL ?
-                infer.instantiateArg(env, (ForAll)argtypes.head, formals.head, warn) :
+                infer.instantiateArg(env, (ForAll)argtypes.head, formals.head, warn, phase) :
                 argtypes.head;
             boolean works = false;
             JCDiagnostic problem = null;
@@ -473,7 +482,7 @@
             Type elt = types.elemtype(varargsFormal);
     	    while (argtypes.nonEmpty()) {
     	        Type actual = argtypes.head.tag == FORALL ?
-    	            infer.instantiateArg(env, (ForAll)argtypes.head, elt, warn) :
+    	            infer.instantiateArg(env, (ForAll)argtypes.head, elt, warn, phase) :
                     argtypes.head;
                 Types.ConversionResult res = types.isConvertible(actual, elt, warn);
     	        if (!res.check(env)) {
--- a/test/tools/javac/lambda/TargetType01.out	Thu Nov 11 14:47:39 2010 +0000
+++ b/test/tools/javac/lambda/TargetType01.out	Fri Nov 12 12:49:12 2010 +0000
@@ -1,4 +1,3 @@
+TargetType01.java:45:9: compiler.err.ref.ambiguous: M, kindname.method, M(TargetType01.F_I_I), TargetType01, kindname.method, M(TargetType01.F_S_S), TargetType01
 TargetType01.java:45:33: compiler.err.ref.ambiguous: M, kindname.method, M(TargetType01.F_I_I), TargetType01, kindname.method, M(TargetType01.F_S_S), TargetType01
-TargetType01.java:45:34: compiler.err.prob.found.req: (compiler.misc.incompatible.types), java.lang.Integer, java.lang.String
-TargetType01.java:45:9: compiler.err.ref.ambiguous: M, kindname.method, M(TargetType01.F_I_I), TargetType01, kindname.method, M(TargetType01.F_S_S), TargetType01
-3 errors
+2 errors
--- a/test/tools/javac/lambda/TargetType15.java	Thu Nov 11 14:47:39 2010 +0000
+++ b/test/tools/javac/lambda/TargetType15.java	Fri Nov 12 12:49:12 2010 +0000
@@ -34,7 +34,17 @@
         T foo(T a, T b);
     }
 
+    void m1(SAM<? extends String> f_1) {}
+    void m2(SAM<? super String> f_2) {}
+    void m3(SAM<?> f_3) {}
+
     SAM<? extends String> f_1 = #{ a, b -> a };
-    //SAM<? super String> f_2 = #{ a, b -> a }; //disabled as this is problematic
+    SAM<? super String> f_2 = #{ a, b -> a };
     SAM<?> f_3 = #{ a, b -> a };
+
+    {
+        m1(#{ a, b -> a });
+        m2(#{ a, b -> a });
+        m3(#{ a, b -> a });
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/TargetType16.java	Fri Nov 12 12:49:12 2010 +0000
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2010, 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
+ * @summary Bad recovery from ambiguous overload resolution
+ * @author  Maurizio Cimadamore
+ * @compile/fail/ref=TargetType16.out -XDrawDiagnostics TargetType16.java
+ */
+
+class TargetType16 {
+
+
+    interface SAM1 {
+        void m1();
+    }
+
+    interface SAM2<X> {
+        X m2();
+    }
+
+    void m(SAM1 s1) { }
+    <T> void m(SAM2<T> s2) { }
+
+    {
+        m(#{ System.out.println("Hello!") }); //ambiguity here
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/TargetType16.out	Fri Nov 12 12:49:12 2010 +0000
@@ -0,0 +1,2 @@
+TargetType16.java:46:9: compiler.err.ref.ambiguous: m, kindname.method, m(TargetType16.SAM1), TargetType16, kindname.method, <T>m(TargetType16.SAM2<T>), TargetType16
+1 error