src/share/classes/com/sun/tools/javac/comp/Infer.java

Print this page
rev 108 : 6638712: Inference with wildcard types causes selection of inapplicable method
Summary: Added global sanity check in order to make sure that return type inference does not violate bounds constraints
Reviewed-by: jjg

*** 27,36 **** --- 27,37 ---- 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 static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Kinds.*; import static com.sun.tools.javac.code.TypeTags.*;
*** 48,57 **** --- 49,59 ---- /** A value for prototypes that admit any type, including polymorphic ones. */ public static final Type anyPoly = new Type(NONE, null); Symtab syms; Types types; + Resolve rs; public static Infer instance(Context context) { Infer instance = context.get(inferKey); if (instance == null) instance = new Infer(context);
*** 60,106 **** protected Infer(Context context) { context.put(inferKey, this); syms = Symtab.instance(context); types = Types.instance(context); } ! public static class NoInstanceException extends RuntimeException { private static final long serialVersionUID = 0; - boolean isAmbiguous; // exist several incomparable best instances? - JCDiagnostic diagnostic; ! NoInstanceException(boolean isAmbiguous) { this.diagnostic = null; - this.isAmbiguous = isAmbiguous; } ! NoInstanceException setMessage(String key) { ! this.diagnostic = JCDiagnostic.fragment(key); return this; } ! NoInstanceException setMessage(String key, Object arg1) { ! this.diagnostic = JCDiagnostic.fragment(key, arg1); ! return this; } - NoInstanceException setMessage(String key, Object arg1, Object arg2) { - this.diagnostic = JCDiagnostic.fragment(key, arg1, arg2); - return this; } ! NoInstanceException setMessage(String key, Object arg1, Object arg2, Object arg3) { ! this.diagnostic = JCDiagnostic.fragment(key, arg1, arg2, arg3); ! return this; } ! public JCDiagnostic getDiagnostic() { ! return diagnostic; } } private final NoInstanceException ambiguousNoInstanceException = new NoInstanceException(true); private final NoInstanceException unambiguousNoInstanceException = new NoInstanceException(false); /*************************************************************************** * Auxiliary type values and classes ***************************************************************************/ --- 62,116 ---- protected Infer(Context context) { context.put(inferKey, this); syms = Symtab.instance(context); types = Types.instance(context); + rs = Resolve.instance(context); } ! public static class InferenceException extends RuntimeException { private static final long serialVersionUID = 0; JCDiagnostic diagnostic; ! InferenceException() { this.diagnostic = null; } ! InferenceException setMessage(String key, Object... args) { ! this.diagnostic = JCDiagnostic.fragment(key, args); return this; } ! ! public JCDiagnostic getDiagnostic() { ! return diagnostic; } } ! ! public static class NoInstanceException extends InferenceException { ! private static final long serialVersionUID = 1; ! ! boolean isAmbiguous; // exist several incomparable best instances? ! ! NoInstanceException(boolean isAmbiguous) { ! super(); ! this.isAmbiguous = isAmbiguous; } ! } ! ! public static class InvalidInstanceException extends InferenceException { ! private static final long serialVersionUID = 2; ! ! InvalidInstanceException() { ! super(); } } + private final NoInstanceException ambiguousNoInstanceException = new NoInstanceException(true); private final NoInstanceException unambiguousNoInstanceException = new NoInstanceException(false); + private final InvalidInstanceException invalidInstanceException = new InvalidInstanceException(); /*************************************************************************** * Auxiliary type values and classes ***************************************************************************/
*** 245,255 **** * If no instantiation exists, or if several incomparable * best instantiations exist throw a NoInstanceException. */ public Type instantiateExpr(ForAll that, Type to, ! Warner warn) throws NoInstanceException { List<Type> undetvars = Type.map(that.tvars, fromTypeVarFun); for (List<Type> l = undetvars; l.nonEmpty(); l = l.tail) { UndetVar v = (UndetVar) l.head; ListBuffer<Type> hibounds = new ListBuffer<Type>(); for (List<Type> l1 = types.getBounds((TypeVar) v.qtype); l1.nonEmpty(); l1 = l1.tail) { --- 255,265 ---- * If no instantiation exists, or if several incomparable * best instantiations exist throw a NoInstanceException. */ public Type instantiateExpr(ForAll that, Type to, ! Warner warn) throws InferenceException { List<Type> undetvars = Type.map(that.tvars, fromTypeVarFun); for (List<Type> l = undetvars; l.nonEmpty(); l = l.tail) { UndetVar v = (UndetVar) l.head; ListBuffer<Type> hibounds = new ListBuffer<Type>(); for (List<Type> l1 = types.getBounds((TypeVar) v.qtype); l1.nonEmpty(); l1 = l1.tail) {
*** 271,340 **** // check bounds List<Type> targs = Type.map(undetvars, getInstFun); targs = types.subst(targs, that.tvars, targs); checkWithinBounds(that.tvars, targs, warn); ! ! return getInstFun.apply(qtype1); } /** Instantiate method type `mt' by finding instantiations of * `tvars' so that method can be applied to `argtypes'. */ public Type instantiateMethod(List<Type> tvars, MethodType mt, ! List<Type> argtypes, ! boolean allowBoxing, ! boolean useVarargs, ! Warner warn) throws NoInstanceException { //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG List<Type> undetvars = Type.map(tvars, fromTypeVarFun); List<Type> formals = mt.argtypes; ! // instantiate all polymorphic argument types and // set up lower bounds constraints for undetvars Type varargsFormal = useVarargs ? formals.last() : null; ! while (argtypes.nonEmpty() && formals.head != varargsFormal) { ! Type ft = formals.head; ! Type at = argtypes.head.baseType(); ! if (at.tag == FORALL) ! at = instantiateArg((ForAll) at, ft, tvars, warn); ! Type sft = types.subst(ft, tvars, undetvars); boolean works = allowBoxing ! ? types.isConvertible(at, sft, warn) ! : types.isSubtypeUnchecked(at, sft, warn); if (!works) { throw unambiguousNoInstanceException .setMessage("no.conforming.assignment.exists", ! tvars, at, ft); } formals = formals.tail; ! argtypes = argtypes.tail; } if (formals.head != varargsFormal || // not enough args ! !useVarargs && argtypes.nonEmpty()) { // too many args // argument lists differ in length throw unambiguousNoInstanceException .setMessage("arg.length.mismatch"); } // for varargs arguments as well if (useVarargs) { ! Type elt = types.elemtype(varargsFormal); ! Type sft = types.subst(elt, tvars, undetvars); ! while (argtypes.nonEmpty()) { ! Type ft = sft; ! Type at = argtypes.head.baseType(); ! if (at.tag == FORALL) ! at = instantiateArg((ForAll) at, ft, tvars, warn); ! boolean works = types.isConvertible(at, sft, warn); if (!works) { throw unambiguousNoInstanceException .setMessage("no.conforming.assignment.exists", ! tvars, at, ft); } ! argtypes = argtypes.tail; } } // minimize as yet undetermined type variables for (Type t : undetvars) --- 281,356 ---- // check bounds List<Type> targs = Type.map(undetvars, getInstFun); targs = types.subst(targs, that.tvars, targs); checkWithinBounds(that.tvars, targs, warn); ! return that.inst(targs, types); } /** Instantiate method type `mt' by finding instantiations of * `tvars' so that method can be applied to `argtypes'. */ public Type instantiateMethod(List<Type> tvars, MethodType mt, ! final List<Type> argtypes, ! final boolean allowBoxing, ! final boolean useVarargs, ! final Warner warn) throws InferenceException { //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG List<Type> undetvars = Type.map(tvars, fromTypeVarFun); List<Type> formals = mt.argtypes; ! //need to capture exactly once - otherwise subsequent ! //applicability checks might fail ! final List<Type> capturedArgs = types.capture(argtypes); ! List<Type> actuals = capturedArgs; ! List<Type> actualsNoCapture = argtypes; // instantiate all polymorphic argument types and // set up lower bounds constraints for undetvars Type varargsFormal = useVarargs ? formals.last() : null; ! while (actuals.nonEmpty() && formals.head != varargsFormal) { ! Type formal = formals.head; ! Type actual = actuals.head.baseType(); ! Type actualNoCapture = actualsNoCapture.head.baseType(); ! if (actual.tag == FORALL) ! actual = instantiateArg((ForAll)actual, formal, tvars, warn); ! Type undetFormal = types.subst(formal, tvars, undetvars); boolean works = allowBoxing ! ? types.isConvertible(actual, undetFormal, warn) ! : types.isSubtypeUnchecked(actual, undetFormal, warn); if (!works) { throw unambiguousNoInstanceException .setMessage("no.conforming.assignment.exists", ! tvars, actualNoCapture, formal); } formals = formals.tail; ! actuals = actuals.tail; ! actualsNoCapture = actualsNoCapture.tail; } if (formals.head != varargsFormal || // not enough args ! !useVarargs && actuals.nonEmpty()) { // too many args // argument lists differ in length throw unambiguousNoInstanceException .setMessage("arg.length.mismatch"); } // for varargs arguments as well if (useVarargs) { ! Type elemType = types.elemtype(varargsFormal); ! Type elemUndet = types.subst(elemType, tvars, undetvars); ! while (actuals.nonEmpty()) { ! Type actual = actuals.head.baseType(); ! Type actualNoCapture = actualsNoCapture.head.baseType(); ! if (actual.tag == FORALL) ! actual = instantiateArg((ForAll)actual, elemType, tvars, warn); ! boolean works = types.isConvertible(actual, elemUndet, warn); if (!works) { throw unambiguousNoInstanceException .setMessage("no.conforming.assignment.exists", ! tvars, actualNoCapture, elemType); } ! actuals = actuals.tail; ! actualsNoCapture = actualsNoCapture.tail; } } // minimize as yet undetermined type variables for (Type t : undetvars)
*** 361,380 **** undettypes.append(uv.inst); } } checkWithinBounds(tvars, undettypes.toList(), warn); if (!restvars.isEmpty()) { // if there are uninstantiated variables, // quantify result type with them ! mt = new MethodType(mt.argtypes, ! new ForAll(restvars.toList(), mt.restype), ! mt.thrown, syms.methodClass); } ! // return instantiated version of method type ! return types.subst(mt, tvars, insttypes.toList()); } //where /** Try to instantiate argument type `that' to given type `to'. * If this fails, try to insantiate `that' to `to' where --- 377,418 ---- undettypes.append(uv.inst); } } checkWithinBounds(tvars, undettypes.toList(), warn); + mt = (MethodType)types.subst(mt, tvars, insttypes.toList()); + if (!restvars.isEmpty()) { // if there are uninstantiated variables, // quantify result type with them ! final List<Type> inferredTypes = insttypes.toList(); ! final List<Type> all_tvars = tvars; //this is the wrong tvars ! final MethodType mt2 = new MethodType(mt.argtypes, null, mt.thrown, syms.methodClass); ! mt2.restype = new ForAll(restvars.toList(), mt.restype) { ! @Override ! public Type inst(List<Type> inferred, Types types) throws NoInstanceException { ! List<Type> formals = types.subst(mt2.argtypes, tvars, inferred); ! if (!rs.argumentsAcceptable(capturedArgs, formals, ! allowBoxing, useVarargs, warn)) { ! // inferred method is not applicable ! throw invalidInstanceException.setMessage("inferred.do.not.conform.to.params", formals, argtypes); ! } ! // check that inferred bounds conform to their bounds ! checkWithinBounds(all_tvars, ! types.subst(inferredTypes, tvars, inferred), warn); ! return super.inst(inferred, types); ! }}; ! return mt2; ! } ! else if (!rs.argumentsAcceptable(capturedArgs, mt.getParameterTypes(), allowBoxing, useVarargs, warn)) { ! // inferred method is not applicable ! throw invalidInstanceException.setMessage("inferred.do.not.conform.to.params", mt.getParameterTypes(), argtypes); } ! else { // return instantiated version of method type ! return mt; ! } } //where /** Try to instantiate argument type `that' to given type `to'. * If this fails, try to insantiate `that' to `to' where
*** 382,392 **** * by an unknown type. */ private Type instantiateArg(ForAll that, Type to, List<Type> tvars, ! Warner warn) throws NoInstanceException { List<Type> targs; try { return instantiateExpr(that, to, warn); } catch (NoInstanceException ex) { Type to1 = to; --- 420,430 ---- * by an unknown type. */ private Type instantiateArg(ForAll that, Type to, List<Type> tvars, ! Warner warn) throws InferenceException { List<Type> targs; try { return instantiateExpr(that, to, warn); } catch (NoInstanceException ex) { Type to1 = to;
*** 399,416 **** /** check that type parameters are within their bounds. */ private void checkWithinBounds(List<Type> tvars, List<Type> arguments, Warner warn) ! throws NoInstanceException { for (List<Type> tvs = tvars, args = arguments; tvs.nonEmpty(); tvs = tvs.tail, args = args.tail) { if (args.head instanceof UndetVar) continue; List<Type> bounds = types.subst(types.getBounds((TypeVar)tvs.head), tvars, arguments); if (!types.isSubtypeUnchecked(args.head, bounds, warn)) ! throw unambiguousNoInstanceException .setMessage("inferred.do.not.conform.to.bounds", ! arguments, tvars); } } } --- 437,454 ---- /** check that type parameters are within their bounds. */ private void checkWithinBounds(List<Type> tvars, List<Type> arguments, Warner warn) ! throws InvalidInstanceException { for (List<Type> tvs = tvars, args = arguments; tvs.nonEmpty(); tvs = tvs.tail, args = args.tail) { if (args.head instanceof UndetVar) continue; List<Type> bounds = types.subst(types.getBounds((TypeVar)tvs.head), tvars, arguments); if (!types.isSubtypeUnchecked(args.head, bounds, warn)) ! throw invalidInstanceException .setMessage("inferred.do.not.conform.to.bounds", ! args.head, bounds); } } }