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 ||
tvars.head.getUpperBound().isErroneous()) 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);
}
}
check that type parameters are within their bounds. |
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 uv = (UndetVar) l.head;
TypeVar tv = (TypeVar)uv.qtype;
ListBuffer< Type > hibounds = new ListBuffer< Type >();
for (Type t : that.getConstraints(tv, ConstraintKind.EXTENDS)) {
hibounds.append(types.subst(t, that.tvars, undetvars));
}
List< Type > inst = that.getConstraints(tv, ConstraintKind.EQUAL);
if (inst.nonEmpty() && inst.head.tag != BOT) {
uv.inst = inst.head;
}
uv.hibounds = hibounds.toList();
}
Type qtype1 = types.subst(that.qtype, that.tvars, undetvars);
if (!types.isSubtype(qtype1,
qtype1.tag == UNDETVAR ? types.boxedTypeOrType(to) : to)) {
throw unambiguousNoInstanceException
.setMessage("infer.no.conforming.instance.exists",
that.tvars, that.qtype, to);
}
for (List< Type > l = undetvars; l.nonEmpty(); l = l.tail)
maximizeInst((UndetVar) l.head, warn);
// System.out.println(" = " + qtype1.map(getInstFun));//DEBUG
// check bounds
List< Type > targs = Type.map(undetvars, getInstFun);
if (Type.containsAny(targs, that.tvars)) {
//replace uninferred type-vars
targs = types.subst(targs,
that.tvars,
instaniateAsUninferredVars(undetvars, that.tvars));
}
return chk.checkType(warn.pos(), that.inst(targs, types), to);
}
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. |
public Type instantiateMethod(Env<AttrContext> env,
List<Type> tvars,
MethodType mt,
Symbol msym,
List<Type> argtypes,
boolean allowBoxing,
boolean useVarargs,
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;
if (varargsFormal == null &&
actuals.size() != formals.size()) {
throw unambiguousNoInstanceException
.setMessage("infer.arg.length.mismatch");
}
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("infer.no.conforming.assignment.exists",
tvars, actualNoCapture, formal);
}
formals = formals.tail;
actuals = actuals.tail;
actualsNoCapture = actualsNoCapture.tail;
}
if (formals.head != varargsFormal) // not enough args
throw unambiguousNoInstanceException.setMessage("infer.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("infer.no.conforming.assignment.exists",
tvars, actualNoCapture, elemType);
}
actuals = actuals.tail;
actualsNoCapture = actualsNoCapture.tail;
}
}
// minimize as yet undetermined type variables
for (Type t : undetvars)
minimizeInst((UndetVar) t, warn);
/** Type variables instantiated to bottom */
ListBuffer< Type > restvars = new ListBuffer< Type >();
/** Undet vars instantiated to bottom */
final ListBuffer< Type > restundet = new ListBuffer< Type >();
/** Instantiated types or TypeVars if under-constrained */
ListBuffer< Type > insttypes = new ListBuffer< Type >();
/** Instantiated types or UndetVars if under-constrained */
ListBuffer< Type > undettypes = new ListBuffer< Type >();
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, 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
return new UninferredMethodType(mt, restvars.toList()) {
@Override
List< Type > getConstraints(TypeVar tv, ConstraintKind ck) {
for (Type t : restundet.toList()) {
UndetVar uv = (UndetVar)t;
if (uv.qtype == tv) {
switch (ck) {
case EXTENDS: return uv.hibounds.appendList(types.subst(types.getBounds(tv), all_tvars, inferredTypes));
case SUPER: return uv.lobounds;
case EQUAL: return uv.inst != null ? List.of(uv.inst) : List.< Type >nil();
}
}
}
return List.nil();
}
@Override
void check(List< Type > inferred, Types types) throws NoInstanceException {
// check that actuals conform to inferred formals
checkArgumentsAcceptable(env, capturedArgs, getParameterTypes(), allowBoxing, useVarargs, warn);
// check that inferred bounds conform to their bounds
checkWithinBounds(all_tvars,
types.subst(inferredTypes, tvars, inferred), warn);
if (useVarargs) {
chk.checkVararg(env.tree.pos(), getParameterTypes(), msym);
}
}};
}
else {
// check that actuals conform to inferred formals
checkArgumentsAcceptable(env, capturedArgs, mt.getParameterTypes(), allowBoxing, useVarargs, warn);
// return instantiated version of method type
return mt;
}
}
Instantiate method type `mt' by finding instantiations of
`tvars' so that method can be applied to `argtypes'. |
Type instantiatePolymorphicSignatureInstance(Env<AttrContext> env,
Type site,
Name name,
MethodSymbol spMethod,
List<Type> argtypes) {
final Type restype;
//The return type for a polymorphic signature call is computed from
//the enclosing tree E, as follows: if E is a cast, then use the
//target type of the cast expression as a return type; if E is an
//expression statement, the return type is 'void' - otherwise the
//return type is simply 'Object'. A correctness check ensures that
//env.next refers to the lexically enclosing environment in which
//the polymorphic signature call environment is nested.
switch (env.next.tree.getTag()) {
case JCTree.TYPECAST:
JCTypeCast castTree = (JCTypeCast)env.next.tree;
restype = (TreeInfo.skipParens(castTree.expr) == env.tree) ?
castTree.clazz.type :
syms.objectType;
break;
case JCTree.EXEC:
JCTree.JCExpressionStatement execTree =
(JCTree.JCExpressionStatement)env.next.tree;
restype = (TreeInfo.skipParens(execTree.expr) == env.tree) ?
syms.voidType :
syms.objectType;
break;
default:
restype = syms.objectType;
}
List< Type > paramtypes = Type.map(argtypes, implicitArgType);
List< Type > exType = spMethod != null ?
spMethod.getThrownTypes() :
List.of(syms.throwableType); // make it throw all exceptions
MethodType mtype = new MethodType(paramtypes,
restype,
exType,
syms.methodClass);
return mtype;
}
Compute a synthetic method type corresponding to the requested polymorphic
method signature. The target return type is computed from the immediately
enclosing scope surrounding the polymorphic-signature call. |
void minimizeInst(UndetVar that,
Warner warn) throws NoInstanceException {
List< Type > lobounds = Type.filter(that.lobounds, errorFilter);
if (that.inst == null) {
if (lobounds.isEmpty())
that.inst = syms.botType;
else if (lobounds.tail.isEmpty())
that.inst = lobounds.head.isPrimitive() ? syms.errType : lobounds.head;
else {
that.inst = types.lub(lobounds);
}
if (that.inst == null || that.inst.tag == ERROR)
throw ambiguousNoInstanceException
.setMessage("no.unique.minimal.instance.exists",
that.qtype, lobounds);
// VGJ: sort of inlined maximizeInst() below. Adding
// bounds can cause lobounds that are above hibounds.
List< Type > hibounds = Type.filter(that.hibounds, errorFilter);
if (hibounds.isEmpty())
return;
Type hb = null;
if (hibounds.tail.isEmpty())
hb = hibounds.head;
else for (List< Type > bs = hibounds;
bs.nonEmpty() && hb == null;
bs = bs.tail) {
if (isSubClass(bs.head, hibounds))
hb = types.fromUnknownFun.apply(bs.head);
}
if (hb == null ||
!types.isSubtypeUnchecked(hb, hibounds, warn) ||
!types.isSubtypeUnchecked(that.inst, hb, warn))
throw ambiguousNoInstanceException;
}
}
Instantiate undetermined type variable to the lub of all its lower bounds.
Throw a NoInstanceException if this not possible. |