changeset 1051:33a9a25c7347

Improvements: *) Switch to a more local type-inference process. Inference variables are not propagated outwards anymore. This allows for more predictable inference results and better error messages *) added -XDcomplexinference flag to enable more aggressive inference options, such as (i) handling of cycles in inference constraints (i.e. when a lambda expression returns a poly expression and target SAM type contains inference variables), (ii) enabling target-type inference for diamond/generic method calls in method context.
author mcimadamore
date Mon, 04 Jul 2011 13:54:48 +0100
parents 495ee57deb1e
children 46942593309e
files src/share/classes/com/sun/tools/javac/code/Printer.java 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 src/share/classes/com/sun/tools/javac/main/OptionName.java src/share/classes/com/sun/tools/javac/main/RecognizedOptions.java test/tools/javac/lambda/ExceptionTransparency04.out test/tools/javac/lambda/TargetType10.java test/tools/javac/lambda/TargetType10.out test/tools/javac/lambda/TargetType14.out test/tools/javac/lambda/TargetType24.out test/tools/javac/lambda/TargetType27.java test/tools/javac/lambda/TargetType27.out test/tools/javac/lambda/TargetType28.java test/tools/javac/lambda/TargetType28.out test/tools/javac/lambda/TargetType28_b.out
diffstat 18 files changed, 480 insertions(+), 299 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/code/Printer.java	Thu Jun 23 14:28:57 2011 +0100
+++ b/src/share/classes/com/sun/tools/javac/code/Printer.java	Mon Jul 04 13:54:48 2011 +0100
@@ -162,7 +162,7 @@
 
     @Override
     public String visitForAll(ForAll t, Locale locale) {
-        return "?" + visit(t.qtype, locale);
+        return visit(t.qtype, locale);
     }
 
     @Override
--- a/src/share/classes/com/sun/tools/javac/code/Type.java	Thu Jun 23 14:28:57 2011 +0100
+++ b/src/share/classes/com/sun/tools/javac/code/Type.java	Mon Jul 04 13:54:48 2011 +0100
@@ -30,6 +30,7 @@
 import com.sun.tools.javac.code.Symbol.*;
 import com.sun.tools.javac.comp.AttrContext;
 import com.sun.tools.javac.comp.Env;
+import com.sun.tools.javac.comp.Infer.InferenceContext;
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 
 import javax.lang.model.type.*;
@@ -1198,7 +1199,7 @@
          * @param check ForAll instantiation phase
          * @return instantiated type
          */
-        public abstract Type complete(Type to, boolean allowBoxing, boolean check);
+        public abstract Type complete(InferenceContext inferenceContext, Type to, boolean allowBoxing, boolean check);
 
         @Override
         public int getPolyTag() {
--- a/src/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Jun 23 14:28:57 2011 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java	Mon Jul 04 13:54:48 2011 +0100
@@ -44,6 +44,7 @@
 import com.sun.tools.javac.tree.JCTree.*;
 import com.sun.tools.javac.code.Type.ForAll.*;
 import com.sun.tools.javac.code.Type.*;
+import com.sun.tools.javac.comp.Infer.InferenceContext;
 
 import com.sun.source.tree.IdentifierTree;
 import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
@@ -1884,7 +1885,7 @@
             if (!diamondResolution) {
                 //if this is not a diamond expression, close the poly type
                 //as there's no dependency on the expected return type
-                check(tree, tree.constructorType.getReturnType(), VAL, pkind, syms.voidType);
+                chk.checkType(tree, localEnv, tree.constructorType.getReturnType(), syms.voidType);
             }
         }
         finally {
@@ -1923,13 +1924,13 @@
             final ForAll uninferredReturn = (ForAll)tree.constructorType.getReturnType();
             final ForAll fa = new ForAll(POLY_DIAMOND, TreeInfo.isDiamond(tree) ? uninferredReturn.qtype : clazztype) {
                 @Override
-                public Type complete(Type to, boolean allowBoxing, boolean check) {
-                    Type inferredType = uninferredReturn.complete(to, allowBoxing, check);
+                public Type complete(InferenceContext inferenceContext, Type to, boolean allowBoxing, boolean check) {
+                    Type inferredType = uninferredReturn.complete(inferenceContext, to, allowBoxing, check);
                     if (check) {
                         qtype = finishAttribDiamond(localEnv, tree, inferredType,
                                 clazztype, to, argtypes, typeargtypes);
                     }
-                    return qtype;
+                    return inferredType;
                 }
             };
             return fa;
@@ -2164,8 +2165,8 @@
             }
 
             @Override
-            Type deferredAttr(Env<AttrContext> attrEnv, JCLambda treeToCheck, Type pt, boolean allowBoxing, boolean check) {
-                return attribLambda(attrEnv, treeToCheck, pt, allowBoxing, check, this);
+            Type deferredAttr(Env<AttrContext> attrEnv, InferenceContext inferenceContext, JCLambda treeToCheck, Type pt, boolean allowBoxing, boolean check) {
+                return attribLambda(attrEnv, inferenceContext, treeToCheck, pt, allowBoxing, check, this);
             }
         };
         result = check(that, owntype, VAL, pkind, pt);
@@ -2216,7 +2217,7 @@
      * Attribute a lambda expression - return type/thrown types of the lambda
      * expression are inferred from the lambda body.
      */
-    Type attribLambda(Env<AttrContext> env, JCLambda that, Type target,
+    Type attribLambda(Env<AttrContext> env, final InferenceContext inferenceContext, JCLambda that, Type target,
             final boolean allowBoxing, final boolean check, SAMDeferredAttribution<JCLambda> deferredAttr) {
         
         //create an environment for attribution of the lambda expression
@@ -2246,23 +2247,10 @@
         
         ForAll.Completer prevPolyCompleter = chk.setPolyCompleter(new ForAll.Completer() {
             public Type complete(DiagnosticPosition pos, Env<AttrContext> env, ForAll t, Type pt, Warner warn) throws Infer.NoInstanceException {
-                if (pt == Infer.lambdaPoly) {
-                    switch (t.getPolyTag()) {
-                        case POLY_LAMBDA:
-                        case POLY_REFERENCE:
-                            Type res = infer.instantiatePoly(env, t, localEnv.enclMethod.type.getReturnType(), warn, allowBoxing, check);
-                            if (res == null) {
-                                //the lambda expression/reference was not checked
-                                //because the target contains inference variables
-                                throw new Infer.InferenceException(diags).setMessage("cyclic.lambda.inference");
-                            } else {
-                                return res;
-                            }
-                        default: return t.complete(localEnv.enclMethod.type.getReturnType(), allowBoxing, check);
-                    }
-                } else {
-                    return chk.instantiatePoly(pos, env, t, pt, warn);
-                }
+                Type expectedType = localEnv.enclMethod.type.getReturnType();
+                return pt == Infer.lambdaPoly ?
+                    infer.instantiatePoly(env, inferenceContext, t, expectedType, warn, allowBoxing, check) :
+                    chk.instantiatePoly(pos, env, t, pt, warn);
             }
         });
 
@@ -2290,7 +2278,7 @@
             thrownTypes = that.inferredThrownTypes;
         }
 
-        checkSAMCompatible(target, returnTypes, TreeInfo.types(that.params), thrownTypes, allowBoxing, that.canCompleteNormally);
+        checkSAMCompatible(inferenceContext, target, returnTypes, TreeInfo.types(that.params), thrownTypes, allowBoxing, that.canCompleteNormally);
         
         return that.targetType = target;
     }
@@ -2644,8 +2632,8 @@
                     initialParameterTypes;
             }
             @Override
-            Type deferredAttr(Env<AttrContext> attrEnv, JCMemberReference treeToCheck, Type pt, boolean allowBoxing, boolean check) {
-                return attribMethodReference(attrEnv, treeToCheck, pt, allowBoxing);
+            Type deferredAttr(Env<AttrContext> attrEnv, InferenceContext inferenceContext, JCMemberReference treeToCheck, Type pt, boolean allowBoxing, boolean check) {
+                return attribMethodReference(attrEnv, inferenceContext, treeToCheck, pt, allowBoxing);
             }
 
             @Override
@@ -2667,7 +2655,7 @@
     }
 
     //where
