7175433: Inference cleanup: add helper class to handle inference variables
authormcimadamore
Tue, 25 Sep 2012 11:55:34 +0100
changeset 14047 7c7a5611cf76
parent 14046 8ef5d5b19998
child 14048 308d1cf8fe46
7175433: Inference cleanup: add helper class to handle inference variables Summary: Add class to handle inference variables instantiation and associated info Reviewed-by: jjg, dlsmith
langtools/src/share/classes/com/sun/tools/javac/comp/Check.java
langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java
langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java
langtools/test/tools/javac/generics/inference/6638712/T6638712c.out
langtools/test/tools/javac/varargs/6313164/T7175433.java
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java	Tue Sep 25 11:53:18 2012 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java	Tue Sep 25 11:55:34 2012 +0100
@@ -40,6 +40,8 @@
 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 com.sun.tools.javac.comp.Infer.InferenceContext.FreeTypeListener;
 
 import static com.sun.tools.javac.code.Flags.*;
 import static com.sun.tools.javac.code.Flags.ANNOTATION;
@@ -429,6 +431,8 @@
          * Obtain a warner for this check context
          */
         public Warner checkWarner(DiagnosticPosition pos, Type found, Type req);
+
+        public Infer.InferenceContext inferenceContext();
     }
 
     /**
@@ -455,6 +459,10 @@
         public Warner checkWarner(DiagnosticPosition pos, Type found, Type req) {
             return enclosingContext.checkWarner(pos, found, req);
         }
+
+        public Infer.InferenceContext inferenceContext() {
+            return enclosingContext.inferenceContext();
+        }
     }
 
     /**
@@ -471,6 +479,10 @@
         public Warner checkWarner(DiagnosticPosition pos, Type found, Type req) {
             return convertWarner(pos, found, req);
         }
+
+        public InferenceContext inferenceContext() {
+            return infer.emptyContext;
+        }
     };
 
     /** Check that a given type is assignable to a given proto-type.
@@ -483,7 +495,16 @@
         return checkType(pos, found, req, basicHandler);
     }
 
-    Type checkType(final DiagnosticPosition pos, Type found, Type req, CheckContext checkContext) {
+    Type checkType(final DiagnosticPosition pos, final Type found, final Type req, final CheckContext checkContext) {
+        final Infer.InferenceContext inferenceContext = checkContext.inferenceContext();
+        if (inferenceContext.free(req)) {
+            inferenceContext.addFreeTypeListener(List.of(req), new FreeTypeListener() {
+                @Override
+                public void typesInferred(InferenceContext inferenceContext) {
+                    checkType(pos, found, inferenceContext.asInstType(req, types), checkContext);
+                }
+            });
+        }
         if (req.tag == ERROR)
             return req;
         if (req.tag == NONE)
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java	Tue Sep 25 11:53:18 2012 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java	Tue Sep 25 11:55:34 2012 +0100
@@ -25,18 +25,21 @@
 
 package com.sun.tools.javac.comp;
 
+import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.code.Type.*;
+import com.sun.tools.javac.comp.Resolve.InapplicableMethodException;
+import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode;
 import com.sun.tools.javac.tree.JCTree;
 import com.sun.tools.javac.tree.JCTree.JCTypeCast;
 import com.sun.tools.javac.tree.TreeInfo;
 import com.sun.tools.javac.util.*;
 import com.sun.tools.javac.util.List;
-import com.sun.tools.javac.code.*;
-import com.sun.tools.javac.code.Type.*;
-import com.sun.tools.javac.code.Symbol.*;
-import com.sun.tools.javac.comp.Resolve.InapplicableMethodException;
-import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode;
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import static com.sun.tools.javac.code.TypeTags.*;
 
 /** Helper class for type parameter inference, used by the attribution phase.
@@ -76,50 +79,48 @@
         chk = Check.instance(context);
         diags = JCDiagnostic.Factory.instance(context);
         inferenceException = new InferenceException(diags);
-
     }
 
+   /**
+    * This exception class is design to store a list of diagnostics corresponding
+    * to inference errors that can arise during a method applicability check.
+    */
     public static class InferenceException extends InapplicableMethodException {
         private static final long serialVersionUID = 0;
 
+        List<JCDiagnostic> messages = List.nil();
+
         InferenceException(JCDiagnostic.Factory diags) {
             super(diags);
         }
+
+        @Override
+        InapplicableMethodException setMessage(JCDiagnostic diag) {
+            messages = messages.append(diag);
+            return this;
+        }
+
+        @Override
+        public JCDiagnostic getDiagnostic() {
+            return messages.head;
+        }
+
+        void clear() {
+            messages = List.nil();
+        }
     }
 
     private final InferenceException inferenceException;
 
 /***************************************************************************
- * Auxiliary type values and classes
- ***************************************************************************/
-
-    /** A mapping that turns type variables into undetermined type variables.
-     */
-    List<Type> makeUndetvars(List<Type> tvars) {
-        List<Type> undetvars = Type.map(tvars, fromTypeVarFun);
-        for (Type t : undetvars) {
-            UndetVar uv = (UndetVar)t;
-            uv.hibounds = types.getBounds((TypeVar)uv.qtype);
-        }
-        return undetvars;
-    }
-    //where
-            Mapping fromTypeVarFun = new Mapping("fromTypeVarFun") {
-                public Type apply(Type t) {
-                    if (t.tag == TYPEVAR) return new UndetVar(t);
-                    else return t.map(this);
-                }
-            };
-
-/***************************************************************************
  * Mini/Maximization of UndetVars
  ***************************************************************************/
 
     /** Instantiate undetermined type variable to its minimal upper bound.
      *  Throw a NoInstanceException if this not possible.
      */
