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
--- 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);
+ }
+}