-    private Type attribMethodReference(Env<AttrContext> localEnv, JCMemberReference tree, Type to, boolean allowBoxing) {
+    private Type attribMethodReference(Env<AttrContext> localEnv, InferenceContext inferenceContext, JCMemberReference tree, Type to, boolean allowBoxing) {
         Assert.check(tree.mode == JCMemberReference.ReferenceMode.INVOKE ||
                 tree.mode == JCMemberReference.ReferenceMode.NEW);
 
@@ -2726,7 +2714,7 @@
         Type mtype = types.memberType(tree.expr.type, tree.sym);
         Type returnType = tree.getMode() == ReferenceMode.INVOKE ?
                 mtype.getReturnType() : tree.expr.type;
-        checkSAMCompatible(to, List.of(returnType), samDesc.getParameterTypes(), mtype.getThrownTypes(), allowBoxing, true);
+        checkSAMCompatible(inferenceContext, to, List.of(returnType), samDesc.getParameterTypes(), mtype.getThrownTypes(), allowBoxing, true);
         return to;
     }
 
@@ -2738,9 +2726,9 @@
      * thrown types must be 'included' in the thown types list of the expected
      * SAM descriptor.
      */
-    void checkSAMCompatible(Type sam, List<Type> resultTypes, List<Type> argtypes, List<Type> thrownTypes, boolean allowBoxing, boolean completesNormally) {
+    void checkSAMCompatible(InferenceContext inferenceContext, Type sam, List<Type> resultTypes, List<Type> argtypes, List<Type> thrownTypes, boolean allowBoxing, boolean completesNormally) {
         Types.SAMResult samRes = types.findSAM(sam, env);
-        Type samDescriptor = samRes.getTargetType();
+        Type samDescriptor = inferenceContext.asUndetType(samRes.getTargetType(), types);
 
         if (completesNormally &&
                 resultTypes.length() == 0 &&
@@ -2748,7 +2736,7 @@
            throw new Infer.InvalidInstanceException(diags).setMessage("infer.incompatible.ret.types.in.lambda", syms.voidType);
         } else {
             for (Type resType : resultTypes) {
-                if (!isReturnCompatible(env, resType, samDescriptor.getReturnType(), allowBoxing)) {
+                if (!resType.isErroneous() && !isReturnCompatible(env, resType, samDescriptor.getReturnType(), allowBoxing)) {
                     throw new Infer.InvalidInstanceException(diags).setMessage("infer.incompatible.ret.types.in.lambda", resType);
                 }
             }
@@ -2772,8 +2760,6 @@
      * convert t to s via subtyping or assignment conversion.
      */
     public boolean isReturnCompatible(Env<AttrContext> env, Type t, Type s, boolean allowBoxing) {
-        t = infer.asUndetType(t);
-
         return t.tag == NONE || //can't complete normally
                 t.tag == VOID && isVoidCompatible(s) ||
                 s.tag == VOID && isVoidCompatible(t) ||
@@ -2801,22 +2787,16 @@
             this.tree = tree;
         }
 
-        public Type complete(final Type to, boolean allowBoxing, boolean check) {
+        public Type complete(InferenceContext inferenceContext, final Type to, boolean allowBoxing, boolean check) {
             if (completed) return qtype;
-            Type targetType = null;
-            try {
-                targetType = instantiateSAM(to, getParameterTypes());
-            }
-            catch (Infer.InferenceException ex) {
-                throw new Infer.InferenceException(diags).setMessage("no.suitable.sam.inst", to);
-            }
+            Type targetType = instantiateSAM(to, getParameterTypes());
             T treeToCheck = check ?
                 tree :
                 new TreeCopier<Object>(make).copy(tree);
             boolean prevDeferDiagnostics = log.deferDiagnostics;
             try {
                 log.deferDiagnostics = !check;
-                Type owntype = deferredAttr(baseEnv.dup(tree), treeToCheck, targetType, allowBoxing, check);
+                Type owntype = deferredAttr(baseEnv.dup(tree), inferenceContext, treeToCheck, targetType, allowBoxing, check);
                 if (check) {
                     qtype = owntype;
                     completed = true;
@@ -2845,7 +2825,7 @@
             return infer.inferSAM(env, to, paramTypes);
         }
 
-        abstract Type deferredAttr(Env<AttrContext> attrEnv, T t, Type pt, boolean allowBoxing, boolean check);
+        abstract Type deferredAttr(Env<AttrContext> attrEnv, InferenceContext inferenceContext, T t, Type pt, boolean allowBoxing, boolean check);
     }
 
     public void visitSelect(JCFieldAccess tree) {
@@ -2887,6 +2867,13 @@
             sitesym != null &&
             sitesym.name == names._super;
 
+        // If selected expression is polymorphic, strip
+        // type parameters and remember in env.info.tvars, so that
+        // they can be added later (in Attr.checkId and Infer.instantiateMethod).
+        if (tree.selected.type.getPolyTag() != NO_POLY) {
+            Assert.error();
+        }
+
         // Determine the symbol represented by the selection.
         env.info.varArgs = false;
         Symbol sym = selectSym(tree, sitesym, site, env, pt, pkind);
--- a/src/share/classes/com/sun/tools/javac/comp/Check.java	Thu Jun 23 14:28:57 2011 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Check.java	Mon Jul 04 13:54:48 2011 +0100
@@ -40,13 +40,12 @@
 import com.sun.tools.javac.code.Lint.LintCategory;
 import com.sun.tools.javac.code.Type.*;
 import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.comp.Infer.InferenceContext;
 
 import static com.sun.tools.javac.code.Flags.*;
 import static com.sun.tools.javac.code.Kinds.*;
 import static com.sun.tools.javac.code.TypeTags.*;
 
-import static com.sun.tools.javac.main.OptionName.*;
-
 /** Type checking helper class for the attribution phase.
  *
  *  <p><b>This is NOT part of any supported API.
@@ -111,7 +110,7 @@
         allowCovariantReturns = source.allowCovariantReturns();
         allowSimplifiedVarargs = source.allowSimplifiedVarargs();
         allowDefenderMethods = source.allowDefenderMethods();
-        complexInference = options.isSet(COMPLEXINFERENCE);
+        complexInference = options.isSet("complexinference");
         skipAnnotations = options.isSet("skipAnnotations");
         warnOnSyntheticConflicts = options.isSet("warnOnSyntheticConflicts");
         suppressAbortOnBadClassFile = options.isSet("suppressAbortOnBadClassFile");
@@ -469,9 +468,8 @@
     //where
         class CheckPolyCompleter implements ForAll.Completer {
             public Type complete(DiagnosticPosition pos, Env<AttrContext> env, ForAll t, Type pt, Warner warn) throws Infer.NoInstanceException {
-
                 if (pt == Infer.anyPoly &&
-                        (t.getPolyTag() == POLY_LAMBDA ||
+                        (complexInference || t.getPolyTag() == POLY_LAMBDA ||
                         t.getPolyTag() == POLY_REFERENCE)) {
                     return t;
                 } else if (pt.tag == NONE) {
@@ -494,7 +492,7 @@
                                         t, pt);
                             }
                             try {
-                                return t.complete(pt, true, true);
+                                return t.complete(InferenceContext.emptyContext, pt, true, true);
                             }
                             catch (Infer.InferenceException ex) {
                                 return typeError(pos,
@@ -503,7 +501,7 @@
                             }
                         case POLY_RETURN:
                             try {
-                                Type typeToCheck = t.complete(pt, true, true);
+                                Type typeToCheck = t.complete(InferenceContext.emptyContext, pt, true, true);
                                 return checkType(warn.pos(), env, typeToCheck, pt);
                             } catch (Infer.NoInstanceException ex) {
                                 JCDiagnostic d = ex.getDiagnostic();
@@ -517,7 +515,7 @@
                             }
                         case POLY_DIAMOND:
                             try {
-                                Type typeToCheck = t.complete(pt, true, true);
+                                Type typeToCheck = t.complete(InferenceContext.emptyContext, pt, true, true);
                                 return checkType(warn.pos(), env, typeToCheck, pt);
                             } catch (Infer.InferenceException ex) {
                                 //an error occurred while inferring uninstantiated type-variables
--- a/src/share/classes/com/sun/tools/javac/comp/Infer.java	Thu Jun 23 14:28:57 2011 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Infer.java	Mon Jul 04 13:54:48 2011 +0100
@@ -80,7 +80,8 @@
             new NoInstanceException(false, diags);
         invalidInstanceException =
             new InvalidInstanceException(diags);
-
+        cyclicInferenceException =
+            new CyclicInferenceException(diags);
     }
 
     public static class InferenceException extends Resolve.InapplicableMethodException {
@@ -110,23 +111,23 @@
         }
     }
 
+    public static class CyclicInferenceException extends InferenceException {
+        private static final long serialVersionUID = 3;
+
+        CyclicInferenceException(JCDiagnostic.Factory diags) {
+            super(diags);
+        }
+    }
+
     private final NoInstanceException ambiguousNoInstanceException;
     private final NoInstanceException unambiguousNoInstanceException;
     private final InvalidInstanceException invalidInstanceException;
+    private final CyclicInferenceException cyclicInferenceException;
 
 /***************************************************************************
  * Auxiliary type values and classes
  ***************************************************************************/
 
-    /** A mapping that turns type variables into undetermined type variables.
-     */
-    Mapping fromTypeVarFun = new Mapping("fromTypeVarFun") {
-            public Type apply(Type t) {
-                if (t.tag == TYPEVAR) return new UndetVar(t);
-                else return t.map(this);
-            }
-        };
-
     /** A mapping that returns its type argument with every UndetVar replaced
      *  by its `inst' field. Throws a NoInstanceException
      *  if this not possible because an `inst' field is null.
@@ -198,28 +199,7 @@
             return constraintScanner.visit(uv);
         }
     };
-
-    /** A mapping that returns its type argument with every UndetVar replaced
-     *  by its `inst' field. If 'inst' is 'null', the mapping returns the
-     *  type-variable 'hidden' behind the UndetVar (what about loops?)
-     */
-
-    Mapping partialInstFun = new Mapping("partialInstFun") {
-            public Type apply(Type t) {
-                switch (t.tag) {
-                    case UNKNOWN:
-                        throw ambiguousNoInstanceException
-                            .setMessage("undetermined.type");
-                    case UNDETVAR:
-                        UndetVar that = (UndetVar) t;
-                        return (that.inst == null || that.inst.tag == BOT)
-                                ? that.qtype : that.inst.map(this);
-                    default:
-                        return t.map(this);
-                }
-            }
-    };
-
+    
 /***************************************************************************
  * Mini/Maximization of UndetVars
  ***************************************************************************/
@@ -341,21 +321,22 @@
      *  If a maximal instantiation exists the instantiate type-arguments are
      *  returned.
      */
-    public List<Type> instantiateUninferred(Env<AttrContext> env, Warner warn) throws InferenceException {
-        for (List<Type> l = undetVars(); l.nonEmpty(); l = l.tail)
+    public void instantiateUninferred(InferenceContext inferenceContext, Warner warn) throws InferenceException {
+        for (List<Type> l = inferenceContext.undetVars(); l.nonEmpty(); l = l.tail)
             maximizeInst((UndetVar) l.head, warn);
         // check bounds
-        List<Type> targs = Type.map(undetVars(), getInstFun);
-        if (Type.containsAny(targs, inferenceVars())) {
+        List<Type> targs = Type.map(inferenceContext.undetVars(), getInstFun);
+        if (Type.containsAny(targs, inferenceContext.inferenceVars())) {
             //replace uninferred type-vars
             targs = types.subst(targs,
-                    inferenceVars(),
-                    instaniateAsUninferredVars(undetVars(), inferenceVars()));
+                    inferenceContext.inferenceVars(),
+                    instaniateAsUninferredVars(inferenceContext.undetVars(), inferenceContext.inferenceVars()));
         }
         // check that inferred bounds conform to their bounds
-        checkWithinBounds(inferenceVars(), targs, warn);
-        return targs;
+        checkWithinBounds(inferenceContext.inferenceVars(), targs, warn);
+        inferenceContext.setInferredTypes(types.subst(inferenceContext.inferredTypes(), inferenceContext.inferenceVars(), targs));
     }
+
     //where
     private List<Type> instaniateAsUninferredVars(List<Type> undetvars, List<Type> tvars) {
         ListBuffer<Type> new_targs = ListBuffer.lb();
@@ -376,6 +357,88 @@
     }
     // </editor-fold>
 
+    // <editor-fold desc="cyclic inference vars instantiation">
+
+    /**
+     * This method instantiates a subset of the inference variables in a given
+     * inference context, such that the list of the input types can be fully
+     * instantiated.
+     */
+    public void instantiateCyclic(InferenceContext inferenceContext, List<Type> ts, Warner warn) throws InferenceException {
+        CyclicVisitor cv = new CyclicVisitor(inferenceContext);
+        cv.visit(ts);
+        List<Type> cyclicUndet = inferenceContext.asUndetTypes(cv.cyclicVars, types);
+        //include upper bounds
+        for (Type t : cyclicUndet) {
+            UndetVar uv = (UndetVar)t;
+            List<Type> bounds = inferenceContext.asInstantiatedTypes(types.getBounds((TypeVar)uv.qtype), types);
+            if (Type.containsAny(bounds, cv.acyclicVars)) {
+                throw cyclicInferenceException.setMessage("cyclic.lambda.inference");
+            }
+            uv.hibounds = uv.hibounds.appendList(inferenceContext.asUndetTypes(bounds, types));
+        }
+        for (List<Type> l = cyclicUndet; l.nonEmpty(); l = l.tail)
+            maximizeInst((UndetVar) l.head, warn);
+
+        List<Type> targs = Type.map(cyclicUndet, getInstFun);
+        if (Type.containsAny(targs, cv.cyclicVars)) {
+            //replace uninferred type-vars
+            targs = types.subst(targs,
+                    cv.cyclicVars,
+                    instaniateAsUninferredVars(cyclicUndet, cv.cyclicVars));
+        }
+        inferenceContext.setInferredTypes(types.subst(inferenceContext.inferredTypes(), cv.cyclicVars, targs));
+    }
+
+    /**
+     * Scan a type and partition the inference context in two sets of variables;
+     * the ones occurring in the input type and the ones not occurring in the
+     * input types.
+     */
+    class CyclicVisitor extends Types.UnaryVisitor<Void> {
+
+        InferenceContext inferenceContext;
+        List<Type> cyclicVars = List.nil();
+        List<Type> acyclicVars;
+
+        public CyclicVisitor(InferenceContext inferenceContext) {
+            this.inferenceContext = inferenceContext;
+            this.acyclicVars = inferenceContext.inferenceVars();
+        }
+
+        public Void visitType(Type t, Void s) {
+            return null;
+        }
+
+        public Void visit(List<Type> ts) {
+            for (Type t : ts) {
+                visit(t);
+            }
+            return null;
+        }
+
+        @Override
+        public Void visitTypeVar(TypeVar t, Void s) {
+            if (!cyclicVars.contains(t) && inferenceContext.isInferenceVar(t)) {
+                cyclicVars = cyclicVars.append(t);
+                acyclicVars = List.filter(acyclicVars, t);
+            }
+            return null;
+        }
+
+        @Override
+        public Void visitClassType(ClassType t, Void s) {
+            return visit(t.getTypeArguments());
+        }
+
+        @Override
+        public Void visitWildcardType(WildcardType t, Void s) {
+            return visit(t.type);
+        }
+    };
+    // </editor-fold>
+    
+
     // <editor-fold desc="method instantiation routines">
     /**
      * Instantiate a method type. There are two cases in which a method type
@@ -407,81 +470,77 @@
                                   final List<Type> argtypes,
                                   final boolean allowBoxing,
                                   final boolean useVarargs,
-                                  boolean polyArgs,
+                                  boolean check,
                                   final Warner warn) throws InferenceException {
 
         //init
         List<Type> uncheckedArgs = List.nil();
         List<Type> prevUncheckedArgs;
+        final InferenceContext context = new InferenceContext(tvars);
 
         do {
             prevUncheckedArgs = uncheckedArgs;
-            uncheckedArgs = rs.checkRawArgumentsAcceptable(env, mt, argtypes,
+            uncheckedArgs = rs.checkRawArgumentsAcceptable(env, context, mt, argtypes,
                     allowBoxing, useVarargs, false, warn,
-                    inferenceVars().isEmpty() ? rs.resolveHandler : inferenceHandler);
+                    context.tvars.isEmpty() ? rs.resolveHandler : new InferenceCheckHandler(context));
                 
             // minimize as yet undetermined type variables
-            for (Type t : undetVars())
+            for (Type t : context.undetvars)
                 minimizeInst((UndetVar) t, warn);
 
             /** Type variables instantiated to bottom */
             ListBuffer<Type> restvars = new ListBuffer<Type>();
-
-            /** Undet vars instantiated to bottom */
-            final ListBuffer<Type> restundet = new ListBuffer<Type>();
-
+            
             /** Instantiated types or TypeVars if under-constrained */
             List<Type> insttypes = List.nil();
-
-            /** Instantiated types or UndetVars if under-constrained */
-            List<Type> undettypes = List.nil();
-
-            for (Type t : undetVars()) {
+            
+            for (Type t : context.undetvars) {
                 UndetVar uv = (UndetVar)t;
-                Type inst = uv.inst == null ? null : partialInstFun.apply(uv.inst);
                 if (uv.inst == null || uv.inst.tag == BOT) {
                      restvars.append(uv.qtype);
-                     restundet.append(uv);
                      insttypes = insttypes.append(uv.qtype);
-                     undettypes = undettypes.append(uv);
-                    uv.inst = null;
+                     uv.inst = null;
                  } else {
-                    insttypes = insttypes.append(inst);
-                    undettypes = undettypes.append(inst);
+                    insttypes = insttypes.append(uv.inst);
                  }
             }
 
-            checkWithinBounds(inferenceVars(), undettypes, warn);
-
-            mt = (MethodType)types.subst(mt, inferenceVars(), insttypes);
+            context.setInferredTypes(insttypes);
+            checkWithinBounds(context.tvars, context.asUndetTypes(context.inferredTypes(), types), warn);
 
             //if no progress has been made, terminate the loop
             if (prevUncheckedArgs.size() == uncheckedArgs.size() &&
                     uncheckedArgs.size() > 0) {
-                throw invalidInstanceException.setMessage("cyclic.lambda.inference");
+                if (chk.complexInference && context.mode() == InferenceContext.Mode.ERROR_ON_CYCLES) {
+                    context.setMode(InferenceContext.Mode.BREAK_CYCLES);
+                } else {
+                    throw invalidInstanceException.setMessage("cyclic.lambda.inference");
+                }
             }
             tvars = restvars.toList();
         }
         while (uncheckedArgs.size() > 0 && tvars.nonEmpty());
 
-        if (tvars.nonEmpty() || polyArgs) {
+        mt = (MethodType)context.asInstantiatedType(mt, types);
+
+        if (tvars.nonEmpty()) {
             // if there are uninstantiated variables,
             // quantify result type with them
-            
-            final List<Type> inference_vars = inferenceVars();
 
             //include upper bounds
-            for (Type t : undetVars()) {
+            for (Type t : context.undetVars()) {
                 UndetVar uv = (UndetVar)t;
-                uv.hibounds = uv.hibounds.appendList(asUndetTypes(types.getBounds((TypeVar)uv.qtype)));
+                uv.hibounds = uv.hibounds.appendList(context.asUndetTypes(types.getBounds((TypeVar)uv.qtype), types));
             }
 
             final ForAll uninferredMethodType = new ForAll(POLY_METHOD,
                     new MethodType(mt.getParameterTypes(), null, mt.getThrownTypes(), mt.tsym)) {
-                public Type complete(Type to, boolean allowBoxing, boolean check) {
+                public Type complete(InferenceContext unused, Type to, boolean allowBoxing, boolean check) {
                     // check that actuals conform to inferred formals
+                    Assert.check(check);
                     try {
-                        rs.checkRawArgumentsAcceptable(env, qtype, argtypes, allowBoxing, useVarargs, warn);
+                        qtype = context.asInstantiatedType(qtype, types);
+                        rs.checkRawArgumentsAcceptable(env, qtype, argtypes, allowBoxing, useVarargs, check, warn);
                     }
                     catch (Resolve.InapplicableMethodException ex) {
                         // inferred method is not applicable
@@ -495,34 +554,30 @@
             };
 
             ForAll uninferredReturn = new ForAll(POLY_RETURN, mt.getReturnType()) {
-                public Type complete(Type to, boolean allowBoxing, boolean check) {
-                    try {
-                        //add constraints from return type
-                        Type qtype1 = asUndetType(qtype);
-                        Type target = qtype1.tag == UNDETVAR ?
-                            types.boxedTypeOrType(to) : to;
-                        if (allowBoxing ?
-                            !types.isAssignable(qtype1, target) :
-                            !types.isSubtypeUnchecked(qtype1, target)) {
-                           throw unambiguousNoInstanceException
-                                   .setMessage("infer.no.conforming.instance.exists",
-                                               inferenceVars(), qtype, to);
-                        }
-                        if (check) {
-                            //instantiate all current inference variables...
-                            List<Type> actuals = instantiateUninferred(env, Warner.noWarnings);
-                            //...and compute instantiated signature of generic method
-                            qtype = types.subst(qtype, inference_vars, actuals);
-                            uninferredMethodType.qtype = types.subst(uninferredMethodType.qtype, inference_vars, actuals);
-                            uninferredMethodType.complete(to, allowBoxing, check);
-                        }
-                        return qtype;
+                public Type complete(InferenceContext pendingContext, Type to, boolean allowBoxing, boolean check) {
+                    //if in overload resolution mode, dup the inference environment
+                    //in order to avoid propagating constraints to other candidates
+                    InferenceContext localContext = check ? context : context.dup(types);
+                    //add constraints from return type
+                    Type qtype1 = localContext.asUndetType(qtype, types);
+                    Type target = qtype1.tag == UNDETVAR ?
+                        types.boxedTypeOrType(to) : to;
+                    if (allowBoxing ?
+                        !types.isAssignable(qtype1, target) :
+                        !types.isSubtypeUnchecked(qtype1, target)) {
+                       throw unambiguousNoInstanceException
+                               .setMessage("infer.no.conforming.instance.exists",
+                                           localContext.inferenceVars(), qtype, to);
                     }
-                    finally {
-                        if (check) {
-                            inferenceContexts.head.clear();
-                        }
+                    //instantiate all current inference variables...
+                    instantiateUninferred(localContext, Warner.noWarnings);
+                    //...and compute instantiated signature of generic method
+                    Type owntype = localContext.asInstantiatedType(qtype, types);
+                    if (check) {
+                        qtype = owntype;
+                        uninferredMethodType.complete(pendingContext, to, allowBoxing, check);
                     }
+                    return owntype;
                 }
             };
 
@@ -530,11 +585,9 @@
             return uninferredMethodType;
         }
         else {
-            //no rest vars - clear inference context
-            inferenceContexts.head.clear();
             try {
                 // check that actuals conform to inferred formals
-                rs.checkRawArgumentsAcceptable(env, mt, argtypes, allowBoxing, useVarargs, warn);
+                rs.checkRawArgumentsAcceptable(env, mt, argtypes, allowBoxing, useVarargs, check, warn);
             }
             catch (Resolve.InapplicableMethodException ex) {
                 // inferred method is not applicable
@@ -547,28 +600,36 @@
     //where
 
         /** inference check handler **/
-        Resolve.MethodCheckHandler inferenceHandler = new Resolve.MethodCheckHandler() {
-                public void arityMismatch() {
-                    throw unambiguousNoInstanceException.setMessage("infer.arg.length.mismatch");
+        class InferenceCheckHandler implements Resolve.MethodCheckHandler {
+            
+            InferenceContext inferenceContext;
+
+            public InferenceCheckHandler(InferenceContext inferenceContext) {
+                this.inferenceContext = inferenceContext;
+            }
+
+            public void arityMismatch() {
+                throw unambiguousNoInstanceException.setMessage("infer.arg.length.mismatch");
+            }
+            public void argumentMismatch(boolean varargs, Type found, Type expected, JCDiagnostic cause) {
+                String key = null;
+                if (varargs) {
+                    key = cause == null ?
+                        "infer.varargs.argument.mismatch" :
+                        "infer.varargs.argument.mismatch.1";
+                } else {
+                    key = cause == null ?
+                        "infer.no.conforming.assignment.exists" :
+                        "infer.no.conforming.assignment.exists.1";
                 }
-                public void argumentMismatch(boolean varargs, Type found, Type expected, JCDiagnostic cause) {
-                    String key = null;
-                    if (varargs) {
-                        key = cause == null ?
-                            "infer.varargs.argument.mismatch" :
-                            "infer.varargs.argument.mismatch.1";
-                    } else {
-                        key = cause == null ?
-                            "infer.no.conforming.assignment.exists" :
-                            "infer.no.conforming.assignment.exists.1";
-                    }
-                    throw unambiguousNoInstanceException.setMessage(key,
-                            inferenceVars(), found, expected, cause);
-                }
-                public void inaccessibleVarargs(Symbol location, Type expected) {
-                    //this kind of error should not be acted upon at this stage
-                }
-        };
+                throw unambiguousNoInstanceException.setMessage(key,
+                        inferenceContext.inferenceVars(), found, expected, cause);
+            }
+            public void inaccessibleVarargs(Symbol location, Type expected) {
+                throw unambiguousNoInstanceException.setMessage("inaccessible.varargs.type",
+                        expected, Kinds.kindName(location), location);
+            }
+        }
     // </editor-fold>
     
     // <editor-fold desc="SAM type instantiation">
@@ -584,19 +645,18 @@
             //(this means the target contains no wilddcards!)
             return samType;
         } else {
-            List<Type> tvars = samType.tsym.type.getTypeArguments();
             Type formalSam = samType.tsym.type;
+            InferenceContext inferenceContext = new InferenceContext(formalSam.getTypeArguments());
             if (paramTypes != null) {
-                pushContext(formalSam.getTypeArguments());
                 //get constraints from explicit params (this is done by
                 //checking that explicit param types are equal to the ones
                 //in the SAM descriptors)
-                Type undetSAM = asUndetType(formalSam);
+                Type undetSAM = inferenceContext.asUndetType(formalSam, types);
                 Type target = types.findSAM(undetSAM, env).getTargetType();
                 if (!types.isSameTypes(target.getParameterTypes(), paramTypes)) {
                     throw invalidInstanceException;
                 }
-                for (List<Type> l = undetVars(); l.nonEmpty(); l = l.tail) {
+                for (List<Type> l = inferenceContext.undetVars(); l.nonEmpty(); l = l.tail) {
                     UndetVar uv = (UndetVar) l.head;
                     if (uv.lobounds.nonEmpty())
                         minimizeInst(uv, Warner.noWarnings);
@@ -606,17 +666,15 @@
                         uv.inst = uv.qtype;
                     }
                 }
-                List<Type> targs = Type.map(undetVars(), getInstFun);
-                formalSam = types.subst(formalSam, inferenceVars(), targs);
-                //discard current context;
-                popContext(false);
+                inferenceContext.setInferredTypes(Type.map(inferenceContext.undetVars(), getInstFun));
+                formalSam = inferenceContext.asInstantiatedType(formalSam, types);
             }
             ListBuffer<Type> typeargs = ListBuffer.lb();
             List<Type> actualTypeargs = samType.getTypeArguments();
             //for remaining uninferred type-vars in the SAM type,
             //simply replace the wildcards with its bound
             for (Type t : formalSam.getTypeArguments()) {
-                if (tvars.contains(t) && actualTypeargs.head.tag == WILDCARD) {
+                if (inferenceContext.isInferenceVar(t) && actualTypeargs.head.tag == WILDCARD) {
                     WildcardType wt = (WildcardType)actualTypeargs.head;
                     Type bound = ((t.tsym.flags() & Flags.THROWS) != 0) &&
                             types.isSameType(wt.type, syms.objectType) ?
@@ -628,11 +686,15 @@
                 }
                 actualTypeargs = actualTypeargs.tail;
             }
-            Type owntype = types.subst(formalSam, tvars, typeargs.toList());
-            if (!chk.checkValidGenericType(owntype) || !types.isSubtypeUnchecked(owntype, samType)) {
+            List<Type> targs = types.subst(inferenceContext.inferredTypes(),
+                                           inferenceContext.inferenceVars(),
+                                           typeargs.toList());
+            inferenceContext.setInferredTypes(targs);
+            Type owntype = inferenceContext.asInstantiatedType(formalSam, types);
+            if (!chk.checkValidGenericType(owntype)) {
                 //if the inferred SAM type is not well-formed, or if it's not
                 //a subtype of the original target, issue an error
-                throw invalidInstanceException;
+                throw invalidInstanceException.setMessage("no.suitable.sam.inst", samType);
             }
             return owntype;
         }
@@ -643,48 +705,89 @@
     /**
      * Instantiate an actual argument type (whose kind is a poly type) using
      * the corresponding formal as target type. This could trigger poly type
-     * completion (and, as a consequence, deferred attribution).
+     * completion (and, as a consequence, deferred attribution). It is possible
+     * that the expected type might, in turn, contain inference variable from
+     * the pending inference/resolution context; depending on the inference
+     * policy, the compiler might try to break the inference cycle, or,
+     * alternatively, raise an error (the former option is only available
+     * with the -XDcomplexinference flag).
      */
     public class InferPolyCompleter implements ForAll.Completer {
-        
+
         boolean allowBoxing;
         boolean check;
+        InferenceContext inferenceContext;
 
-        public InferPolyCompleter(boolean allowBoxing, boolean check) {
+        public InferPolyCompleter(boolean allowBoxing, boolean check, InferenceContext inferenceContext) {
             this.allowBoxing = allowBoxing;
             this.check = check;
+            this.inferenceContext = inferenceContext;
         }
 
+        @SuppressWarnings("fallthrough")
         public Type complete(DiagnosticPosition pos, Env<AttrContext> env, ForAll t, Type pt, Warner warn) {
+            pt = inferenceContext.asInstantiatedType(pt, types);
             switch (t.getPolyTag()) {
+                case POLY_DIAMOND:
+                case POLY_RETURN:
+                    if (pt.containsAny(inferenceContext.inferenceVars())) {
+                        if (inferenceContext.mode() == InferenceContext.Mode.BREAK_CYCLES) {
+                            Assert.check(chk.complexInference);
+                            Type owntype = t.complete(inferenceContext, t.qtype.tag == VOID ? syms.voidType : syms.objectType, allowBoxing, check);
+                            Type undetFormal = inferenceContext.asUndetType(pt, types);
+                            boolean works = allowBoxing ?
+                                    types.isConvertible(owntype, undetFormal, warn) :
+                                    types.isSubtypeUnchecked(owntype, undetFormal, warn);
+                            if (!works) {
+                                throw invalidInstanceException;
+                            }
+                            return owntype;
+                        } else {
+                            throw cyclicInferenceException.setMessage("cyclic.lambda.inference");
+                        }
+                    } else {
+                        return t.complete(inferenceContext, pt, allowBoxing, check);
+                    }
+
+                case POLY_REFERENCE:
                 case POLY_LAMBDA:
-                case POLY_REFERENCE:
-                    if (inferenceVars().contains(pt) || undetVars().contains(pt)) {
-                        return null;
+                    if (inferenceContext.isInferenceVar(pt)) {
+                        throw cyclicInferenceException.setMessage("cyclic.lambda.inference");
                     }
                     //is the target a valid SAM?
                     Types.SAMResult samRes = types.findSAM(pt, env);
                     if (samRes.isErroneous()) {
-                        throw invalidInstanceException.setMessage(samRes.getDiagnostic(diags));
+                        throw new Infer.InvalidInstanceException(diags).setMessage(samRes.getDiagnostic(diags));
                     }
                     //complete the poly type iff either (i) the lambda/mref has
                     //explicit params, or (ii) the formals in the expected SAM desc
                     //do not contain any inference var
                     List<Type> expectedFormals = samRes.getTargetType().getParameterTypes();
-                    if (t.getParameterTypes() != null ||
-                             !Type.containsAny(expectedFormals, inferenceVars())) {
-                        return t.complete(asUndetType(pt), allowBoxing, check);
-                    } else {
-                        return null;
+                    if (t.getParameterTypes() == null &&
+                             Type.containsAny(expectedFormals, inferenceContext.inferenceVars())) {
+                        if (inferenceContext.mode() == InferenceContext.Mode.BREAK_CYCLES) {
+                            Assert.check(chk.complexInference);
+                            try {
+                                instantiateCyclic(inferenceContext, expectedFormals, warn);
+                                pt = inferenceContext.asInstantiatedType(pt, types);
+                            }
+                            catch (InferenceException ex) {
+                                throw cyclicInferenceException.setMessage("cyclic.lambda.inference");
+                            }
+                        } else {
+                            throw cyclicInferenceException.setMessage("cyclic.lambda.inference");
+                        }
                     }
+                    return t.complete(inferenceContext, pt, allowBoxing, check);
+
                 default: Assert.error(); return Type.noType;
             }
         }
     };
 
-    public Type instantiatePoly(Env<AttrContext> env, ForAll that, Type to,
+    public Type instantiatePoly(Env<AttrContext> env, InferenceContext inferenceContext, ForAll that, Type to,
             Warner warn, boolean allowBoxing, boolean check) throws InferenceException {
-        return new InferPolyCompleter(allowBoxing, check).complete(env.tree.pos(), env, that, to, warn);
+        return new InferPolyCompleter(allowBoxing, check, inferenceContext).complete(env.tree.pos(), env, that, to, warn);
     }
     // </editor-fold>
 
@@ -754,73 +857,166 @@
     /**
      * An inference context contains a set of type-variables and corresponding
      * undet variables, used to keep track of constraints. In the typical scenario,
-     * an inference context is pushed onto the stack when a new method signature
-     * needs to be instantiated. Such context is then popped off the stack when
-     * overload resolution is completed. It is possible that, when popping an
-     * inference context, its variables/undetvars are copied over to the
-     * outer context (this behavior is required in order to propagate inference
-     * variables in the right way).
+     * an inference context is used when a generic method signature needs to be
+     * instantiated. An inference context can be dup'ed in order to prevent
+     * side-effects on the original inference context (usueful when during overload
+     * resolution).
      */
-    class InferenceContext {
-        List<Type> tvars;
-        List<Type> undetvars;
+    public static class InferenceContext {
 
-        public InferenceContext() {
-            this(List.<Type>nil());
+        /** inference variables in this context
+         */
+        private List<Type> tvars;
+
+        /** undet variables in this context (used to keep track of inference constraints)
+         */
+        private List<Type> undetvars;
+
+        /** inferred variables in this context
+         */
+        private List<Type> insttypes;
+
+        /** current inference policy
+         */
+        private Mode mode = Mode.ERROR_ON_CYCLES;
+
+        /**
+         * Policy associated with this inference context. The inference process
+         * starts up in a conservative mode, where inference constraints that have
+         * inference variables on both ends are discarded with an error. If the
+         * flag -XDcompleinference is set, certain poly expression can be 'unstuck'
+         * by performing a more aggressive form of type-inference, in which
+         * cyclic constraints are reduced.
+         */
+        public enum Mode {
+            ERROR_ON_CYCLES,
+            BREAK_CYCLES;
         }
 
-        public InferenceContext(List<Type> tvars) {
-            this.tvars = tvars;
+        private InferenceContext(List<Type> tvars) {
+            this.tvars = this.insttypes = tvars;
             this.undetvars = Type.map(tvars, fromTypeVarFun);
         }
 
-        void clear() {
-            tvars = List.nil();
-            undetvars = List.nil();
+        /**
+         * Creates a new copy of the existing inference context with fresh
+         * undetermined variables - useful to avoid propagation of spurious
+         * inference constraints (i.e. during overload resolution)
+         */
+        public InferenceContext dup(Types types) {
+            InferenceContext newContext = new InferenceContext(tvars);
+            newContext.mode = mode;
+            //dup undet vars
+            List<Type> from = undetvars;
+            List<Type> to = newContext.undetvars;
+            while (from.nonEmpty()) {
+                UndetVar uvFrom = (UndetVar)from.head;
+                UndetVar uvTo = (UndetVar)to.head;
+                uvTo.hibounds = newContext.asUndetTypes(Type.map(uvFrom.hibounds, toTypeVarFun), types);
+                uvTo.lobounds = newContext.asUndetTypes(Type.map(uvFrom.lobounds, toTypeVarFun), types);
+                if (uvFrom.inst != null) {
+                    uvTo.inst = newContext.asUndetType(toTypeVarFun.apply(uvFrom.inst), types);
+                }
+                from = from.tail;
+                to = to.tail;
+            }
+            return newContext;
         }
-    }
 
-    /** starts off with global context - for Check.instantiatePoly **/
-    List<InferenceContext> inferenceContexts = List.of(new InferenceContext());
+        /**
+         * Returns a type obtained from a given type t where all inference variables
+         * have been replaced with undetvars - useful in order implicitly collect
+         * constraints by performing compatibility checks
+         */
+        Type asUndetType(Type t, Types types) {
+            return types.subst(t, tvars, undetvars);
+        }
 
-    void pushContext(List<Type> tvars) {
-        inferenceContexts = inferenceContexts.prepend(new InferenceContext(tvars));
-    }
+        List<Type> asUndetTypes(List<Type> ts, Types types) {
+            ListBuffer<Type> buf = ListBuffer.lb();
+            for (Type t : ts) {
+                buf.append(asUndetType(t, types));
+            }
+            return buf.toList();
+        }
 
-    void popContext(boolean merge) {
-        InferenceContext oldCtx = inferenceContexts.head;
-        inferenceContexts = inferenceContexts.tail;
-        if (merge) {
-            inferenceContexts.head.tvars = inferenceContexts.head.tvars.appendList(oldCtx.tvars);
-            inferenceContexts.head.undetvars = inferenceContexts.head.undetvars.appendList(oldCtx.undetvars);
+        /**
+         * Returns a type obtained from a given type t where all inference variables
+         * have been replaced with inferred types
+         */
+        public Type asInstantiatedType(Type t, Types types) {
+            return types.subst(t, tvars, insttypes);
         }
-    }
 
-    /**
-     * Return an uninstantiated version of this type, where the inference
-     * type-variables are replaced with undetvars. This is useful in order
-     * to perform subtyping tests, conversions, etc.
-     */
-    Type asUndetType(Type t) {
-        return types.subst(t,
-                inferenceContexts.head.tvars,
-                inferenceContexts.head.undetvars);
-    }
+        public List<Type> asInstantiatedTypes(List<Type> ts, Types types) {
+            ListBuffer<Type> buf = ListBuffer.lb();
+            for (Type t : ts) {
+                buf.append(asInstantiatedType(t, types));
+            }
+            return buf.toList();
+        }
 
-    List<Type> asUndetTypes(List<Type> ts) {
-        ListBuffer<Type> buf = ListBuffer.lb();
-        for (Type t : ts) {
-            buf.append(asUndetType(t));
+        // <editor-fold desc="getters">
+        List<Type> inferenceVars() {
+            return tvars;
         }
-        return buf.toList();
-    }
 
-    List<Type> inferenceVars() {
-        return inferenceContexts.head.tvars;
-    }
+        List<Type> inferredTypes() {
+            return insttypes;
+        }
 
-    List<Type> undetVars() {
-        return inferenceContexts.head.undetvars;
+        List<Type> undetVars() {
+            return undetvars;
+        }
+
+        Mode mode() {
+            return mode;
+        }
+        // </editor-fold>
+
+        // <editor-fold desc="setters">
+        void setMode(Mode mode) {
+            this.mode = mode;
+        }
+
+        void setInferredTypes(List<Type> insttypes) {
+            Assert.check(tvars.length() == insttypes.length());
+            this.insttypes = insttypes;
+        }
+        // </editor-fold>
+        
+        // <editor-fold desc="predicates">
+        boolean isInferenceVar(Type t) {
+            return tvars.contains(t);
+        }
+
+        boolean isInstantiatedVar(Type t) {
+            int pos = tvars.indexOf(t);
+            return insttypes.get(pos) != t;
+        }
+        // </editor-fold>
+
+        // <editor-fold desc="utility mappings">
+        /** A mapping that turns type variables into undetermined type variables.
+         */
+        private Mapping fromTypeVarFun = new Mapping("fromTypeVarFun") {
+            public Type apply(Type t) {
+                if (t.tag == TYPEVAR) return new UndetVar(t);
+                else return t.map(this);
+            }
+        };
+
+        /** A mapping that undetermined type variables back into inference variables.
+         */
+        private Mapping toTypeVarFun = new Mapping("toTypeVarFun") {
+            public Type apply(Type t) {
+                if (t.tag == UNDETVAR) return ((UndetVar)t).qtype;
+                else return t.map(this);
+            }
+        };
+        // </editor-fold>
+
+        public static final InferenceContext emptyContext = new InferenceContext(List.<Type>nil());
     }
     // </editor-fold>
 }
--- a/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Thu Jun 23 14:28:57 2011 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Mon Jul 04 13:54:48 2011 +0100
@@ -35,6 +35,7 @@
 
 import com.sun.tools.javac.code.Type.*;
 import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.comp.Infer.InferenceContext;
 import com.sun.tools.javac.tree.JCTree.*;
 
 import static com.sun.tools.javac.code.Flags.*;
@@ -378,7 +379,7 @@
         }
 
         // find out whether we need to go the slow route via infer
-
+        
         boolean hasPolyArgs = false;
         for (Type t : argtypes) {
             if (t.getPolyTag() != NO_POLY) {
@@ -386,7 +387,7 @@
                 break;
             }
         }
-        
+
         boolean instNeeded = tvars.tail != null || /*inlined: tvars.nonEmpty()*/
                 polymorphicSignature ||
                 hasPolyArgs;
@@ -396,28 +397,19 @@
             types.capture(types.upperBounds(argtypes));
 
         if (instNeeded) {
-            try {
-                infer.pushContext(tvars);
-                return polymorphicSignature ?
-                    infer.instantiatePolymorphicSignatureInstance(env, site, m.name, (MethodSymbol)m, argsToCheck) :
-                    infer.instantiateMethod(env,
-                                        tvars,
-                                        (MethodType)mt,
-                                        m,
-                                        argsToCheck,
-                                        allowBoxing,
-                                        useVarargs,
-                                        hasPolyArgs,
-                                        warn);
-            }
-            finally {
-                //if we are checking the method, and if the call is not unchecked
-                //copy current inference vars to outer inference context
-                infer.popContext(check && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED));
-            }
+            return polymorphicSignature ?
+                infer.instantiatePolymorphicSignatureInstance(env, site, m.name, (MethodSymbol)m, argsToCheck) :
+                infer.instantiateMethod(env,
+                                    tvars,
+                                    (MethodType)mt,
+                                    m,
+                                    argsToCheck,
+                                    allowBoxing,
+                                    useVarargs,
+                                    check,
+                                    warn);
         }
-
-        checkRawArgumentsAcceptable(env, mt, argsToCheck, allowBoxing, useVarargs, warn);
+        checkRawArgumentsAcceptable(env, mt, argsToCheck, allowBoxing, useVarargs, check, warn);
         return mt;
     }
 
@@ -451,7 +443,7 @@
             boolean useVarargs,
             Warner warn) {
         try {
-            checkRawArgumentsAcceptable(env, mtype, argtypes, allowBoxing, useVarargs, warn);
+            checkRawArgumentsAcceptable(env, mtype, argtypes, allowBoxing, useVarargs, true, warn);
             return true;
         } catch (InapplicableMethodException ex) {
             return false;
@@ -510,9 +502,10 @@
                                 List<Type> argtypes,
                                 boolean allowBoxing,
                                 boolean useVarargs,
+                                boolean check,
                                 Warner warn) {
-        return checkRawArgumentsAcceptable(env, mtype, argtypes,
-                allowBoxing, useVarargs, true, warn, resolveHandler);
+        return checkRawArgumentsAcceptable(env, InferenceContext.emptyContext, mtype, argtypes,
+                allowBoxing, useVarargs, check, warn, resolveHandler);
     }
 
     /**
@@ -532,6 +525,7 @@
      * A method check handler (see above) is used in order to report errors.
      */
     List<Type> checkRawArgumentsAcceptable(Env<AttrContext> env,
+                                InferenceContext inferenceContext,
                                 Type mtype,
                                 List<Type> argtypes,
                                 boolean allowBoxing,
@@ -554,15 +548,16 @@
         while (argtypes.nonEmpty() && formals.head != varargsFormal) {
             Type actual = argtypes.head;
             if (actual.tag == FORALL) {
+                //this stuff should be moved in the old 'good' infer.instantiatePoly!!
                 try {
-                    if (infer.instantiatePoly(env, (ForAll)actual, formals.head, warn, allowBoxing, check) == null) {
-                        uncheckedArgs = uncheckedArgs.append(actual);
-                    }
+                    infer.instantiatePoly(env, inferenceContext, (ForAll)argtypes.head, formals.head, warn, allowBoxing, check);
+                } catch (Infer.CyclicInferenceException ex) {
+                    uncheckedArgs = uncheckedArgs.append(actual);
                 } catch (InapplicableMethodException ex) {
                     handler.argumentMismatch(useVarargs, actual, formals.head, ex.diagnostic);
                 }
             } else {
-                Type undetFormal = infer.asUndetType(formals.head);
+                Type undetFormal = inferenceContext.asUndetType(formals.head, types);
                 boolean works = allowBoxing ?
                         types.isConvertible(argtypes.head, undetFormal, warn) :
                         types.isSubtypeUnchecked(argtypes.head, undetFormal, warn);
@@ -588,14 +583,14 @@
                 Type actual = argtypes.head;
                 if (actual.tag == FORALL) {
                     try {
-                        if (infer.instantiatePoly(env, (ForAll)actual, elt, warn, allowBoxing, check) == null) {
-                            uncheckedArgs = uncheckedArgs.append(actual);
-                        }
+                        infer.instantiatePoly(env, inferenceContext, (ForAll)actual, elt, warn, allowBoxing, check);
+                    } catch (Infer.CyclicInferenceException ex) {
+                        uncheckedArgs = uncheckedArgs.append(actual);
                     } catch (InapplicableMethodException ex) {
                         handler.argumentMismatch(true, actual, elt, ex.diagnostic);
                     }
                 } else {
-                    Type eltUndet = infer.asUndetType(elt);
+                    Type eltUndet = inferenceContext.asUndetType(elt, types);
                     if (!types.isConvertible(actual, eltUndet, warn)) {
                         handler.argumentMismatch(true,
                                 argtypes.head,
@@ -606,7 +601,7 @@
     	        argtypes = argtypes.tail;
     	    }
             //check varargs element type accessibility
-            if (check && !isAccessible(env, elt)) {
+            if (!elt.containsAny(inferenceContext.inferenceVars()) && !isAccessible(env, elt)) {
                 Symbol location = env.enclClass.sym;
                 handler.inaccessibleVarargs(location, elt);
             }
--- a/src/share/classes/com/sun/tools/javac/main/OptionName.java	Thu Jun 23 14:28:57 2011 +0100
+++ b/src/share/classes/com/sun/tools/javac/main/OptionName.java	Mon Jul 04 13:54:48 2011 +0100
@@ -72,7 +72,6 @@
     J("-J"),
     MOREINFO("-moreinfo"),
     WERROR("-Werror"),
-    COMPLEXINFERENCE("-complexinference"),
     PROMPT("-prompt"),
     DOE("-doe"),
     PRINTSOURCE("-printsource"),
--- a/src/share/classes/com/sun/tools/javac/main/RecognizedOptions.java	Thu Jun 23 14:28:57 2011 +0100
+++ b/src/share/classes/com/sun/tools/javac/main/RecognizedOptions.java	Mon Jul 04 13:54:48 2011 +0100
@@ -470,9 +470,6 @@
         // treat warnings as errors
         new Option(WERROR,                                      "opt.Werror"),
 
-        // use complex inference from context in the position of a method call argument
-        new HiddenOption(COMPLEXINFERENCE),
-
         // generare source stubs
         // new HiddenOption("-stubs"),
 
--- a/test/tools/javac/lambda/ExceptionTransparency04.out	Thu Jun 23 14:28:57 2011 +0100
+++ b/test/tools/javac/lambda/ExceptionTransparency04.out	Mon Jul 04 13:54:48 2011 +0100
@@ -1,4 +1,4 @@
-ExceptionTransparency04.java:43:46: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.infer.no.conforming.instance.exists: Z, ExceptionTransparency04.SAM<java.io.IOException|java.lang.ClassNotFoundException>, ExceptionTransparency04.SAM<java.lang.ClassNotFoundException>)), ExceptionTransparency04.SAM<java.io.IOException|java.lang.ClassNotFoundException>, ExceptionTransparency04.SAM<java.lang.ClassNotFoundException>
-ExceptionTransparency04.java:44:35: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.infer.no.conforming.instance.exists: Z, ExceptionTransparency04.SAM<java.io.IOException|java.lang.ClassNotFoundException>, ExceptionTransparency04.SAM<java.io.IOException>)), ExceptionTransparency04.SAM<java.io.IOException|java.lang.ClassNotFoundException>, ExceptionTransparency04.SAM<java.io.IOException>
-ExceptionTransparency04.java:47:68: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.infer.no.conforming.instance.exists: Z, ExceptionTransparency04.SAM<java.io.IOException|java.lang.ClassNotFoundException>, ExceptionTransparency04.SAM<java.lang.ClassNotFoundException|java.io.FileNotFoundException>)), ExceptionTransparency04.SAM<java.io.IOException|java.lang.ClassNotFoundException>, ExceptionTransparency04.SAM<java.lang.ClassNotFoundException|java.io.FileNotFoundException>
+ExceptionTransparency04.java:43:46: compiler.err.prob.found.req: (compiler.misc.incompatible.types), ExceptionTransparency04.SAM<java.io.IOException|java.lang.ClassNotFoundException>, ExceptionTransparency04.SAM<java.lang.ClassNotFoundException>
+ExceptionTransparency04.java:44:35: compiler.err.prob.found.req: (compiler.misc.incompatible.types), ExceptionTransparency04.SAM<java.io.IOException|java.lang.ClassNotFoundException>, ExceptionTransparency04.SAM<java.io.IOException>
+ExceptionTransparency04.java:47:68: compiler.err.prob.found.req: (compiler.misc.incompatible.types), ExceptionTransparency04.SAM<java.io.IOException|java.lang.ClassNotFoundException>, ExceptionTransparency04.SAM<java.lang.ClassNotFoundException|java.io.FileNotFoundException>
 3 errors
--- a/test/tools/javac/lambda/TargetType10.java	Thu Jun 23 14:28:57 2011 +0100
+++ b/test/tools/javac/lambda/TargetType10.java	Mon Jul 04 13:54:48 2011 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -26,6 +26,7 @@
  * @summary check that wildcards in the target method of a lambda conversion is handled correctly
  * @author  Maurizio Cimadamore
  * @compile/fail/ref=TargetType10.out -XDrawDiagnostics TargetType10.java
+ * @compile -XDcomplexinference TargetType10.java
  */
 
 class TargetType10 {
--- a/test/tools/javac/lambda/TargetType10.out	Thu Jun 23 14:28:57 2011 +0100
+++ b/test/tools/javac/lambda/TargetType10.out	Mon Jul 04 13:54:48 2011 +0100
@@ -1,2 +1,2 @@
-TargetType10.java:38:11: compiler.err.cant.apply.symbol.1: kindname.method, compose, TargetType10.Function<B,C>,TargetType10.Function<A,? extends B>, compiler.misc.type.lambda,compiler.misc.type.lambda, kindname.class, TargetType10.Test, (compiler.misc.cyclic.lambda.inference)
+TargetType10.java:39:11: compiler.err.cant.apply.symbol.1: kindname.method, compose, TargetType10.Function<B,C>,TargetType10.Function<A,? extends B>, compiler.misc.type.lambda,compiler.misc.type.lambda, kindname.class, TargetType10.Test, (compiler.misc.cyclic.lambda.inference)
 1 error
--- a/test/tools/javac/lambda/TargetType14.out	Thu Jun 23 14:28:57 2011 +0100
+++ b/test/tools/javac/lambda/TargetType14.out	Mon Jul 04 13:54:48 2011 +0100
@@ -1,2 +1,2 @@
-TargetType14.java:41:29: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.infer.no.conforming.instance.exists: X, TargetType14.SAM<java.lang.String>, TargetType14.SAM<java.lang.Integer>)), TargetType14.SAM<java.lang.String>, TargetType14.SAM<java.lang.Integer>
+TargetType14.java:41:29: compiler.err.prob.found.req: (compiler.misc.incompatible.types), TargetType14.SAM<java.lang.String>, TargetType14.SAM<java.lang.Integer>
 1 error
--- a/test/tools/javac/lambda/TargetType24.out	Thu Jun 23 14:28:57 2011 +0100
+++ b/test/tools/javac/lambda/TargetType24.out	Mon Jul 04 13:54:48 2011 +0100
@@ -1,4 +1,4 @@
-TargetType24.java:55:37: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.infer.no.conforming.instance.exists: , java.lang.String, boolean)), java.lang.String, boolean
+TargetType24.java:55:37: compiler.err.prob.found.req: (compiler.misc.incompatible.types), java.lang.String, boolean
 TargetType24.java:56:47: compiler.err.cant.apply.symbol.1: kindname.method, forAll2, TargetType24.FSub<java.lang.Character,java.lang.String>, compiler.misc.type.lambda, kindname.class, TargetType24.Array<A>, (compiler.misc.no.conforming.assignment.exists.1: compiler.misc.type.lambda, TargetType24.FSub<java.lang.Character,java.lang.String>, (compiler.misc.infer.incompatible.ret.types.in.lambda: boolean))
 TargetType24.java:57:98: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.infer.incompatible.ret.types.in.lambda: java.lang.String)), compiler.misc.type.lambda, TargetType24.F<java.lang.Character,java.lang.Boolean>
 TargetType24.java:58:101: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.infer.incompatible.ret.types.in.lambda: boolean)), compiler.misc.type.lambda, TargetType24.FSub<java.lang.Character,java.lang.String>
--- a/test/tools/javac/lambda/TargetType27.java	Thu Jun 23 14:28:57 2011 +0100
+++ b/test/tools/javac/lambda/TargetType27.java	Mon Jul 04 13:54:48 2011 +0100
@@ -25,6 +25,7 @@
  * @test
  * @summary complex case of cyclic type inference (lambda returned where inference var expected)
  * @compile/fail/ref=TargetType27.out -XDrawDiagnostics TargetType27.java
+ * @compile/fail/ref=TargetType27.out -XDrawDiagnostics -XDcomplexinference TargetType27.java
  */
 
 class TargetType26 {
--- a/test/tools/javac/lambda/TargetType27.out	Thu Jun 23 14:28:57 2011 +0100
+++ b/test/tools/javac/lambda/TargetType27.out	Mon Jul 04 13:54:48 2011 +0100
@@ -1,2 +1,2 @@
-TargetType27.java:38:9: compiler.err.cant.apply.symbol.1: kindname.method, m, TargetType26.F<A,R>, compiler.misc.type.lambda, kindname.class, TargetType26, (compiler.misc.infer.no.conforming.assignment.exists.1: A,R, compiler.misc.type.lambda, TargetType26.F<A,R>, (compiler.misc.cyclic.lambda.inference))
+TargetType27.java:39:9: compiler.err.cant.apply.symbol.1: kindname.method, m, TargetType26.F<A,R>, compiler.misc.type.lambda, kindname.class, TargetType26, (compiler.misc.cyclic.lambda.inference)
 1 error
--- a/test/tools/javac/lambda/TargetType28.java	Thu Jun 23 14:28:57 2011 +0100
+++ b/test/tools/javac/lambda/TargetType28.java	Mon Jul 04 13:54:48 2011 +0100
@@ -25,6 +25,7 @@
  * @test
  * @summary target type inference in a case where lambda expression returns diamond
  * @compile/fail/ref=TargetType28.out -XDrawDiagnostics TargetType28.java
+ * @compile/fail/ref=TargetType28_b.out -XDrawDiagnostics -XDcomplexinference TargetType28.java
  */
 
 class TargetType28 {
--- a/test/tools/javac/lambda/TargetType28.out	Thu Jun 23 14:28:57 2011 +0100
+++ b/test/tools/javac/lambda/TargetType28.out	Mon Jul 04 13:54:48 2011 +0100
@@ -1,2 +1,4 @@
-TargetType28.java:41:32: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.infer.no.conforming.instance.exists: Z,R,X, TargetType28.SuperFoo<X>, TargetType28.SuperFoo<java.lang.String>)), TargetType28.SuperFoo<X>, TargetType28.SuperFoo<java.lang.String>
-1 error
+TargetType28.java:42:27: compiler.err.cant.apply.symbol.1: kindname.method, apply, TargetType28.A<Z,R>,Z, compiler.misc.type.lambda,int, kindname.class, TargetType28, (compiler.misc.cyclic.lambda.inference)
+TargetType28.java:43:28: compiler.err.cant.apply.symbol.1: kindname.method, apply, TargetType28.A<Z,R>,Z, compiler.misc.type.lambda,int, kindname.class, TargetType28, (compiler.misc.cyclic.lambda.inference)
+TargetType28.java:44:22: compiler.err.cant.apply.symbol.1: kindname.method, apply, TargetType28.A<Z,R>,Z, compiler.misc.type.lambda,int, kindname.class, TargetType28, (compiler.misc.cyclic.lambda.inference)
+3 errors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/TargetType28_b.out	Mon Jul 04 13:54:48 2011 +0100
@@ -0,0 +1,3 @@
+TargetType28.java:42:32: compiler.err.prob.found.req: (compiler.misc.incompatible.types), TargetType28.SuperFoo<java.lang.Number>, TargetType28.SuperFoo<java.lang.String>
+TargetType28.java:43:33: compiler.err.prob.found.req: (compiler.misc.incompatible.types), TargetType28.SuperFoo<java.lang.Number>, TargetType28.SuperFoo<java.lang.Integer>
+2 errors