summaryrefslogtreecommitdiff
path: root/sources/scala/tools/scalac/typechecker/Infer.scala
diff options
context:
space:
mode:
Diffstat (limited to 'sources/scala/tools/scalac/typechecker/Infer.scala')
-rw-r--r--sources/scala/tools/scalac/typechecker/Infer.scala889
1 files changed, 889 insertions, 0 deletions
diff --git a/sources/scala/tools/scalac/typechecker/Infer.scala b/sources/scala/tools/scalac/typechecker/Infer.scala
new file mode 100644
index 0000000000..1886a42822
--- /dev/null
+++ b/sources/scala/tools/scalac/typechecker/Infer.scala
@@ -0,0 +1,889 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scala.tools.scalac.typechecker;
+
+import java.lang.Object;
+
+import scalac.Global;
+import scalac.ApplicationError;
+import scalac.util._;
+import scalac.ast._;
+import scalac.symtab._;
+
+import scala.tools.scalac.util.NewArray;
+
+class Infer(global: Global, gen: TreeGen, make: TreeFactory) {
+
+ import Modifiers._, Kinds._;
+
+ val definitions: Definitions = global.definitions;
+ val substituter: Substituter = new Substituter(global, gen);
+
+ def this(trans: Transformer) = this(trans.global, trans.gen, trans.make);
+
+// Error messages -------------------------------------------------------------
+
+ def applyErrorMsg(msg1: String, fn: Tree, msg2: String, argtypes: Array[Type], pt: Type): String =
+ msg1 + toString(fn.symbol(), fn.getType()) + msg2 +
+ ArrayApply.toString(argtypes.asInstanceOf[Array[Object]], "(", ",", ")") +
+ (if (pt == Type.AnyType) "" else " with expected result type " + pt);
+
+ def typeErrorMsg(msg: String, found: Type, req: Type): String =
+ msg + ";\n found : " + found.toLongString() + "\n required: " + req;
+
+ def overloadResolveErrorMsg(sym1: Symbol, tpe1: Type, sym2: Symbol, tpe2: Type): String =
+ "ambiguous reference to overloaded definition,\n" +
+ "both " + sym1 + ": " + tpe1 + "\n" +
+ "and " + sym2 + ": " + tpe2 + "\nmatch";
+
+ /** Give a string representation of symbol `sym' with type `tp'
+ * for error diagnostics. `sym' may be null.
+ */
+ def toString(sym: Symbol, tp: Type): String =
+ (if (tp.isInstanceOf[Type$OverloadedType]) "overloaded " else "") +
+ (if (sym == null) "expression" else sym) + " of type " + tp;
+
+// Variance calculation ----------------------------------------------------------
+
+ private def flip(v: int): int = {
+ if (v == COVARIANT) CONTRAVARIANT;
+ else if (v == CONTRAVARIANT) COVARIANT;
+ else v
+ }
+
+ private def cut(v: int): int = {
+ if (v == VARIANCES) v
+ else 0
+ }
+
+ /** Compute variances of all type parameters `tparams' in type `tp'.
+ * A variance is taken from the four point lattice
+ *
+ * 0, Modifiers.COVARIANT, Modifiers.CONTRAVARIANT, Modifiers.VARIANCES.
+ */
+ private def variance(tparams: Array[Symbol], tp: Type): Array[int] = {
+ val vs: Array[int] = new Array[int](tparams.length);
+ for (val i <- Iterator.range(0, vs.length))
+ vs(i) = variance(tparams(i), tp);
+ vs
+ }
+
+ /** Compute variances of all type parameters `tparams' in types `tps'.
+ */
+ private def variance(tparams: Array[Symbol], tps: Array[Type]): Array[int] = {
+ val vs: Array[int] = new Array[int](tparams.length);
+ for (val i <- Iterator.range(0, vs.length))
+ vs(i) = variance(tparams(i), tps);
+ vs
+ }
+
+ /** Compute variance of type parameter `tparam' in types of all symbols `sym'.
+ */
+ private def variance(tparam: Symbol, syms: Array[Symbol]): int = {
+ var v: int = VARIANCES;
+ for (val i <- Iterator.range(0, syms.length)) {
+ v = v & variance(tparam, syms(i));
+ }
+ v
+ }
+
+ /** Compute variance of type parameter `tparam' in type of symbol `sym'.
+ */
+ private def variance(tparam: Symbol, sym: Symbol): int = sym.kind match {
+ case ERROR =>
+ VARIANCES
+ case VAL =>
+ variance(tparam, sym.info())
+ case TYPE =>
+ variance(tparam, sym.info()) & flip(variance(tparam, sym.loBound()))
+ case ALIAS =>
+ cut(variance(tparam, sym.info()))
+ case _ =>
+ 0
+ }
+
+ /** Compute variance of type parameter `tparam' in all types `tps'.
+ */
+ private def variance(tparam: Symbol, tps: Array[Type]): int = {
+ var v: int = VARIANCES;
+ for (val i <- Iterator.range(0, tps.length)) {
+ v = v & variance(tparam, tps(i));
+ }
+ v
+ }
+
+ /** Compute variance of type parameter `tparam' in all type arguments
+ * `tps' which correspond to formal type parameters `tparams'.
+ */
+ private def varianceInArgs(tvar: Symbol, tps: Array[Type], tparams: Array[Symbol]): int = {
+ var v: int = VARIANCES;
+ for (val i <- Iterator.range(0, tps.length)) {
+ if ((tparams(i).flags & COVARIANT) != 0) {
+ v = v & variance(tvar, tps(i));
+ } else if ((tparams(i).flags & CONTRAVARIANT) != 0) {
+ v = v & flip(variance(tvar, tps(i)));
+ } else {
+ v = v & cut(variance(tvar, tps(i)));
+ }
+ }
+ v
+ }
+
+ /** Does given `tparam' occur with variance `v' in type?
+ */
+ private def variance(tparam: Symbol, tp: Type): int = tp match {
+ case Type.ErrorType | Type.AnyType | Type.NoType |
+ Type$ThisType(_) | Type$ConstantType(_, _) =>
+ VARIANCES
+ case Type$TypeRef(pre, sym, args) =>
+ if (sym == tparam) COVARIANT
+ else variance(tparam, pre) & varianceInArgs(tparam, args, sym.typeParams())
+ case Type$SingleType(pre, sym) =>
+ cut(variance(tparam, pre))
+ case Type$CompoundType(parts, members) =>
+ variance(tparam, parts) & variance(tparam, members.elements())
+ case Type$MethodType(params, restype) =>
+ flip(variance(tparam, params)) & variance(tparam, restype)
+ case Type$PolyType(tparams, restype) =>
+ flip(variance(tparam, tparams)) & variance(tparam, restype)
+ }
+
+// Type parameter inference -----------------------------------------------------
+
+ private class NoInstance(msg: String) extends RuntimeException(msg);
+
+ /** map every TypeVar to its constraint.inst field.
+ * throw a NoInstance exception if a NoType or AnyType is encountered.
+ */
+ private val instantiateMap: Type$Map = new Type$Map() {
+ def apply(t: Type): Type = instantiate(t)
+ }
+
+ private def instantiate(tp: Type): Type = tp match {
+ case Type.AnyType | Type.NoType =>
+ throw new NoInstance("undetermined type");
+ case Type$TypeVar(origin, constr) =>
+ if (constr.inst != Type.NoType) instantiate(constr.inst)
+ else throw new NoInstance("no unique instantiation of type variable " +
+ origin + " could be found");
+ case _ =>
+ instantiateMap.map(tp)
+ }
+
+ /** Map type variable to its instance, or, if `covariant' is true,
+ * to its upper bound;
+ */
+ private def instantiateToBound(tp: Type, variance: int): Type = tp match {
+ case Type$TypeVar(origin, constr) =>
+ try {
+ if (constr.inst != Type.NoType) {
+ instantiate(constr.inst)
+ } else if ((variance & COVARIANT) != 0 && constr.hibounds != Type$List.EMPTY) {
+ maximizeVar(tp);
+ instantiate(constr.inst)
+ } else if ((variance & CONTRAVARIANT) != 0 && constr.lobounds != Type$List.EMPTY) {
+ minimizeVar(tp);
+ instantiate(constr.inst)
+ } else {
+ Type.AnyType;
+ }
+ } catch {
+ case ex: NoInstance => Type.AnyType
+ }
+ }
+
+ /** The formal parameter types corresponding to `params'.
+ * If `params' is a repeated parameter, a list of `length' copies
+ * of its type is returned.
+ */
+ def formalTypes(params: Array[Symbol], length: int): Array[Type] = {
+ if (params.length == 1 && (params(0).flags & REPEATED) != 0) {
+ val formals: Array[Type] = new Array[Type](length);
+ val args: Array[Type] = params(0).getType().typeArgs();
+ if (args.length == 1) {
+ val ft: Type = args(0);
+ // params(0) has type Seq[T], we need T here
+ for (val i <- Iterator.range(0, length))
+ formals(i) = ft;
+ return formals;
+ }
+ }
+ Symbol.getType(params);
+ }
+
+ /** Is type fully defined, i.e. no embedded anytypes or typevars in it?
+ */
+ def isFullyDefined(tp: Type): boolean = {
+ try {
+ instantiate(tp);
+ true
+ } catch {
+ case ex: NoInstance => false
+ }
+ }
+
+ /** Do type arguments `targs' conform to formal parameters `tparams'?
+ */
+ private def isWithinBounds(tparams: Array[Symbol], targs: Array[Type]): boolean = {
+ var i = 0;
+ while (i < targs.length) {
+ val hibound: Type = tparams(i).info().subst(tparams, targs);
+ if (!targs(i).isSubType(hibound)) {
+ var j = 0;
+ while (j < tparams.length) {
+ if (hibound.symbol() == tparams(j))
+ return isWithinBounds(
+ tparams,
+ Type.subst(
+ targs,
+ NewArray.Symbol(tparams(j)),
+ NewArray.Type(targs(i))));
+ j = j + 1
+ }
+ return false;
+ }
+ val lobound: Type = tparams(i).loBound().subst(tparams, targs);
+ if (!lobound.isSubType(targs(i))) {
+ var j = 0;
+ while (j < tparams.length) {
+ if (lobound.symbol() == tparams(j))
+ return isWithinBounds(
+ tparams,
+ Type.subst(
+ targs,
+ NewArray.Symbol(tparams(j)),
+ NewArray.Type(targs(i))));
+ j = j + 1
+ }
+ return false
+ }
+ i = i + 1
+ }
+ true
+ }
+
+ /** throw a type error if arguments not within bounds.
+ */
+ def checkBounds(tparams: Array[Symbol], targs: Array[Type], prefix: String): unit =
+ if (!isWithinBounds(tparams, targs))
+ throw new Type$Error(
+ prefix + "type arguments " +
+ ArrayApply.toString(targs.asInstanceOf[Array[Object]], "[", ",", "]") +
+ " do not conform to " +
+ tparams(0).owner() + "'s type parameter bounds " +
+ ArrayApply.toString(Symbol.defString(tparams).asInstanceOf[Array[Object]], "[", ",", "]"));
+
+ /** Instantiate variable to glb of its high bounds.
+ */
+ private def maximizeVar(tp: Type): unit = tp match {
+ case Type$TypeVar(origin, constr) =>
+ if (constr.inst == Type.NoType)
+ constr.inst = Type.glb(constr.hibounds.toArray());
+ }
+
+ /** Instantiate variable to lub of its low bounds.
+ */
+ private def minimizeVar(tp: Type): unit = tp match {
+ case Type$TypeVar(origin, constr) =>
+ if (constr.inst == Type.NoType)
+ constr.inst = Type.lub(constr.lobounds.toArray());
+ }
+
+ /** Solve constraint collected in types `tvars', instantiating `tvars(i)'
+ * in the process.
+ * @param tparams The type parameters corresponding to `tvars'
+ * @param upper When `true' search for max solution else min.
+ * @param variances The variances of type parameters; need to reverse
+ * solution direction for all contravariant variables.
+ * @param tvars All type variables to be instantiated.
+ * @param i The index of the type variable to be instantiated.
+ */
+ private def solve(tparams: Array[Symbol], upper: boolean, variances: Array[int], tvars: Array[Type], i: int): unit = {
+ if (tvars(i) != Type.NoType) {
+ tvars(i) match {
+ case Type$TypeVar(origin, constr) =>
+ if (constr.inst != Type.NoType) {
+ constr.inst = instantiate(constr.inst);
+ tvars(i) = constr.inst;
+ } else {
+ val tvar: Type = tvars(i);
+ val up: boolean = if (variances(i) != CONTRAVARIANT) upper
+ else !upper;
+ tvars(i) = Type.NoType;
+ val bound: Type = if (up) tparams(i).info() else tparams(i).loBound();
+ var cyclic: boolean = false;
+ for (val j <- Iterator.range(0, tvars.length)) {
+ if (bound.contains(tparams(j)) ||
+ up && tparams(j).loBound().isSameAs(tparams(i).getType()) ||
+ !up && tparams(j).info().isSameAs(tparams(i).getType())) {
+ cyclic = cyclic | tvars(j) == Type.NoType;
+ solve(tparams, upper, variances, tvars, j);
+ }
+ }
+ if (!cyclic) {
+ if (up) {
+ if (bound.symbol() != Global.instance.definitions.ANY_CLASS)
+ constr.hibounds = new Type$List(
+ bound.subst(tparams, tvars), constr.hibounds);
+ for (val j <- Iterator.range(0, tvars.length)) {
+ if (tparams(j).loBound().isSameAs(
+ tparams(i).getType())) {
+ constr.hibounds = new Type$List(
+ tparams(j).getType().subst(tparams, tvars),
+ constr.hibounds);
+ }
+ }
+ } else {
+ if (bound.symbol() != Global.instance.definitions.ALL_CLASS)
+ constr.lobounds = new Type$List(
+ bound.subst(tparams, tvars), constr.lobounds);
+ for (val j <- Iterator.range(0, tvars.length)) {
+ if (tparams(j).info().isSameAs(
+ tparams(i).getType())) {
+ constr.lobounds = new Type$List(
+ tparams(j).getType().subst(tparams, tvars),
+ constr.lobounds);
+ }
+ }
+ }
+ }
+ if (up) maximizeVar(tvar);
+ else minimizeVar(tvar);
+ tvars(i) = tvar.asInstanceOf[Type$TypeVar].constr.inst;
+ }
+ case _ =>
+ }
+ }
+ }
+
+ /** Generate an array of fresh type variables corresponding to parameters
+ * `tparams'
+ */
+ private def freshVars(tparams: Array[Symbol]): Array[Type] = {
+ val tvars: Array[Type] = new Array[Type](tparams.length);
+ for (val i <- Iterator.range(0, tvars.length)) {
+ tvars(i) = new Type$TypeVar(tparams(i).getType(), new Type$Constraint());
+ }
+ tvars
+ }
+
+ private val freshInstanceMap: Type$Map = new Type$Map() {
+ def apply(t: Type): Type = t match {
+ case Type$PolyType(tparams, restp) =>
+ val restp1: Type = apply(restp);
+ var tparams1: Array[Symbol] = Symbol.EMPTY_ARRAY;
+ var newparams1: Array[Symbol] = Symbol.EMPTY_ARRAY;
+ restp1 match {
+ case Type$PolyType(_, _) =>
+ // If there is a nested polytype, we need to
+ // substitute also its new type parameters for its old ones
+ // here. Reason: The outer polytype may refer to type
+ // variables of the inner one.
+ tparams1 = restp.typeParams();
+ newparams1 = restp1.typeParams();
+ case _ =>
+ }
+ val newparams: Array[Symbol] = new Array[Symbol](tparams.length);
+ for (val i <- Iterator.range(0, tparams.length))
+ newparams(i) = tparams(i).cloneSymbol();
+ for (val i <- Iterator.range(0, tparams.length)) {
+ newparams(i).setInfo(
+ newparams(i).info()
+ .subst(tparams, newparams)
+ .subst(tparams1, newparams1));
+ newparams(i).setLoBound(
+ newparams(i).loBound()
+ .subst(tparams, newparams)
+ .subst(tparams1, newparams1));
+ }
+ new Type$PolyType(newparams, restp1.subst(tparams, newparams))
+
+ case Type$OverloadedType(_, _) =>
+ map(t)
+
+ case _ =>
+ t
+ }
+ }
+
+ def freshInstance(tp: Type): Type = freshInstanceMap.apply(tp);
+
+ /** Automatically perform the following conversions on expression types:
+ * A method type becomes the corresponding function type.
+ * A nullary method type becomes its result type.
+ */
+ private def normalize(tp: Type): Type = tp match {
+ case Type$MethodType(params, restype) =>
+ global.definitions.FUNCTION_TYPE(
+ Symbol.getType(params), normalize(restype));
+ case Type$PolyType(tparams, restype) if (tparams.length == 0) =>
+ normalize(restype);
+ case _ =>
+ tp
+ }
+
+ /** Is normalized type `tp' a subtype of prototype `pt'?
+ */
+ def isCompatible(tp: Type, pt: Type): boolean =
+ normalize(tp).isSubType(pt);
+
+ /** Type arguments mapped to `scala.All' are taken to be uninstantiated.
+ * Map all those type arguments to their corresponding type parameters
+ * and return all these type parameters as result.
+ */
+ private def normalizeArgs(targs: Array[Type], tparams: Array[Symbol]): Array[Symbol] = {
+ var uninstantiated: Type$List = Type$List.EMPTY;
+ for (val i <- Iterator.range(0, targs.length)) {
+ if (targs(i).symbol() == Global.instance.definitions.ALL_CLASS) {
+ targs(i) = tparams(i).getType();
+ uninstantiated = Type$List.append(uninstantiated, targs(i));
+ }
+ }
+ Type.symbol(uninstantiated.toArray());
+ }
+
+ /** Return inferred type arguments of polymorphic expression, given
+ * its type parameters and result type and a prototype `pt'.
+ * If no minimal type variables exist that make the
+ * instantiated type a subtype of `pt', return `null'.
+ */
+ private def exprTypeArgs(tparams: Array[Symbol], restype: Type, pt: Type): Array[Type] = {
+ val tvars: Array[Type] = freshVars(tparams);
+ val insttype: Type = restype.subst(tparams, tvars);
+ if (isCompatible(insttype, pt)) {
+ try {
+ val restype1 = normalize(restype);
+ for (val i <- Iterator.range(0, tvars.length)) {
+ solve(tparams, false, variance(tparams, restype1), tvars, i);
+ }
+ tvars
+ } catch {
+ case ex: NoInstance => null
+ }
+ } else {
+ null
+ }
+ }
+
+ /** Return inferred proto-type arguments of function, given
+ * its type and value parameters and result type, and a
+ * prototype `pt' for the function result.
+ * Type arguments need to be either determined precisely by
+ * the prototype, or they are maximized, if they occur only covariantly
+ * in the value parameter list.
+ * If instantiation of a type parameter fails,
+ * take Type.AnyType for the proto-type argument.
+ */
+ def protoTypeArgs(tparams: Array[Symbol], restype: Type, pt: Type, params: Array[Symbol]): Array[Type] = {
+ val tvars: Array[Type] = freshVars(tparams);
+ val insttype: Type = restype.subst(tparams, tvars);
+ val targs: Array[Type] = new Array[Type](tvars.length);
+ for (val i <- Iterator.range(0, tvars.length))
+ targs(i) = Type.AnyType;
+ if (isCompatible(insttype, pt)) {
+ try {
+ for (val i <- Iterator.range(0, tvars.length)) {
+ targs(i) = instantiateToBound(
+ tvars(i), variance(tparams(i), params));
+ }
+ } catch {
+ case ex: NoInstance =>
+ for (val i <- Iterator.range(0, tvars.length))
+ targs(i) = Type.AnyType;
+ }
+ }
+ targs
+ }
+
+ /** Return inferred type arguments, given type parameters, formal parameters,
+ * argument types, result type and expected result type.
+ * If this is not possible, throw a `NoInstance' exception, or, if
+ * `needToSucceed' is false alternatively return `null'.
+ * Undetermined type arguments are represented by `definitions.ALL_TYPE'.
+ * No check that inferred parameters conform to their bounds is made here.
+ */
+ private def methTypeArgs(tparams: Array[Symbol], params: Array[Symbol], argtypes: Array[Type], restp: Type, pt: Type, needToSucceed: boolean): Array[Type] = {
+ //System.out.println("methTypeArgs, tparams = " + ArrayApply.toString(tparams) + ", params = " + ArrayApply.toString(params) + ", type(params) = " + ArrayApply.toString(Symbol.type(params)) + ", argtypes = " + ArrayApply.toString(argtypes));//DEBUG
+
+ val tvars: Array[Type] = freshVars(tparams);
+ val formals: Array[Type] = formalTypes(params, argtypes.length);
+ if (formals.length != argtypes.length) {
+ if (needToSucceed)
+ throw new NoInstance("parameter lists differ in length");
+ return null;
+ }
+
+ // check first whether type variables can be fully defined from
+ // expected result type.
+ if (!isCompatible(restp.subst(tparams, tvars), pt)) {
+ if (needToSucceed)
+ throw new NoInstance("result type " + restp +
+ " is incompatible with expected type " + pt);
+ return null;
+ }
+ for (val i <- Iterator.range(0, tvars.length)) {
+ val tvar: Type$TypeVar = tvars(i).asInstanceOf[Type$TypeVar];
+ if (!isFullyDefined(tvar)) tvar.constr.inst = Type.NoType;
+ }
+
+ // Then define remaining type variables from argument types.
+ var i = 0;
+ while (i < argtypes.length) {
+ if (!isCompatible(argtypes(i).widen().subst(tparams, tvars), formals(i).subst(tparams, tvars))) {
+ if (needToSucceed) {
+ if (global.explaintypes) {
+ Type.explainSwitch = true;
+ argtypes(i).widen().subst(tparams, tvars).isSubType(
+ formals(i).subst(tparams, tvars));
+ Type.explainSwitch = false;
+ }
+ throw new NoInstance(
+ typeErrorMsg(
+ "argument expression's type is not compatible with formal parameter type",
+ argtypes(i).widen().subst(tparams, tvars),
+ formals(i).subst(tparams, tvars)));
+ }
+ return null;
+ }
+ i = i + 1;
+ }
+ for (val i <- Iterator.range(0, tvars.length)) {
+ solve(tparams, false, variance(tparams, formals), tvars, i);
+ }
+ //System.out.println(" = " + ArrayApply.toString(tvars));//DEBUG
+ tvars
+ }
+
+ /** Create and attribute type application node. Pass arguments for that
+ * `tparams' prefix which is owned by the tree's symbol. If there are remaining
+ * type parameters, substitute corresponding type arguments for them in the
+ * tree. Such remaining type parameters always come from an inferred PolyType.
+ */
+ def mkTypeApply(tree: Tree, tparams: Array[Symbol], restype: Type, targs: Array[Type]): Tree = {
+ var tree1: Tree = tree;
+ val sym: Symbol = tree.symbol();
+ var i: int = 0;
+ while (i < tparams.length && tparams(i).owner() == sym)
+ i = i + 1;
+ if (i < tparams.length) {
+ //System.out.println("tpar " + tparams(i) + " of " + tparams(i).owner() + " <> " + sym);//DEBUG
+ //new Printer().print(tree1);//DEBUG
+ //System.out.println(ArrayApply.toString(targs) + "/" + i + "/" + ArrayApply.toString(tparams));//DEBUG
+ val tparams1: Array[Symbol] = new Array[Symbol](tparams.length - i);
+ System.arraycopy(tparams.asInstanceOf[Array[Object]], i,
+ tparams1.asInstanceOf[Array[Object]], 0, tparams1.length);
+ val targs1: Array[Type] = new Array[Type](tparams.length - i);
+ System.arraycopy(targs.asInstanceOf[Array[Object]], i,
+ targs1.asInstanceOf[Array[Object]], 0, targs1.length);
+ tree1 = substituter.apply(tree1, tparams1, targs1);
+ }
+ if (0 < i) {
+ val argtrees: Array[Tree] = new Array[Tree](i);
+ for (val j <- Iterator.range(0, i))
+ argtrees(j) = gen.mkType(tree.pos, targs(j));
+ tree1 = make.TypeApply(tree.pos, tree1, argtrees);
+ }
+ //System.out.println(Sourcefile.files[Position.file(tree1.pos)] + ": ");
+ tree1.setType(restype.subst(tparams, targs))
+ }
+
+ /** Return the instantiated and normalized type of polymorphic expression
+ * with type `[tparams]restype', given two prototypes `pt1', and `pt2'.
+ * `pt1' is the strict first attempt prototype where type parameters
+ * are left unchanged. `pt2' is the fall-back prototype where type parameters
+ * are replaced by `AnyType's. We try to instantiate first to `pt1' and then,
+ * if this fails, to `pt2'. If both attempts fail, a Type.Error is thrown.
+ */
+ def argumentTypeInstance(tparams: Array[Symbol], restype: Type, pt1: Type, pt2: Type): Type = {
+ restype match {
+ case Type$PolyType(tparams1, restype1) =>
+ val tparams2: Array[Symbol] = new Array[Symbol](tparams.length + tparams1.length);
+ System.arraycopy(tparams.asInstanceOf[Array[Object]], 0,
+ tparams2.asInstanceOf[Array[Object]], 0, tparams.length);
+ System.arraycopy(tparams1.asInstanceOf[Array[Object]], 0,
+ tparams2.asInstanceOf[Array[Object]], tparams.length, tparams1.length);
+ argumentTypeInstance(tparams2, restype1, pt1, pt2);
+
+ case _ =>
+ if (tparams.length != 0) {
+ var targs: Array[Type] = exprTypeArgs(tparams, restype, pt1);
+ if (targs == null)
+ targs = exprTypeArgs(tparams, restype, pt2);
+ if (targs == null)
+ throw new Type$Error(
+ typeErrorMsg(
+ "polymorphic argument cannot be instantiated to formal parameter type",
+ new Type$PolyType(tparams, restype), pt2));
+ checkBounds(tparams, targs, "inferred ");
+ restype.subst(tparams, targs);
+ } else {
+ normalize(restype);
+ }
+ }
+ }
+
+ /** Instantiate expression `tree' of polymorphic type [tparams]restype,
+ * using prototype `pt'.
+ */
+ def exprInstance(tree: Tree, tparams: Array[Symbol], restype: Type, pt: Type): Tree = {
+ restype match {
+ case Type$PolyType(tparams1, restype1) =>
+ val tparams2: Array[Symbol] = new Array[Symbol](tparams.length + tparams1.length);
+ System.arraycopy(tparams.asInstanceOf[Array[Object]], 0,
+ tparams2.asInstanceOf[Array[Object]], 0, tparams.length);
+ System.arraycopy(tparams1.asInstanceOf[Array[Object]], 0,
+ tparams2.asInstanceOf[Array[Object]], tparams.length, tparams1.length);
+ exprInstance(tree, tparams2, restype1, pt)
+ case _ =>
+ val targs: Array[Type] = exprTypeArgs(tparams, restype, pt);
+ if (targs == null)
+ throw new Type$Error(
+ "polymorphic expression of type " + tree.getType() +
+ " cannot be instantiated from expected type " + pt);
+ checkBounds(tparams, targs, "inferred ");
+ mkTypeApply(tree, tparams, restype, targs)
+ }
+ }
+
+ /** Instantiate method `tree' of polymorphic type [tparams]restype,
+ * so that resulting method type can be applied to arguments with
+ * types `argtypes' and its result type is compatible with `pt'.
+ */
+ def methodInstance(tree: Tree, tparams: Array[Symbol], restype: Type, argtypes: Array[Type], pt: Type): Tree = {
+ restype match {
+ case Type$PolyType(tparams1, restype1) =>
+ val tparams2: Array[Symbol] = new Array[Symbol](tparams.length + tparams1.length);
+ System.arraycopy(tparams.asInstanceOf[Array[Object]], 0,
+ tparams2.asInstanceOf[Array[Object]], 0, tparams.length);
+ System.arraycopy(tparams1.asInstanceOf[Array[Object]], 0,
+ tparams2.asInstanceOf[Array[Object]], tparams.length, tparams1.length);
+ methodInstance(tree, tparams2, restype1, argtypes, pt)
+
+ case Type$MethodType(params, restpe) =>
+ var targs: Array[Type] = _;
+ try {
+ targs = methTypeArgs(tparams, params, argtypes, restpe, pt, true);
+ } catch {
+ case ex: NoInstance =>
+ throw new Type$Error(
+ applyErrorMsg(
+ "no type parameters for ", tree,
+ " exist so that it can be applied to arguments ",
+ Type.widen(argtypes), Type.AnyType) +
+ "\n --- because ---\n" + ex.getMessage());
+ }
+ val uninstantiated: Array[Symbol] = normalizeArgs(targs, tparams);
+ checkBounds(tparams, targs, "inferred ");
+ val restype1: Type =
+ if (uninstantiated.length == 0) restype
+ else new Type$MethodType(
+ params, new Type$PolyType(uninstantiated, restpe));
+ mkTypeApply(tree, tparams, restype1, targs);
+ case _ =>
+ tree
+ }
+ }
+
+ /** Instantiate constructor `tree' of polymorphic type [tparams]restype',
+ * so that its the result type of `restype' matches prototype `pt'.
+ * If constructor is polymorphic, maximize all type variables under this
+ * condition.
+ */
+ def constructorInstance(tree: Tree, tparams: Array[Symbol], restype: Type, pt: Type): unit = {
+ restype match {
+ case Type$PolyType(tparams1, restype1) =>
+ val tparams2: Array[Symbol] = new Array[Symbol](tparams.length + tparams1.length);
+ System.arraycopy(tparams.asInstanceOf[Array[Object]], 0,
+ tparams2.asInstanceOf[Array[Object]], 0, tparams.length);
+ System.arraycopy(tparams1.asInstanceOf[Array[Object]], 0,
+ tparams2.asInstanceOf[Array[Object]], tparams.length, tparams1.length);
+ constructorInstance(tree, tparams2, restype1, pt)
+
+ case _ =>
+ val tvars: Array[Type] = freshVars(tparams);
+ val restype1: Type = restype.subst(tparams, tvars);
+ val ctpe1: Type = restype1.resultType();
+ if (ctpe1.isSubType(pt)) {
+ try {
+ for (val i <- Iterator.range(0, tvars.length)) {
+ solve(tparams, true, variance(tparams, restype.resultType()),
+ tvars, i);
+ }
+ checkBounds(tparams, tvars, "inferred ");
+ tree.setType(restype.subst(tparams, tvars));
+ //System.out.println("inferred constructor type: " + tree.getType());//DEBUG
+ } catch {
+ case ex: NoInstance =>
+ throw new Type$Error(
+ "constructor of type " + ctpe1 +
+ " can be instantiated in more than one way to expected type " +
+ pt +
+ "\n --- because ---\n" + ex.getMessage());
+ }
+ } else {
+ throw new Type$Error(
+ typeErrorMsg(
+ "constructor cannot be instantiated to expected type",
+ ctpe1, pt));
+ }
+ }
+ }
+
+// Overload Resolution -------------------------------------------------------------
+
+ /** Is function type `ftpe' applicable to `argtypes' and
+ * does its result conform to `pt'?
+ */
+ def isApplicable(ftpe: Type, argtypes: Array[Type], pt: Type): boolean = ftpe match {
+ case Type$MethodType(params, restpe) =>
+ // sequences ? List( a* )
+ val formals: Array[Type] = formalTypes(params, argtypes.length);
+ isCompatible(restpe, pt) &&
+ formals.length == argtypes.length &&
+ Type.isSubType(argtypes, formals);
+ case Type$PolyType(tparams, Type$MethodType(params, restpe)) =>
+ try {
+ val targs: Array[Type] = methTypeArgs(
+ tparams, params, argtypes, restpe, pt, false);
+ if (targs != null) {
+ val uninstantiated: Array[Symbol] = normalizeArgs(targs, tparams);
+ isWithinBounds(tparams, targs) &&
+ exprTypeArgs(uninstantiated, restpe.subst(tparams, targs), pt) != null;
+ } else {
+ false
+ }
+ } catch {
+ case ex: NoInstance => false
+ }
+ case _ =>
+ false
+ }
+
+ /** Does function type `ftpe1' specialize function type `ftpe2'
+ * when both are alternatives in an overloaded function?
+ */
+ def specializes(ftpe1: Type, ftpe2: Type): boolean = ftpe1 match {
+ case Type$MethodType(params, _) =>
+ isApplicable(ftpe2, Symbol.getType(params), Type.AnyType)
+ case Type$PolyType(_, Type$MethodType(params, _)) =>
+ isApplicable(ftpe2, Symbol.getType(params), Type.AnyType);
+ case _ =>
+ false
+ }
+
+ /** Assign `tree' the type of the alternative which matches
+ * prototype `pt', if it exists.
+ * If several alternatives match `pt', take unique parameterless one.
+ * Throw a Type.Error if several such alternatives exist.
+ * If no alternative matches, leave `tree' unchanged.
+ */
+ def exprAlternative(tree: Tree, alts: Array[Symbol], alttypes: Array[Type], pt: Type): unit = {
+
+ def improves(tp1: Type, tp2: Type): boolean =
+ tp2.isParameterized() &&
+ (!tp1.isParameterized() || specializes(tp1, tp2));
+
+ // first, catch the case of a missing parameter
+ // list for an overloaded constructor.
+ if (alts.length > 0) {
+ var i: int = 0;
+ while (i < alts.length && alts(i).isConstructor() && alttypes(i).isInstanceOf[Type$MethodType])
+ i = i + 1;
+ if (i == alts.length)
+ throw new Type$Error("missing arguments for " + alts(0));
+ }
+ // second, do the normal case.
+ var best: int = -1;
+ for (val i <- Iterator.range(0, alttypes.length)) {
+ if (isCompatible(alttypes(i), pt) && (best < 0 || improves(alttypes(i), alttypes(best)))) {
+ best = i;
+ }
+ }
+ if (best >= 0) {
+ for (val i <- Iterator.range(0, alttypes.length)) {
+ if (isCompatible(alttypes(i), pt) && best != i && !improves(alttypes(best), alttypes(i))) {
+ throw new Type$Error(
+ overloadResolveErrorMsg(
+ alts(best), alttypes(best), alts(i), alttypes(i)) +
+ " expected type " + pt);
+ }
+ }
+ tree.setSymbol(alts(best)).setType(alttypes(best));
+ }
+ }
+
+ /** Assign `tree' the type of an alternative
+ * which is applicable to `argtypes', and whose result type is
+ * a subtype of `pt' if it exists.
+ * If several applicable alternatives exist, take the
+ * most specialized one, or throw an error if no
+ * most specialized applicable alternative exists.
+ * If no alternative matches, leave `tree' unchanged,
+ * try to select method with pt = AnyType.
+ * If pt is AnyType, leave tree unchanged.
+ */
+ def methodAlternative(tree: Tree, alts: Array[Symbol], alttypes: Array[Type], argtypes: Array[Type], pt: Type): unit = {
+ if (alts.length == 1) {
+ tree.setSymbol(alts(0)).setType(alttypes(0));
+ return;
+ }
+ var best: int = -1;
+ for (val i <- Iterator.range(0, alttypes.length)) {
+ if (isApplicable(alttypes(i), argtypes, pt) &&
+ (best < 0 || specializes(alttypes(i), alttypes(best))))
+ best = i;
+ }
+ if (best >= 0) {
+ for (val i <- Iterator.range(0, alttypes.length)) {
+ if (i != best &&
+ isApplicable(alttypes(i), argtypes, pt) &&
+ !(specializes(alttypes(best), alttypes(i)) &&
+ !specializes(alttypes(i), alttypes(best))))
+ throw new Type$Error(
+ overloadResolveErrorMsg(
+ alts(best), alttypes(best), alts(i), alttypes(i)) +
+ " argument types " +
+ ArrayApply.toString(argtypes.asInstanceOf[Array[Object]], "(", ",", ")") +
+ (if (pt == Type.AnyType) "" else " and expected result type " + pt));
+ }
+ tree.setSymbol(alts(best)).setType(alttypes(best));
+ } else if (pt != Type.AnyType) {
+ methodAlternative(tree, alts, alttypes, argtypes, Type.AnyType);
+ }
+ }
+
+ /** Assign `tree' the type of unique polymorphic alternative with `nparams'
+ * as the number of type parameters, if it exists.
+ * Throw error if several such polymorphic alternatives exist.
+ * If no alternative matches, leave `tree' unchanged.
+ */
+ def polyAlternative(tree: Tree, alts: Array[Symbol], alttypes: Array[Type], nparams: int): unit = {
+ if (alts.length == 1) {
+ tree.setSymbol(alts(0)).setType(alttypes(0));
+ return;
+ }
+ var i: int = 0;
+ while (i < alttypes.length &&
+ !(alts(i).isValue() &&
+ alttypes(i).typeParams().length == nparams))
+ i = i + 1;
+
+ if (i < alttypes.length) {
+ for (val j <- Iterator.range(i + 1, alttypes.length)) {
+ if (alts(j).isValue() &&
+ alttypes(j).typeParams().length == nparams)
+ throw new Type$Error(
+ overloadResolveErrorMsg(alts(i), alttypes(i), alts(j), alttypes(j)) +
+ " polymorphic function with " + nparams + " parameters");
+ }
+ tree.setSymbol(alts(i)).setType(alttypes(i));
+ }
+ }
+}
+