-    void maximizeInst(UndetVar that, Warner warn) throws InferenceException {
-        List<Type> hibounds = Type.filter(that.hibounds, errorFilter);
+   void maximizeInst(UndetVar that, Warner warn) throws InferenceException {
+        List<Type> hibounds = Type.filter(that.hibounds, boundFilter);
         if (that.eq.isEmpty()) {
             if (hibounds.isEmpty())
                 that.inst = syms.objectType;
@@ -137,10 +138,10 @@
                             that.qtype, hibounds);
     }
 
-    private Filter<Type> errorFilter = new Filter<Type>() {
+    private Filter<Type> boundFilter = new Filter<Type>() {
         @Override
         public boolean accepts(Type t) {
-            return !t.isErroneous();
+            return !t.isErroneous() && t.tag != BOT;
         }
     };
 
@@ -148,11 +149,12 @@
      *  Throw a NoInstanceException if this not possible.
      */
     void minimizeInst(UndetVar that, Warner warn) throws InferenceException {
-        List<Type> lobounds = Type.filter(that.lobounds, errorFilter);
+        List<Type> lobounds = Type.filter(that.lobounds, boundFilter);
         if (that.eq.isEmpty()) {
-            if (lobounds.isEmpty())
-                that.inst = syms.botType;
-            else if (lobounds.tail.isEmpty())
+            if (lobounds.isEmpty()) {
+                //do nothing - the inference variable is under-constrained
+                return;
+            } else if (lobounds.tail.isEmpty())
                 that.inst = lobounds.head.isPrimitive() ? syms.errType : lobounds.head;
             else {
                 that.inst = types.lub(lobounds);
@@ -166,90 +168,70 @@
         }
     }
 
-    Type asUndetType(Type t, List<Type> undetvars) {
-        return types.subst(t, inferenceVars(undetvars), undetvars);
-    }
-
-    List<Type> inferenceVars(List<Type> undetvars) {
-        ListBuffer<Type> tvars = ListBuffer.lb();
-        for (Type uv : undetvars) {
-            tvars.append(((UndetVar)uv).qtype);
-        }
-        return tvars.toList();
-    }
-
 /***************************************************************************
  * Exported Methods
  ***************************************************************************/
 
-    /** Try to instantiate expression type `that' to given type `to'.
-     *  If a maximal instantiation exists which makes this type
-     *  a subtype of type `to', return the instantiated type.
-     *  If no instantiation exists, or if several incomparable
-     *  best instantiations exist throw a NoInstanceException.
+    /**
+     * Instantiate uninferred inference variables (JLS 15.12.2.8). First
+     * if the method return type is non-void, we derive constraints from the
+     * expected type - then we use declared bound well-formedness to derive additional
+     * constraints. If no instantiation exists, or if several incomparable
+     * best instantiations exist throw a NoInstanceException.
      */
-    public List<Type> instantiateUninferred(DiagnosticPosition pos,
-                                List<Type> undetvars,
-                                List<Type> tvars,
-                                MethodType mtype,
-                                Attr.ResultInfo resultInfo,
-                                Warner warn) throws InferenceException {
+    public void instantiateUninferred(DiagnosticPosition pos,
+            InferenceContext inferenceContext,
+            MethodType mtype,
+            Attr.ResultInfo resultInfo,
+            Warner warn) throws InferenceException {
         Type to = resultInfo.pt;
         if (to.tag == NONE) {
             to = mtype.getReturnType().tag <= VOID ?
                     mtype.getReturnType() : syms.objectType;
         }
-        Type qtype1 = types.subst(mtype.getReturnType(), tvars, undetvars);
+        Type qtype1 = inferenceContext.asFree(mtype.getReturnType(), types);
         if (!types.isSubtype(qtype1,
                 qtype1.tag == UNDETVAR ? types.boxedTypeOrType(to) : to)) {
             throw inferenceException
-                .setMessage("infer.no.conforming.instance.exists",
-                            tvars, mtype.getReturnType(), to);
+                    .setMessage("infer.no.conforming.instance.exists",
+                    inferenceContext.restvars(), mtype.getReturnType(), to);
         }
 
-        List<Type> insttypes;
         while (true) {
             boolean stuck = true;
-            insttypes = List.nil();
-            for (Type t : undetvars) {
+            for (Type t : inferenceContext.undetvars) {
                 UndetVar uv = (UndetVar)t;
-                if (uv.inst == null && (uv.eq.nonEmpty() || !Type.containsAny(uv.hibounds, tvars))) {
+                if (uv.inst == null && (uv.eq.nonEmpty() || !inferenceContext.free(uv.hibounds))) {
                     maximizeInst((UndetVar)t, warn);
                     stuck = false;
                 }
-                insttypes = insttypes.append(uv.inst == null ? uv.qtype : uv.inst);
             }
-            if (!Type.containsAny(insttypes, tvars)) {
+            if (inferenceContext.restvars().isEmpty()) {
                 //all variables have been instantiated - exit
                 break;
             } else if (stuck) {
                 //some variables could not be instantiated because of cycles in
                 //upper bounds - provide a (possibly recursive) default instantiation
-                insttypes = types.subst(insttypes,
-                    tvars,
-                    instantiateAsUninferredVars(undetvars, tvars));
+                instantiateAsUninferredVars(inferenceContext);
                 break;
             } else {
                 //some variables have been instantiated - replace newly instantiated
                 //variables in remaining upper bounds and continue
-                for (Type t : undetvars) {
+                for (Type t : inferenceContext.undetvars) {
                     UndetVar uv = (UndetVar)t;
-                    uv.hibounds = types.subst(uv.hibounds, tvars, insttypes);
+                    uv.hibounds = inferenceContext.asInstTypes(uv.hibounds, types);
                 }
             }
         }
-        return insttypes;
     }
 
     /**
      * Infer cyclic inference variables as described in 15.12.2.8.
      */
-    private List<Type> instantiateAsUninferredVars(List<Type> undetvars, List<Type> tvars) {
-        Assert.check(undetvars.length() == tvars.length());
-        ListBuffer<Type> insttypes = ListBuffer.lb();
+    private void instantiateAsUninferredVars(InferenceContext inferenceContext) {
         ListBuffer<Type> todo = ListBuffer.lb();
         //step 1 - create fresh tvars
-        for (Type t : undetvars) {
+        for (Type t : inferenceContext.undetvars) {
             UndetVar uv = (UndetVar)t;
             if (uv.inst == null) {
                 TypeSymbol fresh_tvar = new TypeSymbol(Flags.SYNTHETIC, uv.qtype.tsym.name, null, uv.qtype.tsym.owner);
@@ -257,25 +239,23 @@
                 todo.append(uv);
                 uv.inst = fresh_tvar.type;
             }
-            insttypes.append(uv.inst);
         }
         //step 2 - replace fresh tvars in their bounds
-        List<Type> formals = tvars;
+        List<Type> formals = inferenceContext.inferenceVars();
         for (Type t : todo) {
             UndetVar uv = (UndetVar)t;
             TypeVar ct = (TypeVar)uv.inst;
-            ct.bound = types.glb(types.subst(types.getBounds(ct), tvars, insttypes.toList()));
+            ct.bound = types.glb(inferenceContext.asInstTypes(types.getBounds(ct), types));
             if (ct.bound.isErroneous()) {
                 //report inference error if glb fails
                 reportBoundError(uv, BoundErrorKind.BAD_UPPER);
             }
             formals = formals.tail;
         }
-        return insttypes.toList();
     }
 
-    /** Instantiate method type `mt' by finding instantiations of
-     *  `tvars' so that method can be applied to `argtypes'.
+    /** Instantiate a generic method type by finding instantiations for all its
+     * inference variables so that it can be applied to a given argument type list.
      */
     public Type instantiateMethod(Env<AttrContext> env,
                                   List<Type> tvars,
@@ -287,83 +267,61 @@
                                   boolean useVarargs,
                                   Warner warn) throws InferenceException {
         //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG
-        List<Type> undetvars =  makeUndetvars(tvars);
+        final InferenceContext inferenceContext = new InferenceContext(tvars, types);
+        inferenceException.clear();
 
-        List<Type> capturedArgs =
-                rs.checkRawArgumentsAcceptable(env, undetvars, argtypes, mt.getParameterTypes(),
-                    allowBoxing, useVarargs, warn, new InferenceCheckHandler(undetvars));
-
-        // minimize as yet undetermined type variables
-        for (Type t : undetvars)
-            minimizeInst((UndetVar) t, warn);
+        try {
+            rs.checkRawArgumentsAcceptable(env, inferenceContext, argtypes, mt.getParameterTypes(),
+                    allowBoxing, useVarargs, warn, new InferenceCheckHandler(inferenceContext));
 
-        /** Type variables instantiated to bottom */
-        ListBuffer<Type> restvars = new ListBuffer<Type>();
-
-        /** Undet vars instantiated to bottom */
-        final ListBuffer<Type> restundet = new ListBuffer<Type>();
+            // minimize as yet undetermined type variables
+            for (Type t : inferenceContext.undetvars) {
+                minimizeInst((UndetVar)t, warn);
+            }
 
-        /** Instantiated types or TypeVars if under-constrained */
-        ListBuffer<Type> insttypes = new ListBuffer<Type>();
+            checkWithinBounds(inferenceContext, warn);
 
-        /** Instantiated types or UndetVars if under-constrained */
-        ListBuffer<Type> undettypes = new ListBuffer<Type>();
+            mt = (MethodType)inferenceContext.asInstType(mt, types);
 
-        for (Type t : undetvars) {
-            UndetVar uv = (UndetVar)t;
-            if (uv.inst.tag == BOT) {
-                restvars.append(uv.qtype);
-                restundet.append(uv);
-                insttypes.append(uv.qtype);
-                undettypes.append(uv);
-                uv.inst = null;
-            } else {
-                insttypes.append(uv.inst);
-                undettypes.append(uv.inst);
-            }
-        }
-        checkWithinBounds(tvars, undetvars, insttypes.toList(), warn);
-
-        mt = (MethodType)types.subst(mt, tvars, insttypes.toList());
+            List<Type> restvars = inferenceContext.restvars();
 
-        if (!restvars.isEmpty() && resultInfo != null) {
-            List<Type> restInferred =
-                    instantiateUninferred(env.tree.pos(), restundet.toList(), restvars.toList(), mt, resultInfo, warn);
-            checkWithinBounds(tvars, undetvars,
-                           types.subst(insttypes.toList(), restvars.toList(), restInferred), warn);
-            mt = (MethodType)types.subst(mt, restvars.toList(), restInferred);
-            if (rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) {
-                log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt);
+            if (!restvars.isEmpty()) {
+                if (resultInfo != null) {
+                    instantiateUninferred(env.tree.pos(), inferenceContext, mt, resultInfo, warn);
+                    checkWithinBounds(inferenceContext, warn);
+                    mt = (MethodType)inferenceContext.asInstType(mt, types);
+                    if (rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) {
+                        log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt);
+                    }
+                }
             }
+
+            // return instantiated version of method type
+            return mt;
+        } finally {
+            inferenceContext.notifyChange(types);
         }
-
-        if (restvars.isEmpty() || resultInfo != null) {
-            // check that actuals conform to inferred formals
-            checkArgumentsAcceptable(env, capturedArgs, mt.getParameterTypes(), allowBoxing, useVarargs, warn);
-        }
-        // return instantiated version of method type
-        return mt;
     }
     //where
 
         /** inference check handler **/
         class InferenceCheckHandler implements Resolve.MethodCheckHandler {
 
-            List<Type> undetvars;
+            InferenceContext inferenceContext;
 
-            public InferenceCheckHandler(List<Type> undetvars) {
-                this.undetvars = undetvars;
+            public InferenceCheckHandler(InferenceContext inferenceContext) {
+                this.inferenceContext = inferenceContext;
             }
 
             public InapplicableMethodException arityMismatch() {
-                return inferenceException.setMessage("infer.arg.length.mismatch", inferenceVars(undetvars));
+                return inferenceException.setMessage("infer.arg.length.mismatch", inferenceContext.inferenceVars());
             }
             public InapplicableMethodException argumentMismatch(boolean varargs, JCDiagnostic details) {
                 String key = varargs ?
                         "infer.varargs.argument.mismatch" :
                         "infer.no.conforming.assignment.exists";
                 return inferenceException.setMessage(key,
-                        inferenceVars(undetvars), details);
+                        inferenceContext.inferenceVars(), details);
             }
             public InapplicableMethodException inaccessibleVarargs(Symbol location, Type expected) {
                 return inferenceException.setMessage("inaccessible.varargs.type",
@@ -371,51 +329,37 @@
             }
         }
 
-        private void checkArgumentsAcceptable(Env<AttrContext> env, List<Type> actuals, List<Type> formals,
-                boolean allowBoxing, boolean useVarargs, Warner warn) {
-            try {
-                rs.checkRawArgumentsAcceptable(env, actuals, formals,
-                       allowBoxing, useVarargs, warn);
-            }
-            catch (InapplicableMethodException ex) {
-                // inferred method is not applicable
-                throw inferenceException.setMessage(ex.getDiagnostic());
-            }
-        }
-
     /** check that type parameters are within their bounds.
      */
-    void checkWithinBounds(List<Type> tvars,
-                           List<Type> undetvars,
-                           List<Type> arguments,
+    void checkWithinBounds(InferenceContext inferenceContext,
                            Warner warn)
         throws InferenceException {
-        List<Type> args = arguments;
-        for (Type t : undetvars) {
+        List<Type> tvars = inferenceContext.inferenceVars();
+        for (Type t : inferenceContext.undetvars) {
             UndetVar uv = (UndetVar)t;
-            uv.hibounds = types.subst(uv.hibounds, tvars, arguments);
-            uv.lobounds = types.subst(uv.lobounds, tvars, arguments);
-            uv.eq = types.subst(uv.eq, tvars, arguments);
-            checkCompatibleUpperBounds(uv, tvars);
-            if (args.head.tag != TYPEVAR || !args.head.containsAny(tvars)) {
-                Type inst = args.head;
+            uv.hibounds = inferenceContext.asInstTypes(uv.hibounds, types);
+            uv.lobounds = inferenceContext.asInstTypes(uv.lobounds, types);
+            uv.eq = inferenceContext.asInstTypes(uv.eq, types);
+            checkCompatibleUpperBounds(uv, inferenceContext.inferenceVars());
+            if (!inferenceContext.restvars().contains(tvars.head)) {
+                Type inst = inferenceContext.asInstType(t, types);
                 for (Type u : uv.hibounds) {
-                    if (!types.isSubtypeUnchecked(inst, types.subst(u, tvars, undetvars), warn)) {
+                    if (!types.isSubtypeUnchecked(inst, inferenceContext.asFree(u, types), warn)) {
                         reportBoundError(uv, BoundErrorKind.UPPER);
                     }
                 }
                 for (Type l : uv.lobounds) {
-                    if (!types.isSubtypeUnchecked(types.subst(l, tvars, undetvars), inst, warn)) {
+                    if (!types.isSubtypeUnchecked(inferenceContext.asFree(l, types), inst, warn)) {
                         reportBoundError(uv, BoundErrorKind.LOWER);
                     }
                 }
                 for (Type e : uv.eq) {
-                    if (!types.isSameType(inst, types.subst(e, tvars, undetvars))) {
+                    if (!types.isSameType(inst, inferenceContext.asFree(e, types))) {
                         reportBoundError(uv, BoundErrorKind.EQ);
                     }
                 }
             }
-            args = args.tail;
+            tvars = tvars.tail;
         }
     }
 
@@ -423,7 +367,7 @@
         // VGJ: sort of inlined maximizeInst() below.  Adding
         // bounds can cause lobounds that are above hibounds.
         ListBuffer<Type> hiboundsNoVars = ListBuffer.lb();
-        for (Type t : Type.filter(uv.hibounds, errorFilter)) {
+        for (Type t : Type.filter(uv.hibounds, boundFilter)) {
             if (!t.containsAny(tvars)) {
                 hiboundsNoVars.append(t);
             }
@@ -531,4 +475,199 @@
                     return t;
                 }
         };
+
+    /**
+     * Mapping that turns inference variables into undet vars
+     * (used by inference context)
+     */
+    static Mapping fromTypeVarFun = new Mapping("fromTypeVarFun") {
+        public Type apply(Type t) {
+            if (t.tag == TYPEVAR) return new UndetVar(t);
+            else return t.map(this);
+        }
+    };
+
+    /**
+     * An inference context keeps track of the set of variables that are free
+     * in the current context. It provides utility methods for opening/closing
+     * types to their corresponding free/closed forms. It also provide hooks for
+     * attaching deferred post-inference action (see PendingCheck). Finally,
+     * it can be used as an entry point for performing upper/lower bound inference
+     * (see InferenceKind).
+     */
+    static class InferenceContext {
+
+        /**
+        * Single-method-interface for defining inference callbacks. Certain actions
+        * (i.e. subtyping checks) might need to be redone after all inference variables
+        * have been fixed.
+        */
+        interface FreeTypeListener {
+            void typesInferred(InferenceContext inferenceContext);
+        }
+
+        /** list of inference vars as undet vars */
+        List<Type> undetvars;
+
+        /** list of inference vars in this context */
+        List<Type> inferencevars;
+
+        java.util.Map<FreeTypeListener, List<Type>> freeTypeListeners =
+                new java.util.HashMap<FreeTypeListener, List<Type>>();
+
+        List<FreeTypeListener> freetypeListeners = List.nil();
+
+        public InferenceContext(List<Type> inferencevars, Types types) {
+            this.undetvars = Type.map(inferencevars, fromTypeVarFun);
+            this.inferencevars = inferencevars;
+            for (Type t : this.undetvars) {
+                UndetVar uv = (UndetVar)t;
+                uv.hibounds = types.getBounds((TypeVar)uv.qtype);
+            }
+        }
+
+        /**
+         * returns the list of free variables (as type-variables) in this
+         * inference context
+         */
+        List<Type> inferenceVars() {
+            return inferencevars;
+        }
+
+        /**
+         * returns the list of uninstantiated variables (as type-variables) in this
+         * inference context (usually called after instantiate())
+         */
+        List<Type> restvars() {
+            List<Type> undetvars = this.undetvars;
+            ListBuffer<Type> restvars = ListBuffer.lb();
+            for (Type t : instTypes()) {
+                UndetVar uv = (UndetVar)undetvars.head;
+                if (uv.qtype == t) {
+                    restvars.append(t);
+                }
+                undetvars = undetvars.tail;
+            }
+            return restvars.toList();
+        }
+
+        /**
+         * is this type free?
+         */
+        final boolean free(Type t) {
+            return t.containsAny(inferencevars);
+        }
+
+        final boolean free(List<Type> ts) {
+            for (Type t : ts) {
+                if (free(t)) return true;
+            }
+            return false;
+        }
+
+        /**
+         * Returns a list of free variables in a given type
+         */
+        final List<Type> freeVarsIn(Type t) {
+            ListBuffer<Type> buf = ListBuffer.lb();
+            for (Type iv : inferenceVars()) {
+                if (t.contains(iv)) {
+                    buf.add(iv);
+                }
+            }
+            return buf.toList();
+        }
+
+        final List<Type> freeVarsIn(List<Type> ts) {
+            ListBuffer<Type> buf = ListBuffer.lb();
+            for (Type t : ts) {
+                buf.appendList(freeVarsIn(t));
+            }
+            ListBuffer<Type> buf2 = ListBuffer.lb();
+            for (Type t : buf) {
+                if (!buf2.contains(t)) {
+                    buf2.add(t);
+                }
+            }
+            return buf2.toList();
+        }
+
+        /**
+         * Replace all free variables in a given type with corresponding
+         * undet vars (used ahead of subtyping/compatibility checks to allow propagation
+         * of inference constraints).
+         */
+        final Type asFree(Type t, Types types) {
+            return types.subst(t, inferencevars, undetvars);
+        }
+
+        final List<Type> asFree(List<Type> ts, Types types) {
+            ListBuffer<Type> buf = ListBuffer.lb();
+            for (Type t : ts) {
+                buf.append(asFree(t, types));
+            }
+            return buf.toList();
+        }
+
+        List<Type> instTypes() {
+            ListBuffer<Type> buf = ListBuffer.lb();
+            for (Type t : undetvars) {
+                UndetVar uv = (UndetVar)t;
+                buf.append(uv.inst != null ? uv.inst : uv.qtype);
+            }
+            return buf.toList();
+        }
+
+        /**
+         * Replace all free variables in a given type with corresponding
+         * instantiated types - if one or more free variable has not been
+         * fully instantiated, it will still be available in the resulting type.
+         */
+        Type asInstType(Type t, Types types) {
+            return types.subst(t, inferencevars, instTypes());
+        }
+
+        List<Type> asInstTypes(List<Type> ts, Types types) {
+            ListBuffer<Type> buf = ListBuffer.lb();
+            for (Type t : ts) {
+                buf.append(asInstType(t, types));
+            }
+            return buf.toList();
+        }
+
+        /**
+         * Add custom hook for performing post-inference action
+         */
+        void addFreeTypeListener(List<Type> types, FreeTypeListener ftl) {
+            freeTypeListeners.put(ftl, freeVarsIn(types));
+        }
+
+        /**
+         * Mark the inference context as complete and trigger evaluation
+         * of all deferred checks.
+         */
+        void notifyChange(Types types) {
+            InferenceException thrownEx = null;
+            for (Map.Entry<FreeTypeListener, List<Type>> entry :
+                    new HashMap<FreeTypeListener, List<Type>>(freeTypeListeners).entrySet()) {
+                if (!Type.containsAny(entry.getValue(), restvars())) {
+                    try {
+                        entry.getKey().typesInferred(this);
+                        freeTypeListeners.remove(entry.getKey());
+                    } catch (InferenceException ex) {
+                        if (thrownEx == null) {
+                            thrownEx = ex;
+                        }
+                    }
+                }
+            }
+            //inference exception multiplexing - present any inference exception
+            //thrown when processing listeners as a single one
+            if (thrownEx != null) {
+                throw thrownEx;
+            }
+        }
     }
+
+    final InferenceContext emptyContext = new InferenceContext(List.<Type>nil(), types);
+}
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Tue Sep 25 11:53:18 2012 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Tue Sep 25 11:55:34 2012 +0100
@@ -31,6 +31,8 @@
 import com.sun.tools.javac.code.Symbol.*;
 import com.sun.tools.javac.comp.Attr.ResultInfo;
 import com.sun.tools.javac.comp.Check.CheckContext;
+import com.sun.tools.javac.comp.Infer.InferenceContext;
+import com.sun.tools.javac.comp.Infer.InferenceContext.FreeTypeListener;
 import com.sun.tools.javac.comp.Resolve.MethodResolutionContext.Candidate;
 import com.sun.tools.javac.jvm.*;
 import com.sun.tools.javac.tree.*;
@@ -586,7 +588,7 @@
                                 boolean allowBoxing,
                                 boolean useVarargs,
                                 Warner warn) {
-        checkRawArgumentsAcceptable(env, List.<Type>nil(), argtypes, formals,
+        checkRawArgumentsAcceptable(env, infer.emptyContext, argtypes, formals,
                 allowBoxing, useVarargs, warn, resolveHandler);
     }
 
@@ -606,8 +608,8 @@
      *
      * A method check handler (see above) is used in order to report errors.
      */
-    List<Type> checkRawArgumentsAcceptable(Env<AttrContext> env,
-                                List<Type> undetvars,
+    void checkRawArgumentsAcceptable(final Env<AttrContext> env,
+                                final Infer.InferenceContext inferenceContext,
                                 List<Type> argtypes,
                                 List<Type> formals,
                                 boolean allowBoxing,
@@ -623,7 +625,7 @@
         }
 
         while (argtypes.nonEmpty() && formals.head != varargsFormal) {
-            ResultInfo resultInfo = methodCheckResult(formals.head, allowBoxing, false, undetvars, handler, warn);
+            ResultInfo resultInfo = methodCheckResult(formals.head, allowBoxing, false, inferenceContext, handler, warn);
             checkedArgs.append(resultInfo.check(env.tree.pos(), argtypes.head));
             argtypes = argtypes.tail;
             formals = formals.tail;
@@ -638,17 +640,29 @@
             //the last argument of a varargs is _not_ an array type (see JLS 15.12.2.5)
             Type elt = types.elemtype(varargsFormal);
             while (argtypes.nonEmpty()) {
-                ResultInfo resultInfo = methodCheckResult(elt, allowBoxing, true, undetvars, handler, warn);
+                ResultInfo resultInfo = methodCheckResult(elt, allowBoxing, true, inferenceContext, handler, warn);
                 checkedArgs.append(resultInfo.check(env.tree.pos(), argtypes.head));
                 argtypes = argtypes.tail;
             }
             //check varargs element type accessibility
-            if (undetvars.isEmpty() && !isAccessible(env, elt)) {
+            varargsAccessible(env, elt, handler, inferenceContext);
+        }
+    }
+
+    void varargsAccessible(final Env<AttrContext> env, final Type t, final Resolve.MethodCheckHandler handler, final InferenceContext inferenceContext) {
+        if (inferenceContext.free(t)) {
+            inferenceContext.addFreeTypeListener(List.of(t), new FreeTypeListener() {
+                @Override
+                public void typesInferred(InferenceContext inferenceContext) {
+                    varargsAccessible(env, inferenceContext.asInstType(t, types), handler, inferenceContext);
+                }
+            });
+        } else {
+            if (!isAccessible(env, t)) {
                 Symbol location = env.enclClass.sym;
-                throw handler.inaccessibleVarargs(location, elt);
+                throw handler.inaccessibleVarargs(location, t);
             }
         }
-        return checkedArgs.toList();
     }
 
     /**
@@ -659,13 +673,13 @@
 
         MethodCheckHandler handler;
         boolean useVarargs;
-        List<Type> undetvars;
+        Infer.InferenceContext inferenceContext;
         Warner rsWarner;
 
-        public MethodCheckContext(MethodCheckHandler handler, boolean useVarargs, List<Type> undetvars, Warner rsWarner) {
+        public MethodCheckContext(MethodCheckHandler handler, boolean useVarargs, Infer.InferenceContext inferenceContext, Warner rsWarner) {
             this.handler = handler;
             this.useVarargs = useVarargs;
-            this.undetvars = undetvars;
+            this.inferenceContext = inferenceContext;
             this.rsWarner = rsWarner;
         }
 
@@ -676,6 +690,10 @@
         public Warner checkWarner(DiagnosticPosition pos, Type found, Type req) {
             return rsWarner;
         }
+
+        public InferenceContext inferenceContext() {
+            return inferenceContext;
+        }
     }
 
     /**
@@ -684,12 +702,12 @@
      */
     class StrictMethodContext extends MethodCheckContext {
 
-        public StrictMethodContext(MethodCheckHandler handler, boolean useVarargs, List<Type> undetvars, Warner rsWarner) {
-            super(handler, useVarargs, undetvars, rsWarner);
+        public StrictMethodContext(MethodCheckHandler handler, boolean useVarargs, Infer.InferenceContext inferenceContext, Warner rsWarner) {
+            super(handler, useVarargs, inferenceContext, rsWarner);
         }
 
         public boolean compatible(Type found, Type req, Warner warn) {
-            return types.isSubtypeUnchecked(found, infer.asUndetType(req, undetvars), warn);
+            return types.isSubtypeUnchecked(found, inferenceContext.asFree(req, types), warn);
         }
     }
 
@@ -699,12 +717,12 @@
      */
     class LooseMethodContext extends MethodCheckContext {
 
-        public LooseMethodContext(MethodCheckHandler handler, boolean useVarargs, List<Type> undetvars, Warner rsWarner) {
-            super(handler, useVarargs, undetvars, rsWarner);
+        public LooseMethodContext(MethodCheckHandler handler, boolean useVarargs, Infer.InferenceContext inferenceContext, Warner rsWarner) {
+            super(handler, useVarargs, inferenceContext, rsWarner);
         }
 
         public boolean compatible(Type found, Type req, Warner warn) {
-            return types.isConvertible(found, infer.asUndetType(req, undetvars), warn);
+            return types.isConvertible(found, inferenceContext.asFree(req, types), warn);
         }
     }
 
@@ -712,10 +730,10 @@
      * Create a method check context to be used during method applicability check
      */
     ResultInfo methodCheckResult(Type to, boolean allowBoxing, boolean useVarargs,
-            List<Type> undetvars, MethodCheckHandler methodHandler, Warner rsWarner) {
+            Infer.InferenceContext inferenceContext, MethodCheckHandler methodHandler, Warner rsWarner) {
         MethodCheckContext checkContext = allowBoxing ?
-                new LooseMethodContext(methodHandler, useVarargs, undetvars, rsWarner) :
-                new StrictMethodContext(methodHandler, useVarargs, undetvars, rsWarner);
+                new LooseMethodContext(methodHandler, useVarargs, inferenceContext, rsWarner) :
+                new StrictMethodContext(methodHandler, useVarargs, inferenceContext, rsWarner);
         return attr.new ResultInfo(VAL, to, checkContext) {
             @Override
             protected Type check(DiagnosticPosition pos, Type found) {
@@ -735,16 +753,13 @@
             this.diags = diags;
         }
         InapplicableMethodException setMessage() {
-            this.diagnostic = null;
-            return this;
+            return setMessage((JCDiagnostic)null);
         }
         InapplicableMethodException setMessage(String key) {
-            this.diagnostic = key != null ? diags.fragment(key) : null;
-            return this;
+            return setMessage(key != null ? diags.fragment(key) : null);
         }
         InapplicableMethodException setMessage(String key, Object... args) {
-            this.diagnostic = key != null ? diags.fragment(key, args) : null;
-            return this;
+            return setMessage(key != null ? diags.fragment(key, args) : null);
         }
         InapplicableMethodException setMessage(JCDiagnostic diag) {
             this.diagnostic = diag;
--- a/langtools/test/tools/javac/generics/inference/6638712/T6638712c.out	Tue Sep 25 11:53:18 2012 +0100
+++ b/langtools/test/tools/javac/generics/inference/6638712/T6638712c.out	Tue Sep 25 11:55:34 2012 +0100
@@ -1,2 +1,2 @@
-T6638712c.java:16:9: compiler.err.cant.apply.symbol.1: kindname.method, sort, T[],java.util.Comparator<? super T>, java.lang.Enum[],java.util.Comparator<java.lang.Enum<?>>, kindname.class, T6638712c, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.util.Comparator<java.lang.Enum<?>>, java.util.Comparator<? super java.lang.Enum>))
+T6638712c.java:16:9: compiler.err.cant.apply.symbol.1: kindname.method, sort, T[],java.util.Comparator<? super T>, java.lang.Enum[],java.util.Comparator<java.lang.Enum<?>>, kindname.class, T6638712c, (compiler.misc.infer.no.conforming.assignment.exists: T, (compiler.misc.inconvertible.types: java.util.Comparator<java.lang.Enum<?>>, java.util.Comparator<? super java.lang.Enum>))
 1 error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/varargs/6313164/T7175433.java	Tue Sep 25 11:55:34 2012 +0100
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7175433 6313164
+ * @summary Inference cleanup: add helper class to handle inference variables
+ *
+ */
+
+import java.util.List;
+
+class Bar {
+
+    private class Foo { }
+
+    <Z> List<Z> m(Object... o) { T7175433.assertTrue(true); return null; }
+    <Z> List<Z> m(Foo... o) { T7175433.assertTrue(false); return null; }
+
+    Foo getFoo() { return null; }
+}
+
+public class T7175433 {
+
+    static int assertionCount;
+
+    static void assertTrue(boolean b) {
+        assertionCount++;
+        if (!b) {
+            throw new AssertionError();
+        }
+    }
+
+    public static void main(String[] args) {
+        Bar b = new Bar();
+        b.m(b.getFoo());
+        assertTrue(assertionCount == 1);
+    }
+}