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