--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java Thu Nov 19 17:19:06 2015 +0530
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java Thu Nov 19 16:43:11 2015 +0000
@@ -26,19 +26,20 @@
package com.sun.tools.javac.code;
import java.lang.annotation.Annotation;
+import java.util.ArrayDeque;
import java.util.Collections;
import java.util.EnumMap;
-import java.util.EnumSet;
import java.util.Map;
-import java.util.Set;
import java.util.function.Function;
import javax.lang.model.type.*;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.code.TypeMetadata.Entry;
+import com.sun.tools.javac.comp.Infer.IncorporationAction;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.DefinedBy.Api;
+
import static com.sun.tools.javac.code.BoundKind.*;
import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Kinds.Kind.*;
@@ -1826,17 +1827,15 @@
*/
public interface UndetVarListener {
/** called when some inference variable bounds (of given kinds ibs) change */
- void varChanged(UndetVar uv, Set<InferenceBound> ibs);
+ void varBoundChanged(UndetVar uv, InferenceBound ib, Type bound, boolean update);
+ /** called when the inferred type is set on some inference variable */
+ default void varInstantiated(UndetVar uv) { Assert.error(); }
}
/**
* Inference variable bound kinds
*/
public enum InferenceBound {
- /** upper bounds */
- UPPER {
- public InferenceBound complement() { return LOWER; }
- },
/** lower bounds */
LOWER {
public InferenceBound complement() { return UPPER; }
@@ -1844,16 +1843,38 @@
/** equality constraints */
EQ {
public InferenceBound complement() { return EQ; }
+ },
+ /** upper bounds */
+ UPPER {
+ public InferenceBound complement() { return LOWER; }
};
public abstract InferenceBound complement();
+
+ public boolean lessThan(InferenceBound that) {
+ if (that == this) {
+ return false;
+ } else {
+ switch (that) {
+ case UPPER: return true;
+ case LOWER: return false;
+ case EQ: return (this != UPPER);
+ default:
+ Assert.error("Cannot get here!");
+ return false;
+ }
+ }
+ }
}
+ /** list of incorporation actions (used by the incorporation engine). */
+ public ArrayDeque<IncorporationAction> incorporationActions = new ArrayDeque<>();
+
/** inference variable bounds */
protected Map<InferenceBound, List<Type>> bounds;
/** inference variable's inferred type (set from Infer.java) */
- public Type inst = null;
+ private Type inst = null;
/** number of declared (upper) bounds */
public int declaredCount;
@@ -1866,15 +1887,20 @@
return v.visitUndetVar(this, s);
}
- public UndetVar(TypeVar origin, Types types) {
+ public UndetVar(TypeVar origin, UndetVarListener listener, Types types) {
// This is a synthesized internal type, so we cannot annotate it.
super(UNDETVAR, origin);
+ this.listener = listener;
bounds = new EnumMap<>(InferenceBound.class);
List<Type> declaredBounds = types.getBounds(origin);
declaredCount = declaredBounds.length();
- bounds.put(InferenceBound.UPPER, declaredBounds);
- bounds.put(InferenceBound.LOWER, List.<Type>nil());
- bounds.put(InferenceBound.EQ, List.<Type>nil());
+ bounds.put(InferenceBound.UPPER, List.nil());
+ bounds.put(InferenceBound.LOWER, List.nil());
+ bounds.put(InferenceBound.EQ, List.nil());
+ for (Type t : declaredBounds.reverse()) {
+ //add bound works in reverse order
+ addBound(InferenceBound.UPPER, t, types, true);
+ }
}
@DefinedBy(Api.LANGUAGE_MODEL)
@@ -1904,6 +1930,32 @@
return result;
}
+ /**
+ * Returns a new copy of this undet var.
+ */
+ public UndetVar dup(Types types) {
+ UndetVar uv2 = new UndetVar((TypeVar)qtype, listener, types);
+ dupTo(uv2, types);
+ return uv2;
+ }
+
+ /**
+ * Dumps the contents of this undet var on another undet var.
+ */
+ public void dupTo(UndetVar uv2, Types types) {
+ uv2.listener = null;
+ uv2.bounds.clear();
+ for (InferenceBound ib : InferenceBound.values()) {
+ uv2.bounds.put(ib, List.nil());
+ for (Type t : getBounds(ib)) {
+ uv2.addBound(ib, t, types, true);
+ }
+ }
+ uv2.inst = inst;
+ uv2.listener = listener;
+ uv2.incorporationActions = new ArrayDeque<>(incorporationActions);
+ }
+
@Override
public UndetVar cloneWithMetadata(TypeMetadata md) {
throw new AssertionError("Cannot add metadata to an UndetVar type");
@@ -1919,6 +1971,17 @@
return (inst == null) ? this : inst.baseType();
}
+ public Type getInst() {
+ return inst;
+ }
+
+ public void setInst(Type inst) {
+ this.inst = inst;
+ if (listener != null) {
+ listener.varInstantiated(this);
+ }
+ }
+
/** get all bounds of a given kind */
public List<Type> getBounds(InferenceBound... ibs) {
ListBuffer<Type> buf = new ListBuffer<>();
@@ -1952,13 +2015,14 @@
protected void addBound(InferenceBound ib, Type bound, Types types, boolean update) {
Type bound2 = bound.map(toTypeVarMap).baseType();
List<Type> prevBounds = bounds.get(ib);
+ if (bound == qtype) return;
for (Type b : prevBounds) {
//check for redundancy - use strict version of isSameType on tvars
//(as the standard version will lead to false positives w.r.t. clones ivars)
- if (types.isSameType(b, bound2, true) || bound == qtype) return;
+ if (types.isSameType(b, bound2, true)) return;
}
bounds.put(ib, prevBounds.prepend(bound2));
- notifyChange(EnumSet.of(ib));
+ notifyBoundChange(ib, bound2, false);
}
//where
TypeMapping<Void> toTypeVarMap = new TypeMapping<Void>() {
@@ -1970,16 +2034,14 @@
/** replace types in all bounds - this might trigger listener notification */
public void substBounds(List<Type> from, List<Type> to, Types types) {
- List<Type> instVars = from.diff(to);
- //if set of instantiated ivars is empty, there's nothing to do!
- if (instVars.isEmpty()) return;
- final EnumSet<InferenceBound> boundsChanged = EnumSet.noneOf(InferenceBound.class);
+ final ListBuffer<Pair<InferenceBound, Type>> boundsChanged = new ListBuffer<>();
UndetVarListener prevListener = listener;
try {
//setup new listener for keeping track of changed bounds
listener = new UndetVarListener() {
- public void varChanged(UndetVar uv, Set<InferenceBound> ibs) {
- boundsChanged.addAll(ibs);
+ public void varBoundChanged(UndetVar uv, InferenceBound ib, Type t, boolean _ignored) {
+ Assert.check(uv == UndetVar.this);
+ boundsChanged.add(new Pair<>(ib, t));
}
};
for (Map.Entry<InferenceBound, List<Type>> _entry : bounds.entrySet()) {
@@ -1989,7 +2051,7 @@
ListBuffer<Type> deps = new ListBuffer<>();
//step 1 - re-add bounds that are not dependent on ivars
for (Type t : prevBounds) {
- if (!t.containsAny(instVars)) {
+ if (!t.containsAny(from)) {
newBounds.append(t);
} else {
deps.append(t);
@@ -2004,15 +2066,15 @@
}
} finally {
listener = prevListener;
- if (!boundsChanged.isEmpty()) {
- notifyChange(boundsChanged);
+ for (Pair<InferenceBound, Type> boundUpdate : boundsChanged) {
+ notifyBoundChange(boundUpdate.fst, boundUpdate.snd, true);
}
}
}
- private void notifyChange(EnumSet<InferenceBound> ibs) {
+ private void notifyBoundChange(InferenceBound ib, Type bound, boolean update) {
if (listener != null) {
- listener.varChanged(this, ibs);
+ listener.varBoundChanged(this, ib, bound, update);
}
}
@@ -2029,10 +2091,10 @@
*/
public static class CapturedUndetVar extends UndetVar {
- public CapturedUndetVar(CapturedType origin, Types types) {
- super(origin, types);
+ public CapturedUndetVar(CapturedType origin, UndetVarListener listener, Types types) {
+ super(origin, listener, types);
if (!origin.lower.hasTag(BOT)) {
- bounds.put(InferenceBound.LOWER, List.of(origin.lower));
+ addBound(InferenceBound.LOWER, origin.lower, types, true);
}
}
@@ -2051,6 +2113,12 @@
public boolean isCaptured() {
return true;
}
+
+ public UndetVar dup(Types types) {
+ UndetVar uv2 = new CapturedUndetVar((CapturedType)qtype, listener, types);
+ dupTo(uv2, types);
+ return uv2;
+ }
}
/** Represents NONE.
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java Thu Nov 19 17:19:06 2015 +0530
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java Thu Nov 19 16:43:11 2015 +0000
@@ -25,6 +25,7 @@
package com.sun.tools.javac.comp;
+import com.sun.tools.javac.code.Type.UndetVar.UndetVarListener;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCTypeCast;
import com.sun.tools.javac.tree.TreeInfo;
@@ -57,6 +58,8 @@
import java.util.Map;
import java.util.Properties;
import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
import static com.sun.tools.javac.code.TypeTag.*;
@@ -182,14 +185,14 @@
argtypes, mt.getParameterTypes(), warn);
if (allowGraphInference && resultInfo != null && resultInfo.pt == anyPoly) {
- checkWithinBounds(inferenceContext, warn);
+ doIncorporation(inferenceContext, warn);
//we are inside method attribution - just return a partially inferred type
return new PartiallyInferredMethodType(mt, inferenceContext, env, warn);
} else if (allowGraphInference &&
resultInfo != null &&
!warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
//inject return constraints earlier
- checkWithinBounds(inferenceContext, warn); //propagation
+ doIncorporation(inferenceContext, warn); //propagation
boolean shouldPropagate = resultInfo.checkContext.inferenceContext().free(resultInfo.pt);
@@ -501,7 +504,7 @@
inferenceContext.solve(List.of(from.qtype), new Warner());
inferenceContext.notifyChange();
Type capturedType = resultInfo.checkContext.inferenceContext()
- .cachedCapture(tree, from.inst, false);
+ .cachedCapture(tree, from.getInst(), false);
if (types.isConvertible(capturedType,
resultInfo.checkContext.inferenceContext().asUndetVar(to))) {
//effectively skip additional return-type constraint generation (compatibility)
@@ -523,22 +526,22 @@
TypeSymbol fresh_tvar = new TypeVariableSymbol(Flags.SYNTHETIC, uv.qtype.tsym.name, null, uv.qtype.tsym.owner);
fresh_tvar.type = new TypeVar(fresh_tvar, types.makeIntersectionType(uv.getBounds(InferenceBound.UPPER)), null);
todo.append(uv);
- uv.inst = fresh_tvar.type;
+ uv.setInst(fresh_tvar.type);
} else if (upperBounds.nonEmpty()) {
- uv.inst = types.glb(upperBounds);
+ uv.setInst(types.glb(upperBounds));
} else {
- uv.inst = syms.objectType;
+ uv.setInst(syms.objectType);
}
}
//step 2 - replace fresh tvars in their bounds
List<Type> formals = vars;
for (Type t : todo) {
UndetVar uv = (UndetVar)t;
- TypeVar ct = (TypeVar)uv.inst;
+ TypeVar ct = (TypeVar)uv.getInst();
ct.bound = types.glb(inferenceContext.asInstTypes(types.getBounds(ct)));
if (ct.bound.isErroneous()) {
//report inference error if glb fails
- reportBoundError(uv, BoundErrorKind.BAD_UPPER);
+ reportBoundError(uv, InferenceBound.UPPER);
}
formals = formals.tail;
}
@@ -619,12 +622,12 @@
TypeMapping<Void> fromTypeVarFun = new TypeMapping<Void>() {
@Override
public Type visitTypeVar(TypeVar tv, Void aVoid) {
- return new UndetVar(tv, types);
+ return new UndetVar(tv, incorporationEngine(), types);
}
@Override
public Type visitCapturedType(CapturedType t, Void aVoid) {
- return new CapturedUndetVar(t, types);
+ return new CapturedUndetVar(t, incorporationEngine(), types);
}
};
@@ -671,8 +674,8 @@
List<Type> actualTypeargs = funcInterface.getTypeArguments();
for (Type t : funcInterfaceContext.undetvars) {
UndetVar uv = (UndetVar)t;
- if (uv.inst == null) {
- uv.inst = actualTypeargs.head;
+ if (uv.getInst() == null) {
+ uv.setInst(actualTypeargs.head);
}
actualTypeargs = actualTypeargs.tail;
}
@@ -690,88 +693,399 @@
}
// </editor-fold>
- // <editor-fold defaultstate="collapsed" desc="Bound checking">
+ // <editor-fold defaultstate="collapsed" desc="Incorporation">
+
/**
- * Check bounds and perform incorporation
+ * This class is the root of all incorporation actions.
*/
- void checkWithinBounds(InferenceContext inferenceContext,
- Warner warn) throws InferenceException {
- MultiUndetVarListener mlistener = new MultiUndetVarListener(inferenceContext.undetvars);
- List<Type> saved_undet = inferenceContext.save();
- try {
- while (true) {
- mlistener.reset();
- if (!allowGraphInference) {
- //in legacy mode we lack of transitivity, so bound check
- //cannot be run in parallel with other incoprporation rounds
- for (Type t : inferenceContext.undetvars) {
- UndetVar uv = (UndetVar)t;
- IncorporationStep.CHECK_BOUNDS.apply(uv, inferenceContext, warn);
- }
- }
- for (Type t : inferenceContext.undetvars) {
- UndetVar uv = (UndetVar)t;
- //bound incorporation
- EnumSet<IncorporationStep> incorporationSteps = allowGraphInference ?
- incorporationStepsGraph : incorporationStepsLegacy;
- for (IncorporationStep is : incorporationSteps) {
- if (is.accepts(uv, inferenceContext)) {
- is.apply(uv, inferenceContext, warn);
- }
- }
- }
- if (!mlistener.changed || !allowGraphInference) break;
- }
+ public abstract class IncorporationAction {
+ UndetVar uv;
+ Type t;
+
+ IncorporationAction(UndetVar uv, Type t) {
+ this.uv = uv;
+ this.t = t;
}
- finally {
- mlistener.detach();
- if (incorporationCache.size() == MAX_INCORPORATION_STEPS) {
- inferenceContext.rollback(saved_undet);
- }
- incorporationCache.clear();
+
+ /**
+ * Incorporation action entry-point. Subclasses should define the logic associated with
+ * this incorporation action.
+ */
+ abstract void apply(InferenceContext ic, Warner warn);
+
+ /**
+ * Helper function: perform subtyping through incorporation cache.
+ */
+ boolean isSubtype(Type s, Type t, Warner warn) {
+ return doIncorporationOp(IncorporationBinaryOpKind.IS_SUBTYPE, s, t, warn);
+ }
+
+ /**
+ * Helper function: perform type-equivalence through incorporation cache.
+ */
+ boolean isSameType(Type s, Type t) {
+ return doIncorporationOp(IncorporationBinaryOpKind.IS_SAME_TYPE, s, t, null);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s[undet=%s,t=%s]", getClass().getSimpleName(), uv.qtype, t);
}
}
- //where
- /**
- * This listener keeps track of changes on a group of inference variable
- * bounds. Note: the listener must be detached (calling corresponding
- * method) to make sure that the underlying inference variable is
- * left in a clean state.
- */
- class MultiUndetVarListener implements UndetVar.UndetVarListener {
+
+ /**
+ * Bound-check incorporation action. A newly added bound is checked against existing bounds,
+ * to verify its compatibility; each bound is checked using either subtyping or type equivalence.
+ */
+ class CheckBounds extends IncorporationAction {
- boolean changed;
- List<Type> undetvars;
+ InferenceBound from;
+ BiFunction<InferenceContext, Type, Type> typeFunc;
+ BiPredicate<InferenceContext, Type> optFilter;
+
+ CheckBounds(UndetVar uv, Type t, InferenceBound from) {
+ this(uv, t, InferenceContext::asUndetVar, null, from);
+ }
- public MultiUndetVarListener(List<Type> undetvars) {
- this.undetvars = undetvars;
- for (Type t : undetvars) {
- UndetVar uv = (UndetVar)t;
- uv.listener = this;
- }
- }
+ CheckBounds(UndetVar uv, Type t, BiFunction<InferenceContext, Type, Type> typeFunc,
+ BiPredicate<InferenceContext, Type> typeFilter, InferenceBound from) {
+ super(uv, t);
+ this.from = from;
+ this.typeFunc = typeFunc;
+ this.optFilter = typeFilter;
+ }
- public void varChanged(UndetVar uv, Set<InferenceBound> ibs) {
- //avoid non-termination
- if (incorporationCache.size() < MAX_INCORPORATION_STEPS) {
- changed = true;
- }
- }
-
- void reset() {
- changed = false;
- }
-
- void detach() {
- for (Type t : undetvars) {
- UndetVar uv = (UndetVar)t;
- uv.listener = null;
+ @Override
+ void apply(InferenceContext inferenceContext, Warner warn) {
+ t = typeFunc.apply(inferenceContext, t);
+ if (optFilter != null && optFilter.test(inferenceContext, t)) return;
+ for (InferenceBound to : boundsToCheck()) {
+ for (Type b : uv.getBounds(to)) {
+ b = typeFunc.apply(inferenceContext, b);
+ if (optFilter != null && optFilter.test(inferenceContext, b)) continue;
+ boolean success = checkBound(t, b, from, to, warn);
+ if (!success) {
+ report(from, to);
+ }
}
}
}
- /** max number of incorporation rounds */
- static final int MAX_INCORPORATION_STEPS = 100;
+ /**
+ * The list of bound kinds to be checked.
+ */
+ EnumSet<InferenceBound> boundsToCheck() {
+ return (from == InferenceBound.EQ) ?
+ EnumSet.allOf(InferenceBound.class) :
+ EnumSet.complementOf(EnumSet.of(from));
+ }
+
+ /**
+ * Is source type 's' compatible with target type 't' given source and target bound kinds?
+ */
+ boolean checkBound(Type s, Type t, InferenceBound ib_s, InferenceBound ib_t, Warner warn) {
+ if (ib_s.lessThan(ib_t)) {
+ return isSubtype(s, t, warn);
+ } else if (ib_t.lessThan(ib_s)) {
+ return isSubtype(t, s, warn);
+ } else {
+ return isSameType(s, t);
+ }
+ }
+
+ /**
+ * Report a bound check error.
+ */
+ void report(InferenceBound from, InferenceBound to) {
+ //this is a workaround to preserve compatibility with existing messages
+ if (from == to) {
+ reportBoundError(uv, from);
+ } else if (from == InferenceBound.LOWER || to == InferenceBound.EQ) {
+ reportBoundError(uv, to, from);
+ } else {
+ reportBoundError(uv, from, to);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s[undet=%s,t=%s,bound=%s]", getClass().getSimpleName(), uv.qtype, t, from);
+ }
+ }
+
+ /**
+ * Custom check executed by the legacy incorporation engine. Newly added bounds are checked
+ * against existing eq bounds.
+ */
+ class EqCheckLegacy extends CheckBounds {
+ EqCheckLegacy(UndetVar uv, Type t, InferenceBound from) {
+ super(uv, t, InferenceContext::asInstType, InferenceContext::free, from);
+ }
+
+ @Override
+ EnumSet<InferenceBound> boundsToCheck() {
+ return (from == InferenceBound.EQ) ?
+ EnumSet.allOf(InferenceBound.class) :
+ EnumSet.of(InferenceBound.EQ);
+ }
+ }
+
+ /**
+ * Check that the inferred type conforms to all bounds.
+ */
+ class CheckInst extends CheckBounds {
+
+ EnumSet<InferenceBound> to;
+
+ CheckInst(UndetVar uv, InferenceBound ib, InferenceBound... rest) {
+ super(uv, uv.getInst(), InferenceBound.EQ);
+ this.to = EnumSet.of(ib, rest);
+ }
+
+ @Override
+ EnumSet<InferenceBound> boundsToCheck() {
+ return to;
+ }
+
+ @Override
+ void report(InferenceBound from, InferenceBound to) {
+ reportInstError(uv, to);
+ }
+ }
+
+ /**
+ * Replace undetvars in bounds and check that the inferred type conforms to all bounds.
+ */
+ class SubstBounds extends CheckInst {
+ SubstBounds(UndetVar uv) {
+ super(uv, InferenceBound.LOWER, InferenceBound.EQ, InferenceBound.UPPER);
+ }
+
+ @Override
+ void apply(InferenceContext inferenceContext, Warner warn) {
+ for (Type undet : inferenceContext.undetvars) {
+ //we could filter out variables not mentioning uv2...
+ UndetVar uv2 = (UndetVar)undet;
+ uv2.substBounds(List.of(uv.qtype), List.of(uv.getInst()), types);
+ checkCompatibleUpperBounds(uv2, inferenceContext);
+ }
+ super.apply(inferenceContext, warn);
+ }
+
+ /**
+ * Make sure that the upper bounds we got so far lead to a solvable inference
+ * variable by making sure that a glb exists.
+ */
+ void checkCompatibleUpperBounds(UndetVar uv, InferenceContext inferenceContext) {
+ List<Type> hibounds =
+ Type.filter(uv.getBounds(InferenceBound.UPPER), new BoundFilter(inferenceContext));
+ final Type hb;
+ if (hibounds.isEmpty())
+ hb = syms.objectType;
+ else if (hibounds.tail.isEmpty())
+ hb = hibounds.head;
+ else
+ hb = types.glb(hibounds);
+ if (hb == null || hb.isErroneous())
+ reportBoundError(uv, InferenceBound.UPPER);
+ }
+ }
+
+ /**
+ * Perform pairwise comparison between common generic supertypes of two upper bounds.
+ */
+ class CheckUpperBounds extends IncorporationAction {
+
+ public CheckUpperBounds(UndetVar uv, Type t) {
+ super(uv, t);
+ }
+
+ @Override
+ void apply(InferenceContext inferenceContext, Warner warn) {
+ List<Type> boundList = uv.getBounds(InferenceBound.UPPER).stream()
+ .collect(types.closureCollector(true, types::isSameType));
+ for (Type b2 : boundList) {
+ if (t == b2) continue;
+ /* This wildcard check is temporary workaround. This code may need to be
+ * revisited once spec bug JDK-7034922 is fixed.
+ */
+ if (t != b2 && !t.hasTag(WILDCARD) && !b2.hasTag(WILDCARD)) {
+ for (Pair<Type, Type> commonSupers : getParameterizedSupers(t, b2)) {
+ List<Type> allParamsSuperBound1 = commonSupers.fst.allparams();
+ List<Type> allParamsSuperBound2 = commonSupers.snd.allparams();
+ while (allParamsSuperBound1.nonEmpty() && allParamsSuperBound2.nonEmpty()) {
+ //traverse the list of all params comparing them
+ if (!allParamsSuperBound1.head.hasTag(WILDCARD) &&
+ !allParamsSuperBound2.head.hasTag(WILDCARD)) {
+ if (!isSameType(inferenceContext.asUndetVar(allParamsSuperBound1.head),
+ inferenceContext.asUndetVar(allParamsSuperBound2.head))) {
+ reportBoundError(uv, InferenceBound.UPPER);
+ }
+ }
+ allParamsSuperBound1 = allParamsSuperBound1.tail;
+ allParamsSuperBound2 = allParamsSuperBound2.tail;
+ }
+ Assert.check(allParamsSuperBound1.isEmpty() && allParamsSuperBound2.isEmpty());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Perform propagation of bounds. Given a constraint of the kind {@code alpha <: T}, three
+ * kind of propagation occur:
+ *
+ * <li>T is copied into all matching bounds (i.e. lower/eq bounds) B of alpha such that B=beta (forward propagation)</li>
+ * <li>if T=beta, matching bounds (i.e. upper bounds) of beta are copied into alpha (backwards propagation)</li>
+ * <li>if T=beta, sets a symmetric bound on beta (i.e. beta :> alpha) (symmetric propagation) </li>
+ */
+ class PropagateBounds extends IncorporationAction {
+
+ InferenceBound ib;
+
+ public PropagateBounds(UndetVar uv, Type t, InferenceBound ib) {
+ super(uv, t);
+ this.ib = ib;
+ }
+
+ void apply(InferenceContext inferenceContext, Warner warner) {
+ Type undetT = inferenceContext.asUndetVar(t);
+ if (undetT.hasTag(UNDETVAR) && !((UndetVar)undetT).isCaptured()) {
+ UndetVar uv2 = (UndetVar)undetT;
+ //symmetric propagation
+ uv2.addBound(ib.complement(), uv, types);
+ //backwards propagation
+ for (InferenceBound ib2 : backwards()) {
+ for (Type b : uv2.getBounds(ib2)) {
+ uv.addBound(ib2, b, types);
+ }
+ }
+ }
+ //forward propagation
+ for (InferenceBound ib2 : forward()) {
+ for (Type l : uv.getBounds(ib2)) {
+ Type undet = inferenceContext.asUndetVar(l);
+ if (undet.hasTag(TypeTag.UNDETVAR) && !((UndetVar)undet).isCaptured()) {
+ UndetVar uv2 = (UndetVar)undet;
+ uv2.addBound(ib, inferenceContext.asInstType(t), types);
+ }
+ }
+ }
+ }
+
+ EnumSet<InferenceBound> forward() {
+ return (ib == InferenceBound.EQ) ?
+ EnumSet.of(InferenceBound.EQ) : EnumSet.complementOf(EnumSet.of(ib));
+ }
+
+ EnumSet<InferenceBound> backwards() {
+ return (ib == InferenceBound.EQ) ?
+ EnumSet.allOf(InferenceBound.class) : EnumSet.of(ib);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s[undet=%s,t=%s,bound=%s]", getClass().getSimpleName(), uv.qtype, t, ib);
+ }
+ }
+
+ /**
+ * This class models an incorporation engine. The engine is responsible for listening to
+ * changes in inference variables and register incorporation actions accordingly.
+ */
+ abstract class AbstractIncorporationEngine implements UndetVarListener {
+
+ @Override
+ public void varInstantiated(UndetVar uv) {
+ uv.incorporationActions.addFirst(new SubstBounds(uv));
+ }
+
+ @Override
+ public void varBoundChanged(UndetVar uv, InferenceBound ib, Type bound, boolean update) {
+ if (uv.isCaptured()) return;
+ uv.incorporationActions.addAll(getIncorporationActions(uv, ib, bound, update));
+ }
+
+ abstract List<IncorporationAction> getIncorporationActions(UndetVar uv, InferenceBound ib, Type t, boolean update);
+ }
+
+ /**
+ * A legacy incorporation engine. Used for source <= 7.
+ */
+ AbstractIncorporationEngine legacyEngine = new AbstractIncorporationEngine() {
+
+ List<IncorporationAction> getIncorporationActions(UndetVar uv, InferenceBound ib, Type t, boolean update) {
+ ListBuffer<IncorporationAction> actions = new ListBuffer<>();
+ Type inst = uv.getInst();
+ if (inst != null) {
+ actions.add(new CheckInst(uv, ib));
+ }
+ actions.add(new EqCheckLegacy(uv, t, ib));
+ return actions.toList();
+ }
+ };
+
+ /**
+ * The standard incorporation engine. Used for source >= 8.
+ */
+ AbstractIncorporationEngine graphEngine = new AbstractIncorporationEngine() {
+
+ @Override
+ List<IncorporationAction> getIncorporationActions(UndetVar uv, InferenceBound ib, Type t, boolean update) {
+ ListBuffer<IncorporationAction> actions = new ListBuffer<>();
+ Type inst = uv.getInst();
+ if (inst != null) {
+ actions.add(new CheckInst(uv, ib));
+ }
+ actions.add(new CheckBounds(uv, t, ib));
+
+ if (update) {
+ return actions.toList();
+ }
+
+ if (ib == InferenceBound.UPPER) {
+ actions.add(new CheckUpperBounds(uv, t));
+ }
+
+ actions.add(new PropagateBounds(uv, t, ib));
+
+ return actions.toList();
+ }
+ };
+
+ /**
+ * Get the incorporation engine to be used in this compilation.
+ */
+ AbstractIncorporationEngine incorporationEngine() {
+ return allowGraphInference ? graphEngine : legacyEngine;
+ }
+
+ /** max number of incorporation rounds. */
+ static final int MAX_INCORPORATION_STEPS = 10000;
+
+ /**
+ * Check bounds and perform incorporation.
+ */
+ void doIncorporation(InferenceContext inferenceContext, Warner warn) throws InferenceException {
+ try {
+ boolean progress = true;
+ int round = 0;
+ while (progress && round < MAX_INCORPORATION_STEPS) {
+ progress = false;
+ for (Type t : inferenceContext.undetvars) {
+ UndetVar uv = (UndetVar)t;
+ if (!uv.incorporationActions.isEmpty()) {
+ progress = true;
+ uv.incorporationActions.removeFirst().apply(inferenceContext, warn);
+ }
+ }
+ round++;
+ }
+ } finally {
+ incorporationCache.clear();
+ }
+ }
/* If for two types t and s there is a least upper bound that contains
* parameterized types G1, G2 ... Gn, then there exists supertypes of 't' of the form
@@ -813,407 +1127,14 @@
types.asSuper(t, sup.tsym);
}
- /**
- * This enumeration defines an entry point for doing inference variable
- * bound incorporation - it can be used to inject custom incorporation
- * logic into the basic bound checking routine
- */
- enum IncorporationStep {
- /**
- * Performs basic bound checking - i.e. is the instantiated type for a given
- * inference variable compatible with its bounds?
- */
- CHECK_BOUNDS() {
- public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) {
- Infer infer = inferenceContext.infer;
- uv.substBounds(inferenceContext.inferenceVars(), inferenceContext.instTypes(), infer.types);
- infer.checkCompatibleUpperBounds(uv, inferenceContext);
- if (uv.inst != null) {
- Type inst = uv.inst;
- for (Type u : uv.getBounds(InferenceBound.UPPER)) {
- if (!isSubtype(inst, inferenceContext.asUndetVar(u), warn, infer)) {
- infer.reportBoundError(uv, BoundErrorKind.UPPER);
- }
- }
- for (Type l : uv.getBounds(InferenceBound.LOWER)) {
- if (!isSubtype(inferenceContext.asUndetVar(l), inst, warn, infer)) {
- infer.reportBoundError(uv, BoundErrorKind.LOWER);
- }
- }
- for (Type e : uv.getBounds(InferenceBound.EQ)) {
- if (!isSameType(inst, inferenceContext.asUndetVar(e), infer)) {
- infer.reportBoundError(uv, BoundErrorKind.EQ);
- }
- }
- }
- }
-
- @Override
- boolean accepts(UndetVar uv, InferenceContext inferenceContext) {
- //applies to all undetvars
- return true;
- }
- },
- /**
- * Check consistency of equality constraints. This is a slightly more aggressive
- * inference routine that is designed as to maximize compatibility with JDK 7.
- * Note: this is not used in graph mode.
- */
- EQ_CHECK_LEGACY() {
- public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) {
- Infer infer = inferenceContext.infer;
- Type eq = null;
- for (Type e : uv.getBounds(InferenceBound.EQ)) {
- Assert.check(!inferenceContext.free(e));
- if (eq != null && !isSameType(e, eq, infer)) {
- infer.reportBoundError(uv, BoundErrorKind.EQ);
- }
- eq = e;
- for (Type l : uv.getBounds(InferenceBound.LOWER)) {
- Assert.check(!inferenceContext.free(l));
- if (!isSubtype(l, e, warn, infer)) {
- infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER);
- }
- }
- for (Type u : uv.getBounds(InferenceBound.UPPER)) {
- if (inferenceContext.free(u)) continue;
- if (!isSubtype(e, u, warn, infer)) {
- infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER);
- }
- }
- }
- }
-
- @Override
- boolean accepts(UndetVar uv, InferenceContext inferenceContext) {
- return !uv.isCaptured() && uv.getBounds(InferenceBound.EQ).nonEmpty();
- }
- },
- /**
- * Check consistency of equality constraints.
- */
- EQ_CHECK() {
- @Override
- public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) {
- Infer infer = inferenceContext.infer;
- for (Type e : uv.getBounds(InferenceBound.EQ)) {
- if (e.containsAny(inferenceContext.inferenceVars())) continue;
- for (Type u : uv.getBounds(InferenceBound.UPPER)) {
- if (!isSubtype(e, inferenceContext.asUndetVar(u), warn, infer)) {
- infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER);
- }
- }
- for (Type l : uv.getBounds(InferenceBound.LOWER)) {
- if (!isSubtype(inferenceContext.asUndetVar(l), e, warn, infer)) {
- infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER);
- }
- }
- }
- }
-
- @Override
- boolean accepts(UndetVar uv, InferenceContext inferenceContext) {
- return !uv.isCaptured() && uv.getBounds(InferenceBound.EQ).nonEmpty();
- }
- },
- /**
- * Given a bound set containing {@code alpha <: T} and {@code alpha :> S}
- * perform {@code S <: T} (which could lead to new bounds).
- */
- CROSS_UPPER_LOWER() {
- public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) {
- Infer infer = inferenceContext.infer;
- for (Type b1 : uv.getBounds(InferenceBound.UPPER)) {
- for (Type b2 : uv.getBounds(InferenceBound.LOWER)) {
- if (!isSubtype(inferenceContext.asUndetVar(b2), inferenceContext.asUndetVar(b1), warn , infer)) {
- infer.reportBoundError(uv, BoundErrorKind.BAD_UPPER_LOWER);
- }
- }
- }
- }
-
- @Override
- boolean accepts(UndetVar uv, InferenceContext inferenceContext) {
- return !uv.isCaptured() &&
- uv.getBounds(InferenceBound.UPPER).nonEmpty() &&
- uv.getBounds(InferenceBound.LOWER).nonEmpty();
- }
- },
- /**
- * Given a bound set containing {@code alpha <: T} and {@code alpha == S}
- * perform {@code S <: T} (which could lead to new bounds).
- */
- CROSS_UPPER_EQ() {
- public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) {
- Infer infer = inferenceContext.infer;
- for (Type b1 : uv.getBounds(InferenceBound.UPPER)) {
- for (Type b2 : uv.getBounds(InferenceBound.EQ)) {
- if (!isSubtype(inferenceContext.asUndetVar(b2), inferenceContext.asUndetVar(b1), warn, infer)) {
- infer.reportBoundError(uv, BoundErrorKind.BAD_UPPER_EQUAL);
- }
- }
- }
- }
-
- @Override
- boolean accepts(UndetVar uv, InferenceContext inferenceContext) {
- return !uv.isCaptured() &&
- uv.getBounds(InferenceBound.EQ).nonEmpty() &&
- uv.getBounds(InferenceBound.UPPER).nonEmpty();
- }
- },
- /**
- * Given a bound set containing {@code alpha :> S} and {@code alpha == T}
- * perform {@code S <: T} (which could lead to new bounds).
- */
- CROSS_EQ_LOWER() {
- public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) {
- Infer infer = inferenceContext.infer;
- for (Type b1 : uv.getBounds(InferenceBound.EQ)) {
- for (Type b2 : uv.getBounds(InferenceBound.LOWER)) {
- if (!isSubtype(inferenceContext.asUndetVar(b2), inferenceContext.asUndetVar(b1), warn, infer)) {
- infer.reportBoundError(uv, BoundErrorKind.BAD_EQUAL_LOWER);
- }
- }
- }
- }
-
- @Override
- boolean accepts(UndetVar uv, InferenceContext inferenceContext) {
- return !uv.isCaptured() &&
- uv.getBounds(InferenceBound.EQ).nonEmpty() &&
- uv.getBounds(InferenceBound.LOWER).nonEmpty();
- }
- },
- /**
- * Given a bound set containing {@code alpha <: P<T>} and
- * {@code alpha <: P<S>} where P is a parameterized type,
- * perform {@code T = S} (which could lead to new bounds).
- */
- CROSS_UPPER_UPPER() {
- @Override
- public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) {
- Infer infer = inferenceContext.infer;
- List<Type> boundList = uv.getBounds(InferenceBound.UPPER).stream()
- .collect(infer.types.closureCollector(true, infer.types::isSameType));
- List<Type> boundListTail = boundList.tail;
- while (boundList.nonEmpty()) {
- List<Type> tmpTail = boundListTail;
- while (tmpTail.nonEmpty()) {
- Type b1 = boundList.head;
- Type b2 = tmpTail.head;
- /* This wildcard check is temporary workaround. This code may need to be
- * revisited once spec bug JDK-7034922 is fixed.
- */
- if (b1 != b2 && !b1.hasTag(WILDCARD) && !b2.hasTag(WILDCARD)) {
- for (Pair<Type, Type> commonSupers : infer.getParameterizedSupers(b1, b2)) {
- List<Type> allParamsSuperBound1 = commonSupers.fst.allparams();
- List<Type> allParamsSuperBound2 = commonSupers.snd.allparams();
- while (allParamsSuperBound1.nonEmpty() && allParamsSuperBound2.nonEmpty()) {
- //traverse the list of all params comparing them
- if (!allParamsSuperBound1.head.hasTag(WILDCARD) &&
- !allParamsSuperBound2.head.hasTag(WILDCARD)) {
- if (!isSameType(inferenceContext.asUndetVar(allParamsSuperBound1.head),
- inferenceContext.asUndetVar(allParamsSuperBound2.head), infer)) {
- infer.reportBoundError(uv, BoundErrorKind.BAD_UPPER);
- }
- }
- allParamsSuperBound1 = allParamsSuperBound1.tail;
- allParamsSuperBound2 = allParamsSuperBound2.tail;
- }
- Assert.check(allParamsSuperBound1.isEmpty() && allParamsSuperBound2.isEmpty());
- }
- }
- tmpTail = tmpTail.tail;
- }
- boundList = boundList.tail;
- boundListTail = boundList.tail;
- }
- }
-
- @Override
- boolean accepts(UndetVar uv, InferenceContext inferenceContext) {
- return !uv.isCaptured() &&
- uv.getBounds(InferenceBound.UPPER).nonEmpty();
- }
- },
- /**
- * Given a bound set containing {@code alpha == S} and {@code alpha == T}
- * perform {@code S == T} (which could lead to new bounds).
- */
- CROSS_EQ_EQ() {
- public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) {
- Infer infer = inferenceContext.infer;
- for (Type b1 : uv.getBounds(InferenceBound.EQ)) {
- for (Type b2 : uv.getBounds(InferenceBound.EQ)) {
- if (b1 != b2) {
- if (!isSameType(inferenceContext.asUndetVar(b2), inferenceContext.asUndetVar(b1), infer)) {
- infer.reportBoundError(uv, BoundErrorKind.BAD_EQ);
- }
- }
- }
- }
- }
-
- @Override
- boolean accepts(UndetVar uv, InferenceContext inferenceContext) {
- return !uv.isCaptured() &&
- uv.getBounds(InferenceBound.EQ).nonEmpty();
- }
- },
- /**
- * Given a bound set containing {@code alpha <: beta} propagate lower bounds
- * from alpha to beta; also propagate upper bounds from beta to alpha.
- */
- PROP_UPPER() {
- public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) {
- Infer infer = inferenceContext.infer;
- for (Type b : uv.getBounds(InferenceBound.UPPER)) {
- if (inferenceContext.inferenceVars().contains(b)) {
- UndetVar uv2 = (UndetVar)inferenceContext.asUndetVar(b);
- if (uv2.isCaptured()) continue;
- //alpha <: beta
- //0. set beta :> alpha
- addBound(InferenceBound.LOWER, uv2, inferenceContext.asInstType(uv.qtype), infer);
- //1. copy alpha's lower to beta's
- for (Type l : uv.getBounds(InferenceBound.LOWER)) {
- addBound(InferenceBound.LOWER, uv2, inferenceContext.asInstType(l), infer);
- }
- //2. copy beta's upper to alpha's
- for (Type u : uv2.getBounds(InferenceBound.UPPER)) {
- addBound(InferenceBound.UPPER, uv, inferenceContext.asInstType(u), infer);
- }
- }
- }
- }
-
- @Override
- boolean accepts(UndetVar uv, InferenceContext inferenceContext) {
- return !uv.isCaptured() &&
- uv.getBounds(InferenceBound.UPPER).nonEmpty();
- }
- },
- /**
- * Given a bound set containing {@code alpha :> beta} propagate lower bounds
- * from beta to alpha; also propagate upper bounds from alpha to beta.
- */
- PROP_LOWER() {
- public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) {
- Infer infer = inferenceContext.infer;
- for (Type b : uv.getBounds(InferenceBound.LOWER)) {
- if (inferenceContext.inferenceVars().contains(b)) {
- UndetVar uv2 = (UndetVar)inferenceContext.asUndetVar(b);
- if (uv2.isCaptured()) continue;
- //alpha :> beta
- //0. set beta <: alpha
- addBound(InferenceBound.UPPER, uv2, inferenceContext.asInstType(uv.qtype), infer);
- //1. copy alpha's upper to beta's
- for (Type u : uv.getBounds(InferenceBound.UPPER)) {
- addBound(InferenceBound.UPPER, uv2, inferenceContext.asInstType(u), infer);
- }
- //2. copy beta's lower to alpha's
- for (Type l : uv2.getBounds(InferenceBound.LOWER)) {
- addBound(InferenceBound.LOWER, uv, inferenceContext.asInstType(l), infer);
- }
- }
- }
- }
-
- @Override
- boolean accepts(UndetVar uv, InferenceContext inferenceContext) {
- return !uv.isCaptured() &&
- uv.getBounds(InferenceBound.LOWER).nonEmpty();
- }
- },
- /**
- * Given a bound set containing {@code alpha == beta} propagate lower/upper
- * bounds from alpha to beta and back.
- */
- PROP_EQ() {
- public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) {
- Infer infer = inferenceContext.infer;
- for (Type b : uv.getBounds(InferenceBound.EQ)) {
- if (inferenceContext.inferenceVars().contains(b)) {
- UndetVar uv2 = (UndetVar)inferenceContext.asUndetVar(b);
- if (uv2.isCaptured()) continue;
- //alpha == beta
- //0. set beta == alpha
- addBound(InferenceBound.EQ, uv2, inferenceContext.asInstType(uv.qtype), infer);
- //1. copy all alpha's bounds to beta's
- for (InferenceBound ib : InferenceBound.values()) {
- for (Type b2 : uv.getBounds(ib)) {
- if (b2 != uv2) {
- addBound(ib, uv2, inferenceContext.asInstType(b2), infer);
- }
- }
- }
- //2. copy all beta's bounds to alpha's
- for (InferenceBound ib : InferenceBound.values()) {
- for (Type b2 : uv2.getBounds(ib)) {
- if (b2 != uv) {
- addBound(ib, uv, inferenceContext.asInstType(b2), infer);
- }
- }
- }
- }
- }
- }
-
- @Override
- boolean accepts(UndetVar uv, InferenceContext inferenceContext) {
- return !uv.isCaptured() &&
- uv.getBounds(InferenceBound.EQ).nonEmpty();
- }
- };
-
- abstract void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn);
-
- boolean accepts(UndetVar uv, InferenceContext inferenceContext) {
- return !uv.isCaptured();
- }
-
- boolean isSubtype(Type s, Type t, Warner warn, Infer infer) {
- return doIncorporationOp(IncorporationBinaryOpKind.IS_SUBTYPE, s, t, warn, infer);
- }
-
- boolean isSameType(Type s, Type t, Infer infer) {
- return doIncorporationOp(IncorporationBinaryOpKind.IS_SAME_TYPE, s, t, null, infer);
- }
-
- void addBound(InferenceBound ib, UndetVar uv, Type b, Infer infer) {
- doIncorporationOp(opFor(ib), uv, b, null, infer);
- }
-
- IncorporationBinaryOpKind opFor(InferenceBound boundKind) {
- switch (boundKind) {
- case EQ:
- return IncorporationBinaryOpKind.ADD_EQ_BOUND;
- case LOWER:
- return IncorporationBinaryOpKind.ADD_LOWER_BOUND;
- case UPPER:
- return IncorporationBinaryOpKind.ADD_UPPER_BOUND;
- default:
- Assert.error("Can't get here!");
- return null;
- }
- }
-
- boolean doIncorporationOp(IncorporationBinaryOpKind opKind, Type op1, Type op2, Warner warn, Infer infer) {
- IncorporationBinaryOp newOp = infer.new IncorporationBinaryOp(opKind, op1, op2);
- Boolean res = infer.incorporationCache.get(newOp);
+ boolean doIncorporationOp(IncorporationBinaryOpKind opKind, Type op1, Type op2, Warner warn) {
+ IncorporationBinaryOp newOp = new IncorporationBinaryOp(opKind, op1, op2);
+ Boolean res = incorporationCache.get(newOp);
if (res == null) {
- infer.incorporationCache.put(newOp, res = newOp.apply(warn));
+ incorporationCache.put(newOp, res = newOp.apply(warn));
}
return res;
}
- }
-
- /** incorporation steps to be executed when running in legacy mode */
- EnumSet<IncorporationStep> incorporationStepsLegacy = EnumSet.of(IncorporationStep.EQ_CHECK_LEGACY);
-
- /** incorporation steps to be executed when running in graph mode */
- EnumSet<IncorporationStep> incorporationStepsGraph =
- EnumSet.complementOf(EnumSet.of(IncorporationStep.EQ_CHECK_LEGACY));
/**
* Three kinds of basic operation are supported as part of an incorporation step:
@@ -1232,30 +1153,6 @@
boolean apply(Type op1, Type op2, Warner warn, Types types) {
return types.isSameType(op1, op2);
}
- },
- ADD_UPPER_BOUND() {
- @Override
- boolean apply(Type op1, Type op2, Warner warn, Types types) {
- UndetVar uv = (UndetVar)op1;
- uv.addBound(InferenceBound.UPPER, op2, types);
- return true;
- }
- },
- ADD_LOWER_BOUND() {
- @Override
- boolean apply(Type op1, Type op2, Warner warn, Types types) {
- UndetVar uv = (UndetVar)op1;
- uv.addBound(InferenceBound.LOWER, op2, types);
- return true;
- }
- },
- ADD_EQ_BOUND() {
- @Override
- boolean apply(Type op1, Type op2, Warner warn, Types types) {
- UndetVar uv = (UndetVar)op1;
- uv.addBound(InferenceBound.EQ, op2, types);
- return true;
- }
};
abstract boolean apply(Type op1, Type op2, Warner warn, Types types);
@@ -1296,9 +1193,9 @@
public int hashCode() {
int result = opKind.hashCode();
result *= 127;
- result += types.hashCode(op1, true);
+ result += types.hashCode(op1);
result *= 127;
- result += types.hashCode(op2, true);
+ result += types.hashCode(op2);
return result;
}
@@ -1310,152 +1207,59 @@
/** an incorporation cache keeps track of all executed incorporation-related operations */
Map<IncorporationBinaryOp, Boolean> incorporationCache = new HashMap<>();
- /**
- * Make sure that the upper bounds we got so far lead to a solvable inference
- * variable by making sure that a glb exists.
- */
- void checkCompatibleUpperBounds(UndetVar uv, InferenceContext inferenceContext) {
- List<Type> hibounds =
- Type.filter(uv.getBounds(InferenceBound.UPPER), new BoundFilter(inferenceContext));
- Type hb = null;
- if (hibounds.isEmpty())
- hb = syms.objectType;
- else if (hibounds.tail.isEmpty())
- hb = hibounds.head;
- else
- hb = types.glb(hibounds);
- if (hb == null || hb.isErroneous())
- reportBoundError(uv, BoundErrorKind.BAD_UPPER);
+ protected static class BoundFilter implements Filter<Type> {
+
+ InferenceContext inferenceContext;
+
+ public BoundFilter(InferenceContext inferenceContext) {
+ this.inferenceContext = inferenceContext;
+ }
+
+ @Override
+ public boolean accepts(Type t) {
+ return !t.isErroneous() && !inferenceContext.free(t) &&
+ !t.hasTag(BOT);
+ }
}
- //where
- protected static class BoundFilter implements Filter<Type> {
-
- InferenceContext inferenceContext;
-
- public BoundFilter(InferenceContext inferenceContext) {
- this.inferenceContext = inferenceContext;
- }
-
- @Override
- public boolean accepts(Type t) {
- return !t.isErroneous() && !inferenceContext.free(t) &&
- !t.hasTag(BOT);
- }
- }
/**
- * This enumeration defines all possible bound-checking related errors.
+ * Incorporation error: mismatch between inferred type and given bound.
*/
- enum BoundErrorKind {
- /**
- * The (uninstantiated) inference variable has incompatible upper bounds.
- */
- BAD_UPPER() {
- @Override
- InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
- return ex.setMessage("incompatible.upper.bounds", uv.qtype,
- uv.getBounds(InferenceBound.UPPER));
- }
- },
- /**
- * The (uninstantiated) inference variable has incompatible equality constraints.
- */
- BAD_EQ() {
- @Override
- InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
- return ex.setMessage("incompatible.eq.bounds", uv.qtype,
- uv.getBounds(InferenceBound.EQ));
- }
- },
- /**
- * The (uninstantiated) inference variable has incompatible upper lower bounds.
- */
- BAD_UPPER_LOWER() {
- @Override
- InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
- return ex.setMessage("incompatible.upper.lower.bounds", uv.qtype,
- uv.getBounds(InferenceBound.UPPER), uv.getBounds(InferenceBound.LOWER));
- }
- },
- /**
- * The (uninstantiated) inference variable has incompatible upper equal bounds.
- */
- BAD_UPPER_EQUAL() {
- @Override
- InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
- return ex.setMessage("incompatible.upper.eq.bounds", uv.qtype,
- uv.getBounds(InferenceBound.UPPER), uv.getBounds(InferenceBound.EQ));
- }
- },
- /**
- * The (uninstantiated) inference variable has incompatible upper equal bounds.
- */
- BAD_EQUAL_LOWER() {
- @Override
- InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
- return ex.setMessage("incompatible.eq.lower.bounds", uv.qtype,
- uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.LOWER));
- }
- },
- /**
- * An equality constraint is not compatible with an upper bound.
- */
- BAD_EQ_UPPER() {
- @Override
- InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
- return ex.setMessage("incompatible.eq.upper.bounds", uv.qtype,
- uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.UPPER));
- }
- },
- /**
- * An equality constraint is not compatible with a lower bound.
- */
- BAD_EQ_LOWER() {
- @Override
- InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
- return ex.setMessage("incompatible.eq.lower.bounds", uv.qtype,
- uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.LOWER));
- }
- },
- /**
- * Instantiated inference variable is not compatible with an upper bound.
- */
- UPPER() {
- @Override
- InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
- return ex.setMessage("inferred.do.not.conform.to.upper.bounds", uv.inst,
- uv.getBounds(InferenceBound.UPPER));
- }
- },
- /**
- * Instantiated inference variable is not compatible with a lower bound.
- */
- LOWER() {
- @Override
- InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
- return ex.setMessage("inferred.do.not.conform.to.lower.bounds", uv.inst,
- uv.getBounds(InferenceBound.LOWER));
- }
- },
- /**
- * Instantiated inference variable is not compatible with an equality constraint.
- */
- EQ() {
- @Override
- InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
- return ex.setMessage("inferred.do.not.conform.to.eq.bounds", uv.inst,
- uv.getBounds(InferenceBound.EQ));
- }
- };
-
- abstract InapplicableMethodException setMessage(InferenceException ex, UndetVar uv);
+ void reportInstError(UndetVar uv, InferenceBound ib) {
+ reportInferenceError(
+ String.format("inferred.do.not.conform.to.%s.bounds", StringUtils.toLowerCase(ib.name())),
+ uv.getInst(),
+ uv.getBounds(ib));
}
/**
- * Report a bound-checking error of given kind
+ * Incorporation error: mismatch between two (or more) bounds of same kind.
+ */
+ void reportBoundError(UndetVar uv, InferenceBound ib) {
+ reportInferenceError(
+ String.format("incompatible.%s.bounds", StringUtils.toLowerCase(ib.name())),
+ uv.qtype,
+ uv.getBounds(ib));
+ }
+
+ /**
+ * Incorporation error: mismatch between two (or more) bounds of different kinds.
*/
- void reportBoundError(UndetVar uv, BoundErrorKind bk) {
- throw bk.setMessage(inferenceException, uv);
+ void reportBoundError(UndetVar uv, InferenceBound ib1, InferenceBound ib2) {
+ reportInferenceError(
+ String.format("incompatible.%s.%s.bounds",
+ StringUtils.toLowerCase(ib1.name()),
+ StringUtils.toLowerCase(ib2.name())),
+ uv.qtype,
+ uv.getBounds(ib1),
+ uv.getBounds(ib2));
+ }
+
+ /**
+ * Helper method: reports an inference error.
+ */
+ void reportInferenceError(String key, Object... args) {
+ throw inferenceException.setMessage(key, args);
}
// </editor-fold>
@@ -1502,41 +1306,6 @@
}
return g.nodes.get(0);
}
-
- boolean isSubtype(Type s, Type t, Warner warn, Infer infer) {
- return doIncorporationOp(IncorporationBinaryOpKind.IS_SUBTYPE, s, t, warn, infer);
- }
-
- boolean isSameType(Type s, Type t, Infer infer) {
- return doIncorporationOp(IncorporationBinaryOpKind.IS_SAME_TYPE, s, t, null, infer);
- }
-
- void addBound(InferenceBound ib, UndetVar uv, Type b, Infer infer) {
- doIncorporationOp(opFor(ib), uv, b, null, infer);
- }
-
- IncorporationBinaryOpKind opFor(InferenceBound boundKind) {
- switch (boundKind) {
- case EQ:
- return IncorporationBinaryOpKind.ADD_EQ_BOUND;
- case LOWER:
- return IncorporationBinaryOpKind.ADD_LOWER_BOUND;
- case UPPER:
- return IncorporationBinaryOpKind.ADD_UPPER_BOUND;
- default:
- Assert.error("Can't get here!");
- return null;
- }
- }
-
- boolean doIncorporationOp(IncorporationBinaryOpKind opKind, Type op1, Type op2, Warner warn, Infer infer) {
- IncorporationBinaryOp newOp = infer.new IncorporationBinaryOp(opKind, op1, op2);
- Boolean res = infer.incorporationCache.get(newOp);
- if (res == null) {
- infer.incorporationCache.put(newOp, res = newOp.apply(warn));
- }
- return res;
- }
}
/**
@@ -1856,7 +1625,7 @@
* depends on the selected solver strategy.
*/
void solve(GraphStrategy sstrategy) {
- checkWithinBounds(inferenceContext, warn); //initial propagation of bounds
+ doIncorporation(inferenceContext, warn); //initial propagation of bounds
InferenceGraph inferenceGraph = new InferenceGraph(stuckDeps);
while (!sstrategy.done()) {
if (dependenciesFolder != null) {
@@ -1871,8 +1640,8 @@
outer: while (Type.containsAny(inferenceContext.restvars(), varsToSolve)) {
//for each inference phase
for (GraphInferenceSteps step : GraphInferenceSteps.values()) {
- if (inferenceContext.solveBasic(varsToSolve, step.steps)) {
- checkWithinBounds(inferenceContext, warn);
+ if (inferenceContext.solveBasic(varsToSolve, step.steps).nonEmpty()) {
+ doIncorporation(inferenceContext, warn);
continue outer;
}
}
@@ -1884,7 +1653,7 @@
//did we fail because of interdependent ivars?
inferenceContext.rollback(saved_undet);
instantiateAsUninferredVars(varsToSolve, inferenceContext);
- checkWithinBounds(inferenceContext, warn);
+ doIncorporation(inferenceContext, warn);
}
inferenceGraph.deleteNode(nodeToSolve);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/generics/inference/8067767/T8067767.java Thu Nov 19 16:43:11 2015 +0000
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 8067767
+ * @summary type inference performance regression
+ * @compile T8067767.java
+ */
+class T8067767 {
+
+ static class Pair<A, B> {
+ static <A, B> Pair<A, B> of(A a, B b) {
+ throw new RuntimeException();
+ }
+ }
+
+ static class List<T> {
+ static <T> List<T> of(T... tx) {
+ throw new RuntimeException();
+ }
+ }
+
+ static final List<Pair<String, String>> PAIRS = List.of(
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"),
+ Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"), Pair.of("a", "b"));
+}