summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/Infer.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Infer.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala428
1 files changed, 258 insertions, 170 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 6c27bcace6..3e13f6ddb1 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -9,7 +9,9 @@ package typechecker
import scala.collection.{ mutable, immutable }
import scala.collection.mutable.ListBuffer
import scala.util.control.ControlThrowable
+import scala.tools.util.StringOps.{ countAsString, countElementsAsString }
import symtab.Flags._
+import scala.annotation.tailrec
/** This trait ...
*
@@ -18,10 +20,11 @@ import symtab.Flags._
*/
trait Infer {
self: Analyzer =>
+
import global._
import definitions._
-
- private final val inferInfo = false //@MDEBUG
+ import typer.printInference
+ import typeDebug.ptBlock
/* -- Type parameter inference utility functions --------------------------- */
@@ -69,13 +72,12 @@ trait Infer {
private class DeferredNoInstance(getmsg: () => String) extends NoInstance("") {
override def getMessage(): String = getmsg()
}
+ private def ifNoInstance[T](f: String => T): PartialFunction[Throwable, T] = {
+ case x: NoInstance => f(x.getMessage)
+ }
- /** map every TypeVar to its constraint.inst field.
+ /** Map every TypeVar to its constraint.inst field.
* throw a NoInstance exception if a NoType or WildcardType is encountered.
- *
- * @param tp ...
- * @return ...
- * @throws NoInstance
*/
object instantiate extends TypeMap {
private var excludedVars = immutable.Set[TypeVar]()
@@ -86,7 +88,7 @@ trait Infer {
if (constr.inst == NoType) {
throw new DeferredNoInstance(() =>
"no unique instantiation of type variable " + origin + " could be found")
- } else if (excludedVars contains tv) {
+ } else if (excludedVars(tv)) {
throw new NoInstance("cyclic instantiation")
} else {
excludedVars += tv
@@ -125,37 +127,37 @@ trait Infer {
}
}
- /** Solve constraint collected in types <code>tvars</code>.
+ /** Solve constraint collected in types `tvars`.
*
* @param tvars All type variables to be instantiated.
- * @param tparams The type parameters corresponding to <code>tvars</code>
+ * @param tparams The type parameters corresponding to `tvars`
* @param variances The variances of type parameters; need to reverse
* solution direction for all contravariant variables.
- * @param upper When <code>true</code> search for max solution else min.
+ * @param upper When `true` search for max solution else min.
* @throws NoInstance
*/
def solvedTypes(tvars: List[TypeVar], tparams: List[Symbol],
variances: List[Int], upper: Boolean, depth: Int): List[Type] = {
-// def boundsString(tvar: TypeVar) =
-// "\n "+
-// ((tvar.constr.loBounds map (_ + " <: " + tvar.origin.typeSymbol.name)) :::
-// (tvar.constr.hiBounds map (tvar.origin.typeSymbol.name + " <: " + _)) mkString ", ")
+
if (!solve(tvars, tparams, variances, upper, depth)) {
-// no panic, it's good enough to just guess a solution, we'll find out
-// later whether it works.
-// @M danger, Will Robinson! this means that you should never trust inferred type arguments!
-// need to call checkBounds on the args/typars or type1 on the tree for the expression that results from type inference
-// see e.g., #2421: implicit search had been ignoring this caveat
-// throw new DeferredNoInstance(() =>
-// "no solution exists for constraints"+(tvars map boundsString))
+ // no panic, it's good enough to just guess a solution, we'll find out
+ // later whether it works. *ZAP* @M danger, Will Robinson! this means
+ // that you should never trust inferred type arguments!
+ //
+ // Need to call checkBounds on the args/typars or type1 on the tree
+ // for the expression that results from type inference see e.g., #2421:
+ // implicit search had been ignoring this caveat
+ // throw new DeferredNoInstance(() =>
+ // "no solution exists for constraints"+(tvars map boundsString))
+ }
+ for (tvar <- tvars ; if tvar.constr.inst == tvar) {
+ if (tvar.origin.typeSymbol.info eq ErrorType)
+ // this can happen if during solving a cyclic type parameter
+ // such as T <: T gets completed. See #360
+ tvar.constr.inst = ErrorType
+ else
+ assert(false, tvar.origin+" at "+tvar.origin.typeSymbol.owner)
}
- for (tvar <- tvars)
- if (tvar.constr.inst == tvar)
- if (tvar.origin.typeSymbol.info eq ErrorType) {
- // this can happen if during solving a cyclic type parameter
- // such as T <: T gets completed. See #360
- tvar.constr.inst = ErrorType
- } else assert(false, tvar.origin+" at "+tvar.origin.typeSymbol.owner)
tvars map instantiate
}
@@ -263,23 +265,22 @@ trait Infer {
Console.println("" + pre + " " + sym.owner + " " + context.owner + " " + context.outer.enclClass.owner + " " + sym.owner.thisType + (pre =:= sym.owner.thisType))
}
new AccessError(tree, sym, pre,
- if (settings.check.isDefault) {
+ if (settings.check.isDefault)
analyzer.lastAccessCheckDetails
- } else {
- "\n because of an internal error (no accessible symbol):" +
- "\nsym = " + sym +
- "\nunderlying(sym) = " + underlying(sym) +
- "\npre = " + pre +
- "\nsite = " + site +
- "\ntree = " + tree +
- "\nsym.accessBoundary(sym.owner) = " + sym.accessBoundary(sym.owner) +
- "\nsym.ownerChain = " + sym.ownerChain +
- "\nsym.owner.thisType = " + sym.owner.thisType +
- "\ncontext.owner = " + context.owner +
- "\ncontext.outer.enclClass.owner = " + context.outer.enclClass.owner
- }
+ else
+ ptBlock("because of an internal error (no accessible symbol)",
+ "sym.ownerChain" -> sym.ownerChain,
+ "underlying(sym)" -> underlying(sym),
+ "pre" -> pre,
+ "site" -> site,
+ "tree" -> tree,
+ "sym.accessBoundary(sym.owner)" -> sym.accessBoundary(sym.owner),
+ "context.owner" -> context.owner,
+ "context.outer.enclClass.owner" -> context.outer.enclClass.owner
+ )
)
- } else {
+ }
+ else {
if(sym1.isTerm)
sym1.cookJavaRawInfo() // xform java rawtypes into existentials
@@ -308,6 +309,7 @@ trait Infer {
* they are: perhaps someone more familiar with the intentional distinctions
* can examine the now much smaller concrete implementations below.
*/
+/*
abstract class CompatibilityChecker {
def resultTypeCheck(restpe: Type, arg: Type): Boolean
def argumentCheck(arg: Type, param: Type): Boolean
@@ -317,7 +319,7 @@ trait Infer {
val MethodType(params, restpe) = tp
val TypeRef(pre, sym, args) = pt
- if (sym.isAliasType) apply(tp, pt.dealias)
+ if (sym.isAliasType) apply(tp, pt.normalize)
else if (sym.isAbstractType) apply(tp, pt.bounds.lo)
else {
val len = args.length - 1
@@ -353,7 +355,7 @@ trait Infer {
}
object isPlausiblyCompatible extends CompatibilityChecker {
- def resultTypeCheck(restpe: Type, arg: Type) = isPlausiblySubType(restpe, arg)
+ def resultTypeCheck(restpe: Type, arg: Type) = isPlausiblyCompatible(restpe, arg)
def argumentCheck(arg: Type, param: Type) = isPlausiblySubType(arg, param)
def lastChanceCheck(tp: Type, pt: Type) = false
}
@@ -367,21 +369,76 @@ trait Infer {
case _ => super.apply(tp, pt)
}
}
+*/
+ def isPlausiblyCompatible(tp: Type, pt: Type) = checkCompatibility(true, tp, pt)
+ def normSubType(tp: Type, pt: Type) = checkCompatibility(false, tp, pt)
+
+ @tailrec private def checkCompatibility(fast: Boolean, tp: Type, pt: Type): Boolean = tp match {
+ case mt @ MethodType(params, restpe) =>
+ if (mt.isImplicit)
+ checkCompatibility(fast, restpe, pt)
+ else pt match {
+ case tr @ TypeRef(pre, sym, args) =>
+
+ if (sym.isAliasType) checkCompatibility(fast, tp, pt.normalize)
+ else if (sym.isAbstractType) checkCompatibility(fast, tp, pt.bounds.lo)
+ else {
+ val len = args.length - 1
+ hasLength(params, len) &&
+ sym == FunctionClass(len) && {
+ var ps = params
+ var as = args
+ if (fast) {
+ while (ps.nonEmpty && as.nonEmpty) {
+ if (!isPlausiblySubType(as.head, ps.head.tpe))
+ return false
+ ps = ps.tail
+ as = as.tail
+ }
+ } else {
+ while (ps.nonEmpty && as.nonEmpty) {
+ if (!(as.head <:< ps.head.tpe))
+ return false
+ ps = ps.tail
+ as = as.tail
+ }
+ }
+ ps.isEmpty && as.nonEmpty && {
+ val lastArg = as.head
+ as.tail.isEmpty && checkCompatibility(fast, restpe, lastArg)
+ }
+ }
+ }
+
+ case _ => if (fast) false else tp <:< pt
+ }
+ case NullaryMethodType(restpe) => checkCompatibility(fast, restpe, pt)
+ case PolyType(_, restpe) => checkCompatibility(fast, restpe, pt)
+ case ExistentialType(_, qtpe) => if (fast) checkCompatibility(fast, qtpe, pt) else normalize(tp) <:< pt // is !fast case needed??
+ case _ => if (fast) isPlausiblySubType(tp, pt) else tp <:< pt
+ }
+
/** This expresses more cleanly in the negative: there's a linear path
* to a final true or false.
*/
private def isPlausiblySubType(tp1: Type, tp2: Type) = !isImpossibleSubType(tp1, tp2)
- private def isImpossibleSubType(tp1: Type, tp2: Type) = {
- (tp1.dealias, tp2.dealias) match {
- case (TypeRef(_, sym1, _), TypeRef(_, sym2, _)) =>
- sym1.isClass &&
- sym2.isClass &&
- !(sym1 isSubClass sym2) &&
- !(sym1 isNumericSubClass sym2)
- case _ =>
- false
- }
+ private def isImpossibleSubType(tp1: Type, tp2: Type) = tp1.normalize.widen match {
+ case tr1 @ TypeRef(_, sym1, _) =>
+ // If the lhs is an abstract type, we can't rule out a subtype
+ // relationship because we don't know what it is.
+ !sym1.isAbstractType && (tp2.normalize.widen match {
+ case TypeRef(_, sym2, _) =>
+ sym1.isClass &&
+ sym2.isClass &&
+ !(sym1 isSubClass sym2) &&
+ !(sym1 isNumericSubClass sym2)
+ case RefinedType(parents, decls) =>
+ decls.nonEmpty &&
+ tr1.member(decls.head.name) == NoSymbol
+ case _ => false
+ })
+ case _ => false
}
def isCompatible(tp: Type, pt: Type): Boolean = {
@@ -444,12 +501,13 @@ trait Infer {
* @param pt ...
* @return ...
*/
- private def exprTypeArgs(tparams: List[Symbol], restpe: Type, pt: Type, checkCompat: (Type, Type) => Boolean = isCompatible): List[Type] = {
+ private def exprTypeArgs(tparams: List[Symbol], restpe: Type, pt: Type, useWeaklyCompatible: Boolean = false): List[Type] = {
val tvars = tparams map freshVar
- if (checkCompat(restpe.instantiateTypeParams(tparams, tvars), pt)) {
+ val instResTp = restpe.instantiateTypeParams(tparams, tvars)
+ if ( if (useWeaklyCompatible) isWeaklyCompatible(instResTp, pt) else isCompatible(instResTp, pt) ) {
try {
// If the restpe is an implicit method, and the expected type is fully defined
- // optimze type variables wrt to the implicit formals only; ignore the result type.
+ // optimize type variables wrt to the implicit formals only; ignore the result type.
// See test pos/jesper.scala
val varianceType = restpe match {
case mt: MethodType if mt.isImplicit && isFullyDefined(pt) =>
@@ -571,7 +629,7 @@ trait Infer {
tparam -> Some(
if (targ.typeSymbol == RepeatedParamClass) targ.baseType(SeqClass)
else if (targ.typeSymbol == JavaRepeatedParamClass) targ.baseType(ArrayClass)
- else if (targ.typeSymbol.isModuleClass) targ // this infers Foo.type instead of "object Foo"
+ else if (targ.typeSymbol.isModuleClass) targ // this infers Foo.type instead of "object Foo" (see also widenIfNecessary)
else targ.widen
)
}
@@ -584,73 +642,82 @@ trait Infer {
* Undetermined type arguments are represented by `definitions.NothingClass.tpe'.
* No check that inferred parameters conform to their bounds is made here.
*
- * bq: was private, but need it for unapply checking
- *
* @param tparams the type parameters of the method
* @param formals the value parameter types of the method
* @param restp the result type of the method
* @param argtpes the argument types of the application
* @param pt the expected return type of the application
* @return @see adjustTypeArgs
-
+ *
* @throws NoInstance
*/
def methTypeArgs(tparams: List[Symbol], formals: List[Type], restpe: Type,
argtpes: List[Type], pt: Type): AdjustedTypeArgs.Result = {
val tvars = tparams map freshVar
- if (inferInfo)
- println("methTypeArgs tparams = "+tparams+
- ", formals = "+formals+
- ", restpe = "+restpe+
- ", argtpes = "+argtpes+
- ", pt = "+pt+
- ", tvars = "+tvars+" "+(tvars map (_.constr)))
- if (!sameLength(formals, argtpes)) {
+ if (!sameLength(formals, argtpes))
throw new NoInstance("parameter lists differ in length")
- }
- if (inferInfo) // @MDEBUG
- println("methTypeArgs "+
- " tparams = "+tparams+"\n"+
- " formals = "+formals+"\n"+
- " restpe = "+restpe+"\n"+
- " restpe_inst = "+restpe.instantiateTypeParams(tparams, tvars)+"\n"+
- " argtpes = "+argtpes+"\n"+
- " pt = "+pt)
-
- // check first whether type variables can be fully defined from
- // expected result type.
- if (!isConservativelyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt)) {
-// just wait and instantiate from the arguments.
-// that way, we can try to apply an implicit conversion afterwards.
-// This case could happen if restpe is not fully defined, so that
-// search for an implicit from it to pt fails because of an ambiguity.
-// See #0347. Therefore, the following two lines are commented out.
-// throw new DeferredNoInstance(() =>
-// "result type " + normalize(restpe) + " is incompatible with expected type " + pt)
- }
+ val restpeInst = restpe.instantiateTypeParams(tparams, tvars)
+ printInference(
+ ptBlock("methTypeArgs",
+ "tparams" -> tparams,
+ "formals" -> formals,
+ "restpe" -> restpe,
+ "restpeInst" -> restpeInst,
+ "argtpes" -> argtpes,
+ "pt" -> pt,
+ "tvars" -> tvars,
+ "constraints" -> tvars.map(_.constr)
+ )
+ )
+
+ // first check if typevars can be fully defined from the expected type.
+ // The return value isn't used so I'm making it obvious that this side
+ // effects, because a function called "isXXX" is not the most obvious
+ // side effecter.
+ isConservativelyCompatible(restpeInst, pt)
+
+ // Return value unused with the following explanation:
+ //
+ // Just wait and instantiate from the arguments. That way,
+ // we can try to apply an implicit conversion afterwards.
+ // This case could happen if restpe is not fully defined, so the
+ // search for an implicit from restpe => pt fails due to ambiguity.
+ // See #347. Therefore, the following two lines are commented out.
+ //
+ // throw new DeferredNoInstance(() =>
+ // "result type " + normalize(restpe) + " is incompatible with expected type " + pt)
+
for (tvar <- tvars)
if (!isFullyDefined(tvar)) tvar.constr.inst = NoType
// Then define remaining type variables from argument types.
(argtpes, formals).zipped map { (argtpe, formal) =>
- //@M isCompatible has side-effect: isSubtype0 will register subtype checks in the tvar's bounds
- if (!isCompatible(argtpe.deconst.instantiateTypeParams(tparams, tvars),
- formal.instantiateTypeParams(tparams, tvars))) {
+ val tp1 = argtpe.deconst.instantiateTypeParams(tparams, tvars)
+ val pt1 = formal.instantiateTypeParams(tparams, tvars)
+
+ // Note that isCompatible side-effects: subtype checks involving typevars
+ // are recorded in the typevar's bounds (see TypeConstraint)
+ if (!isCompatible(tp1, pt1)) {
throw new DeferredNoInstance(() =>
- "argument expression's type is not compatible with formal parameter type" +
- foundReqMsg(argtpe.deconst.instantiateTypeParams(tparams, tvars), formal.instantiateTypeParams(tparams, tvars)))
+ "argument expression's type is not compatible with formal parameter type" + foundReqMsg(tp1, pt1))
}
- ()
}
- if (inferInfo)
- println("solve "+tvars+" "+(tvars map (_.constr)))
- val targs = solvedTypes(tvars, tparams, tparams map varianceInTypes(formals),
- false, lubDepth(formals) max lubDepth(argtpes))
-// val res =
- adjustTypeArgs(tparams, targs, restpe)
-// println("meth type args "+", tparams = "+tparams+", formals = "+formals+", restpe = "+restpe+", argtpes = "+argtpes+", underlying = "+(argtpes map (_.widen))+", pt = "+pt+", uninstantiated = "+uninstantiated.toList+", result = "+res) //DEBUG
-// res
+ val targs = solvedTypes(
+ tvars, tparams, tparams map varianceInTypes(formals),
+ false, lubDepth(formals) max lubDepth(argtpes)
+ )
+ val result = adjustTypeArgs(tparams, targs, restpe)
+
+ printInference(
+ ptBlock("methTypeArgs result",
+ "tvars" -> tvars,
+ "constraints" -> tvars.map(_.constr),
+ "targs" -> targs,
+ "adjusted type args" -> result
+ )
+ )
+ result
}
private[typechecker] def followApply(tp: Type): Type = tp match {
@@ -683,9 +750,8 @@ trait Infer {
* - namesOK is false when there's an invalid use of named arguments
*/
private def checkNames(argtpes: List[Type], params: List[Symbol]) = {
- val argPos = (new Array[Int](argtpes.length)) map (x => -1)
- var positionalAllowed = true
- var namesOK = true
+ val argPos = Array.fill(argtpes.length)(-1)
+ var positionalAllowed, namesOK = true
var index = 0
val argtpes1 = argtpes map {
case NamedType(name, tp) => // a named argument
@@ -772,7 +838,7 @@ trait Infer {
try {
val AdjustedTypeArgs.Undets(okparams, okargs, leftUndet) = methTypeArgs(undetparams, formals, restpe, argtpes, pt)
// #2665: must use weak conformance, not regular one (follow the monomorphic case above)
- (exprTypeArgs(leftUndet, restpe.instantiateTypeParams(okparams, okargs), pt, isWeaklyCompatible) ne null) &&
+ (exprTypeArgs(leftUndet, restpe.instantiateTypeParams(okparams, okargs), pt, useWeaklyCompatible = true) ne null) &&
isWithinBounds(NoPrefix, NoSymbol, okparams, okargs)
} catch {
case ex: NoInstance => false
@@ -1055,8 +1121,8 @@ trait Infer {
errors foreach {case (targ, tparam, arityMismatches, varianceMismatches, stricterBounds) => errorMessages +=
(targ+"'s type parameters do not match "+tparam+"'s expected parameters: "+
(for ((a, p) <- arityMismatches)
- yield a+qualify(a,p)+ " has "+reporter.countElementsAsString(a.typeParams.length, "type parameter")+", but "+
- p+qualify(p,a)+" has "+reporter.countAsString(p.typeParams.length)).toList.mkString(", ") +
+ yield a+qualify(a,p)+ " has "+countElementsAsString(a.typeParams.length, "type parameter")+", but "+
+ p+qualify(p,a)+" has "+countAsString(p.typeParams.length)).toList.mkString(", ") +
(for ((a, p) <- varianceMismatches)
yield a+qualify(a,p)+ " is "+varStr(a)+", but "+
p+qualify(p,a)+" is declared "+varStr(p)).toList.mkString(", ") +
@@ -1075,39 +1141,52 @@ trait Infer {
* attempts fail, an error is produced.
*/
def inferArgumentInstance(tree: Tree, undetparams: List[Symbol], strictPt: Type, lenientPt: Type) {
- if (inferInfo)
- println("infer argument instance "+tree+":"+tree.tpe+"\n"+
- " undetparams = "+undetparams+"\n"+
- " strict pt = "+strictPt+"\n"+
- " lenient pt = "+lenientPt)
+ printInference(
+ ptBlock("inferArgumentInstance",
+ "tree" -> tree,
+ "tree.tpe" -> tree.tpe,
+ "undetparams" -> undetparams,
+ "strictPt" -> strictPt,
+ "lenientPt" -> lenientPt
+ )
+ )
var targs = exprTypeArgs(undetparams, tree.tpe, strictPt)
if ((targs eq null) || !(tree.tpe.subst(undetparams, targs) <:< strictPt)) {
targs = exprTypeArgs(undetparams, tree.tpe, lenientPt)
}
substExpr(tree, undetparams, targs, lenientPt)
+ printInference("[inferArgumentInstance] finished, targs = " + targs)
}
-
/** Infer type arguments `targs` for `tparams` of polymorphic expression in `tree`, given prototype `pt`.
*
* Substitute `tparams` to `targs` in `tree`, after adjustment by `adjustTypeArgs`, returning the type parameters that were not determined
* If passed, infers against specified type `treeTp` instead of `tree.tp`.
*/
- def inferExprInstance(tree: Tree, tparams: List[Symbol], pt: Type = WildcardType, treeTp0: Type = null, keepNothings: Boolean = true, checkCompat: (Type, Type) => Boolean = isCompatible): List[Symbol] = {
+ def inferExprInstance(tree: Tree, tparams: List[Symbol], pt: Type = WildcardType, treeTp0: Type = null, keepNothings: Boolean = true, useWeaklyCompatible: Boolean = false): List[Symbol] = {
val treeTp = if(treeTp0 eq null) tree.tpe else treeTp0 // can't refer to tree in default for treeTp0
- if (inferInfo)
- println("infer expr instance "+tree+":"+tree.tpe+"\n"+
- " tparams = "+tparams+"\n"+
- " pt = "+pt)
-
- val targs = exprTypeArgs(tparams, treeTp, pt, checkCompat)
+ printInference(
+ ptBlock("inferExprInstance",
+ "tree" -> tree,
+ "tree.tpe"-> tree.tpe,
+ "tparams" -> tparams,
+ "pt" -> pt
+ )
+ )
+ val targs = exprTypeArgs(tparams, treeTp, pt, useWeaklyCompatible)
if (keepNothings || (targs eq null)) { //@M: adjustTypeArgs fails if targs==null, neg/t0226
substExpr(tree, tparams, targs, pt)
List()
} else {
val AdjustedTypeArgs.Undets(okParams, okArgs, leftUndet) = adjustTypeArgs(tparams, targs)
- if (inferInfo) println("inferred expr instance for "+ tree +" --> (okParams, okArgs, leftUndet)= "+(okParams, okArgs, leftUndet))
+ printInference(
+ ptBlock("inferExprInstance/AdjustedTypeArgs",
+ "okParams" -> okParams,
+ "okArgs" -> okArgs,
+ "leftUndet" -> leftUndet
+ )
+ )
substExpr(tree, okParams, okArgs, pt)
leftUndet
}
@@ -1135,49 +1214,59 @@ trait Infer {
/** Substitute free type variables <code>undetparams</code> of application
* <code>fn(args)</code>, given prototype <code>pt</code>.
*
- * @param fn ...
- * @param undetparams ...
- * @param args ...
- * @param pt ...
+ * @param fn fn: the function that needs to be instantiated.
+ * @param undetparams the parameters that need to be determined
+ * @param args the actual arguments supplied in the call.
+ * @param pt the expected type of the function application
* @return The type parameters that remain uninstantiated,
* and that thus have not been substituted.
*/
def inferMethodInstance(fn: Tree, undetparams: List[Symbol],
args: List[Tree], pt0: Type): List[Symbol] = fn.tpe match {
case MethodType(params0, _) =>
- if (inferInfo)
- println("infer method instance "+fn+"\n"+
- " undetparams = "+undetparams+"\n"+
- " args = "+args+"\n"+
- " pt = "+pt0)
+ printInference(
+ ptBlock("inferMethodInstance",
+ "fn" -> fn,
+ "undetparams" -> undetparams,
+ "args" -> args,
+ "pt0" -> pt0
+ )
+ )
+
try {
- val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0
+ val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0
val formals = formalTypes(params0 map (_.tpe), args.length)
- val argtpes = actualTypes(args map (_.tpe.deconst), formals.length)
- val restpe = fn.tpe.resultType(argtpes)
- val AdjustedTypeArgs.AllArgsAndUndets(okparams, okargs, allargs, leftUndet) = methTypeArgs(undetparams, formals, restpe, argtpes, pt)
+ val argtpes = actualTypes(args map (x => elimAnonymousClass(x.tpe.deconst)), formals.length)
+ val restpe = fn.tpe.resultType(argtpes)
+
+ val AdjustedTypeArgs.AllArgsAndUndets(okparams, okargs, allargs, leftUndet) =
+ methTypeArgs(undetparams, formals, restpe, argtpes, pt)
+
checkBounds(fn.pos, NoPrefix, NoSymbol, undetparams, allargs, "inferred ")
val treeSubst = new TreeTypeSubstituter(okparams, okargs)
- treeSubst.traverse(fn)
- treeSubst.traverseTrees(args)
- if(leftUndet nonEmpty) { // #3890
- val leftUndet1 = treeSubst.typeSubst mapOver leftUndet
- if(leftUndet ne leftUndet1) {
- val symSubst = new TreeSymSubstTraverser(leftUndet, leftUndet1)
- symSubst.traverse(fn)
- symSubst.traverseTrees(args)
- }
- leftUndet1
- } else leftUndet
- } catch {
- case ex: NoInstance =>
- errorTree(fn,
- "no type parameters for " +
- applyErrorMsg(
- fn, " exist so that it can be applied to arguments ",
- args map (_.tpe.widen), WildcardType) +
- "\n --- because ---\n" + ex.getMessage())
- List()
+ treeSubst traverseTrees fn :: args
+
+ val result = leftUndet match {
+ case Nil => Nil
+ case xs =>
+ // #3890
+ val xs1 = treeSubst.typeSubst mapOver xs
+ if (xs ne xs1)
+ new TreeSymSubstTraverser(xs, xs1) traverseTrees fn :: args
+
+ xs1
+ }
+ if (result.nonEmpty)
+ printInference("inferMethodInstance, still undetermined: " + result)
+
+ result
+ }
+ catch ifNoInstance { msg =>
+ errorTree(fn, "no type parameters for " +
+ applyErrorMsg(fn, " exist so that it can be applied to arguments ", args map (_.tpe.widen), WildcardType) +
+ "\n --- because ---\n" + msg
+ )
+ Nil
}
}
@@ -1188,7 +1277,7 @@ trait Infer {
case TypeRef(_, sym, _) if sym.isAliasType =>
widen(tp.normalize)
case rtp @ RefinedType(parents, decls) =>
- copyRefinedType(rtp, parents mapConserve (widen), decls)
+ copyRefinedType(rtp, parents mapConserve widen, decls)
case AnnotatedType(_, underlying, _) =>
widen(underlying)
case _ =>
@@ -1322,6 +1411,7 @@ trait Infer {
} else {
for (arg <- args) {
if (sym == ArrayClass) check(arg, bound)
+ else if (arg.typeArgs.nonEmpty) () // avoid spurious warnings with higher-kinded types
else arg match {
case TypeRef(_, sym, _) if isLocalBinding(sym) =>
;
@@ -1497,14 +1587,11 @@ trait Infer {
*/
def inferExprAlternative(tree: Tree, pt: Type): Unit = tree.tpe match {
case OverloadedType(pre, alts) => tryTwice {
- var alts1 = alts filter (alt => isWeaklyCompatible(pre.memberType(alt), pt))
+ val alts0 = alts filter (alt => isWeaklyCompatible(pre.memberType(alt), pt))
+ val secondTry = alts0.isEmpty
+ val alts1 = if (secondTry) alts else alts0
+
//println("trying "+alts1+(alts1 map (_.tpe))+(alts1 map (_.locationString))+" for "+pt)
- val applicable = alts1
- var secondTry = false
- if (alts1.isEmpty) {
- alts1 = alts
- secondTry = true
- }
def improves(sym1: Symbol, sym2: Symbol): Boolean =
sym2 == NoSymbol || sym2.hasAnnotation(BridgeClass) ||
{ val tp1 = pre.memberType(sym1)
@@ -1512,9 +1599,12 @@ trait Infer {
(tp2 == ErrorType ||
!global.typer.infer.isWeaklyCompatible(tp2, pt) && global.typer.infer.isWeaklyCompatible(tp1, pt) ||
isStrictlyMoreSpecific(tp1, tp2, sym1, sym2)) }
+
val best = ((NoSymbol: Symbol) /: alts1) ((best, alt) =>
if (improves(alt, best)) alt else best)
+
val competing = alts1 dropWhile (alt => best == alt || improves(best, alt))
+
if (best == NoSymbol) {
if (settings.debug.value) {
tree match {
@@ -1635,9 +1725,7 @@ trait Infer {
if (context.implicitsEnabled) {
val reportGeneralErrors = context.reportGeneralErrors
context.reportGeneralErrors = false
- try {
- context.withImplicitsDisabled(infer)
- }
+ try context.withImplicitsDisabled(infer)
catch {
case ex: CyclicReference => throw ex
case ex: TypeError =>