summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/reflect/internal/Trees.scala10
-rw-r--r--src/compiler/scala/reflect/internal/TypeDebugging.scala39
-rw-r--r--src/compiler/scala/reflect/internal/Types.scala39
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala7
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala70
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala228
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala316
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala9
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala73
-rw-r--r--test/files/neg/bug278.check2
-rw-r--r--test/files/neg/bug588.check4
-rw-r--r--test/files/neg/bug752.check4
-rw-r--r--test/files/neg/divergent-implicit.check6
-rw-r--r--test/files/neg/t0003.check4
-rw-r--r--test/files/neg/t0015.check2
-rw-r--r--test/files/neg/unit2anyref.check4
-rw-r--r--test/files/neg/variances.check2
19 files changed, 505 insertions, 317 deletions
diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala
index 6d3cc8b8cb..2ee1a59355 100644
--- a/src/compiler/scala/reflect/internal/Trees.scala
+++ b/src/compiler/scala/reflect/internal/Trees.scala
@@ -1507,6 +1507,10 @@ trait Trees /*extends reflect.generic.Trees*/ { self: SymbolTable =>
if (tree eq orig) super.transform(tree)
else tree
}
+ // Create a readable string describing a substitution.
+ private def substituterString(fromStr: String, toStr: String, from: List[Any], to: List[Any]): String = {
+ "subst[%s, %s](%s)".format(fromStr, toStr, (from, to).zipped map (_ + " -> " + _) mkString ", ")
+ }
class TreeSubstituter(from: List[Symbol], to: List[Tree]) extends Transformer {
override def transform(tree: Tree): Tree = tree match {
@@ -1519,11 +1523,13 @@ trait Trees /*extends reflect.generic.Trees*/ { self: SymbolTable =>
case _ =>
super.transform(tree)
}
+ override def toString = substituterString("Symbol", "Tree", from, to)
}
class TreeTypeSubstituter(val from: List[Symbol], val to: List[Type]) extends Traverser {
val typeSubst = new SubstTypeMap(from, to)
def fromContains = typeSubst.fromContains
+ def isEmpty = from.isEmpty && to.isEmpty
override def traverse(tree: Tree) {
if (tree.tpe ne null) tree.tpe = typeSubst(tree.tpe)
@@ -1552,7 +1558,7 @@ trait Trees /*extends reflect.generic.Trees*/ { self: SymbolTable =>
super.traverse(tree)
}
override def apply[T <: Tree](tree: T): T = super.apply(tree.duplicate)
- override def toString() = "TreeSymSubstTraverser("+from+","+to+")"
+ override def toString() = "TreeSymSubstTraverser/" + substituterString("Symbol", "Symbol", from, to)
}
/** Substitute symbols in 'from' with symbols in 'to'. Returns a new
@@ -1583,7 +1589,7 @@ trait Trees /*extends reflect.generic.Trees*/ { self: SymbolTable =>
super.transform(tree)
}
def apply[T <: Tree](tree: T): T = transform(tree).asInstanceOf[T]
- override def toString() = "TreeSymSubstituter("+from+","+to+")"
+ override def toString() = "TreeSymSubstituter/" + substituterString("Symbol", "Symbol", from, to)
}
class ChangeOwnerTraverser(val oldowner: Symbol, val newowner: Symbol) extends Traverser {
diff --git a/src/compiler/scala/reflect/internal/TypeDebugging.scala b/src/compiler/scala/reflect/internal/TypeDebugging.scala
index 88fab9054b..3680a7996e 100644
--- a/src/compiler/scala/reflect/internal/TypeDebugging.scala
+++ b/src/compiler/scala/reflect/internal/TypeDebugging.scala
@@ -12,7 +12,33 @@ trait TypeDebugging {
import definitions._
// @M toString that is safe during debugging (does not normalize, ...)
- object TypeDebugStrings {
+ object typeDebug {
+ private def to_s(x: Any): String = x match {
+ // otherwise case classes are caught looking like products
+ case _: Tree | _: Type => "" + x
+ case x: TraversableOnce[_] => x mkString ", "
+ case x: Product => x.productIterator mkString ("(", ", ", ")")
+ case _ => "" + x
+ }
+ def ptIndent(x: Any) = ("" + x).replaceAll("\\n", " ")
+ def ptBlock(label: String, pairs: (String, Any)*): String = {
+ val width = pairs map (_._1.length) max
+ val fmt = "%-" + (width + 1) + "s %s"
+ val strs = pairs map { case (k, v) => fmt.format(k, to_s(v)) }
+
+ strs.mkString(label + " {\n ", "\n ", "\n}")
+ }
+ def ptLine(label: String, pairs: (String, Any)*): String = {
+ val strs = pairs map { case (k, v) => k + "=" + to_s(v) }
+ strs.mkString(label + ": ", ", ", "")
+ }
+ def ptTree(t: Tree) = t match {
+ case PackageDef(pid, _) => "package " + pid
+ case ModuleDef(_, name, _) => "object " + name
+ case ClassDef(_, name, tparams, _) => "class " + name + str.brackets(tparams)
+ case _ => to_s(t)
+ }
+
object str {
def parentheses(xs: List[_]): String = xs.mkString("(", ", ", ")")
def brackets(xs: List[_]): String = if (xs.isEmpty) "" else xs.mkString("[", ", ", "]")
@@ -62,11 +88,8 @@ trait TypeDebugging {
}
def debugString(tp: Type) = debug(tp)
}
- private def TDS = TypeDebugStrings
-
- def paramString(tp: Type) = TDS.str parentheses (tp.params map (_.defString))
- def typeParamsString(tp: Type) = TDS.str brackets (tp.typeParams map (_.defString))
- def typeArgsString(tp: Type) = TDS.str brackets (tp.typeArgs map (_.safeToString))
- def debugString(tp: Type) = TDS debugString tp
+ def paramString(tp: Type) = typeDebug.str parentheses (tp.params map (_.defString))
+ def typeParamsString(tp: Type) = typeDebug.str brackets (tp.typeParams map (_.defString))
+ def typeArgsString(tp: Type) = typeDebug.str brackets (tp.typeArgs map (_.safeToString))
+ def debugString(tp: Type) = typeDebug debugString tp
}
-
diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala
index 572cb8d3a4..0e9f2c44e1 100644
--- a/src/compiler/scala/reflect/internal/Types.scala
+++ b/src/compiler/scala/reflect/internal/Types.scala
@@ -79,8 +79,6 @@ trait Types /*extends reflect.generic.Types*/ { self: SymbolTable =>
private var explainSwitch = false
private final val emptySymbolSet = immutable.Set.empty[Symbol]
- private final val alternativeNarrow = false
-
private final val LogPendingSubTypesThreshold = 50
private final val LogPendingBaseTypesThreshold = 50
private final val LogVolatileThreshold = 50
@@ -1931,8 +1929,16 @@ A type's typeSymbol should never be inspected directly.
case TypeRef(_, RepeatedParamClass, arg :: _) => return arg + "*"
case TypeRef(_, ByNameParamClass, arg :: _) => return "=> " + arg
case _ =>
- if (isFunctionType(this))
- return normalize.typeArgs.init.mkString("(", ", ", ")") + " => " + normalize.typeArgs.last
+ if (isFunctionType(this)) {
+ val targs = normalize.typeArgs
+ // Aesthetics: printing Function1 as T => R rather than (T) => R
+ val paramlist = targs.init match {
+ case Nil => "()"
+ case x :: Nil => "" + x
+ case xs => xs.mkString("(", ", ", ")")
+ }
+ return paramlist + " => " + targs.last
+ }
else if (isTupleTypeOrSubtype(this))
return normalize.typeArgs.mkString("(", ", ", if (hasLength(normalize.typeArgs, 1)) ",)" else ")")
else if (sym.isAliasType && prefixChain.exists(_.termSymbol.isSynthetic)) {
@@ -2281,10 +2287,12 @@ A type's typeSymbol should never be inspected directly.
//@M
// a TypeVar used to be a case class with only an origin and a constr
- // then, constr became mutable (to support UndoLog, I guess), but pattern-matching returned the original constr0 (a bug)
+ // then, constr became mutable (to support UndoLog, I guess),
+ // but pattern-matching returned the original constr0 (a bug)
// now, pattern-matching returns the most recent constr
object TypeVar {
- // encapsulate suspension so we can automatically link the suspension of cloned typevars to their original if this turns out to be necessary
+ // encapsulate suspension so we can automatically link the suspension of cloned
+ // typevars to their original if this turns out to be necessary
def Suspension = new Suspension
class Suspension {
private val suspended = mutable.HashSet[TypeVar]()
@@ -2293,17 +2301,20 @@ A type's typeSymbol should never be inspected directly.
suspended += tv
}
def resumeAll(): Unit = {
- for(tv <- suspended) {
+ for (tv <- suspended) {
tv.suspended = false
}
- suspended.clear
+ suspended.clear()
}
}
def unapply(tv: TypeVar): Some[(Type, TypeConstraint)] = Some((tv.origin, tv.constr))
def apply(origin: Type, constr: TypeConstraint) = new TypeVar(origin, constr, List(), List())
- def apply(tparam: Symbol) = new TypeVar(tparam.tpeHK, new TypeConstraint, List(), tparam.typeParams) // TODO why not initialise TypeConstraint with bounds of tparam?
- def apply(origin: Type, constr: TypeConstraint, args: List[Type], params: List[Symbol]) = new TypeVar(origin, constr, args, params)
+ // TODO why not initialise TypeConstraint with bounds of tparam?
+ // @PP: I tried that, didn't work out so well for me.
+ def apply(tparam: Symbol) = new TypeVar(tparam.tpeHK, new TypeConstraint, List(), tparam.typeParams)
+ def apply(origin: Type, constr: TypeConstraint, args: List[Type], params: List[Symbol]) =
+ new TypeVar(origin, constr, args, params)
}
/** A class representing a type variable
@@ -2438,7 +2449,8 @@ A type's typeSymbol should never be inspected directly.
}
}
- def registerTypeEquality(tp: Type, typeVarLHS: Boolean): Boolean = { //println("regTypeEq: "+(safeToString, debugString(tp), typeVarLHS)) //@MDEBUG
+ def registerTypeEquality(tp: Type, typeVarLHS: Boolean): Boolean = {
+ //println("regTypeEq: "+(safeToString, debugString(tp), typeVarLHS)) //@MDEBUG
def checkIsSameType(tp: Type) =
if(typeVarLHS) constr.inst =:= tp
else tp =:= constr.inst
@@ -2600,8 +2612,7 @@ A type's typeSymbol should never be inspected directly.
// Creators ---------------------------------------------------------------
- /** Rebind symbol `sym' to an overriding member in type
- * `pre'.
+ /** Rebind symbol `sym' to an overriding member in type `pre'.
*/
private def rebind(pre: Type, sym: Symbol): Symbol = {
val owner = sym.owner
@@ -2642,7 +2653,7 @@ A type's typeSymbol should never be inspected directly.
}
/** the canonical creator for a refined type with a given scope */
- def refinedType(parents: List[Type], owner: Symbol, decls: Scope, pos : Position): Type = {
+ def refinedType(parents: List[Type], owner: Symbol, decls: Scope, pos: Position): Type = {
if (phase.erasedTypes)
if (parents.isEmpty) ObjectClass.tpe else parents.head
else {
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 0f5be1791b..c777652706 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -247,7 +247,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
// debugging
def checkPhase = wasActive(settings.check)
def logPhase = isActive(settings.log)
- def typerDebug = settings.Ytyperdebug.value
def writeICode = settings.writeICode.value
// showing/printing things
@@ -270,9 +269,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
def profileClass = settings.YprofileClass.value
def profileMem = settings.YprofileMem.value
- // XXX: short term, but I can't bear to add another option.
- // scalac -Dscala.timings will make this true.
+ // shortish-term property based options
def timings = sys.props contains "scala.timings"
+ def inferDebug = (sys.props contains "scalac.debug.infer") || settings.Yinferdebug.value
+ def typerDebug = (sys.props contains "scalac.debug.typer") || settings.Ytyperdebug.value
}
// True if -Xscript has been set, indicating a script run.
@@ -350,6 +350,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
/** Switch to turn on detailed type logs */
var printTypings = opt.typerDebug
+ var printInfers = opt.inferDebug
// phaseName = "parser"
object syntaxAnalyzer extends {
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 921abd6795..b896883e38 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -141,6 +141,7 @@ trait ScalaSettings extends AbsScalaSettings with StandardScalaSettings {
val Ybuildmanagerdebug =
BooleanSetting ("-Ybuild-manager-debug", "Generate debug information for the Refined Build Manager compiler.")
val Ytyperdebug = BooleanSetting ("-Ytyper-debug", "Trace all type assignments.")
+ val Yinferdebug = BooleanSetting ("-Yinfer-debug", "Trace type inference and implicit search.")
val Ypmatdebug = BooleanSetting ("-Ypmat-debug", "Trace all pattern matcher activity.")
val Yrepldebug = BooleanSetting ("-Yrepl-debug", "Trace all repl activity.") .
withPostSetHook(_ => interpreter.replProps.debug setValue true)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index ba534d322c..33822fbd43 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -124,8 +124,10 @@ trait Contexts { self: Analyzer =>
var savedTypeBounds: List[(Symbol, Type)] = List() // saved type bounds
// for type parameters which are narrowed in a GADT
- var typingIndent: String = ""
+ var typingIndentLevel: Int = 0
+ def typingIndent = " " * typingIndentLevel
+ def undetparamsString = if (undetparams.isEmpty) "" else undetparams.mkString("undetparams=", ", ", "")
def undetparams = _undetparams
def undetparams_=(ps: List[Symbol]) = {
//System.out.println("undetparams = " + ps);//debug
@@ -138,6 +140,13 @@ trait Contexts { self: Analyzer =>
tparams
}
+ def withoutReportingErrors[T](op: => T): T = {
+ val saved = reportGeneralErrors
+ reportGeneralErrors = false
+ try op
+ finally reportGeneralErrors = saved
+ }
+
def withImplicitsDisabled[T](op: => T): T = {
val saved = implicitsEnabled
implicitsEnabled = false
@@ -188,7 +197,7 @@ trait Contexts { self: Analyzer =>
c.reportAmbiguousErrors = this.reportAmbiguousErrors
c.reportGeneralErrors = this.reportGeneralErrors
c.diagnostic = this.diagnostic
- c.typingIndent = typingIndent
+ c.typingIndentLevel = typingIndentLevel
c.implicitsEnabled = this.implicitsEnabled
c.checking = this.checking
c.retyping = this.retyping
@@ -208,8 +217,6 @@ trait Contexts { self: Analyzer =>
def makeNewImport(imp: Import): Context =
make(unit, imp, owner, scope, new ImportInfo(imp, depth) :: imports)
-
-
def make(tree: Tree, owner: Symbol, scope: Scope): Context = {
if (tree == this.tree && owner == this.owner && scope == this.scope) this
else make0(tree, owner, scope)
@@ -328,28 +335,39 @@ trait Contexts { self: Analyzer =>
} else throw new TypeError(pos, msg)
}
- def outerContext(clazz: Symbol): Context = {
- var c = this
- while (c != NoContext && c.owner != clazz) c = c.outer.enclClass
- c
- }
-
def isLocal(): Boolean = tree match {
- case Block(_,_) => true
+ case Block(_,_) => true
case PackageDef(_, _) => false
- case EmptyTree => false
- case _ => outer.isLocal()
+ case EmptyTree => false
+ case _ => outer.isLocal()
+ }
+
+ // nextOuter determines which context is searched next for implicits
+ // (after `this`, which contributes `newImplicits` below.) In
+ // most cases, it is simply the outer context: if we're owned by
+ // a constructor, the actual current context and the conceptual
+ // context are different when it comes to scoping. The current
+ // conceptual scope is the context enclosing the blocks which
+ // represent the constructor body (TODO: why is there more than one
+ // such block in the outer chain?)
+ private def nextOuter = {
+ // Drop the constructor body blocks, which come in varying numbers.
+ // -- If the first statement is in the constructor, scopingCtx == (constructor definition)
+ // -- Otherwise, scopingCtx == (the class which contains the constructor)
+ val scopingCtx =
+ if (owner.isConstructor) nextEnclosing(c => !c.tree.isInstanceOf[Block])
+ else this
+
+ scopingCtx.outer
}
def nextEnclosing(p: Context => Boolean): Context =
if (this == NoContext || p(this)) this else outer.nextEnclosing(p)
- override def toString(): String = {
+ override def toString = (
if (this == NoContext) "NoContext"
- else owner.toString() + " @ " + tree.getClass() +
- " " + tree.toString() + ", scope = " + scope.## +
- " " + scope.toList + "\n:: " + outer.toString()
- }
+ else "Context(%s@%s scope=%s)".format(owner.fullName, tree.getClass.getName split "[.$]" last, scope.##)
+ )
/** Is `sub' a subclass of `base' or a companion object of such a subclass?
*/
@@ -502,7 +520,7 @@ trait Contexts { self: Analyzer =>
def resetCache() {
implicitsRunId = NoRunId
implicitsCache = null
- if (outer != null && outer != this) outer.resetCache
+ if (outer != null && outer != this) outer.resetCache()
}
/** A symbol `sym` qualifies as an implicit if it has the IMPLICIT flag set,
@@ -518,8 +536,8 @@ trait Contexts { self: Analyzer =>
})
private def collectImplicits(syms: List[Symbol], pre: Type, imported: Boolean = false): List[ImplicitInfo] =
- for (sym <- syms if isQualifyingImplicit(sym, pre, imported))
- yield new ImplicitInfo(sym.name, pre, sym)
+ for (sym <- syms if isQualifyingImplicit(sym, pre, imported)) yield
+ new ImplicitInfo(sym.name, pre, sym)
private def collectImplicitImports(imp: ImportInfo): List[ImplicitInfo] = {
val pre = imp.qual.tpe
@@ -542,16 +560,6 @@ trait Contexts { self: Analyzer =>
}
def implicitss: List[List[ImplicitInfo]] = {
- // nextOuter determines which context is searched next for implicits (after `this`, which contributes `newImplicits` below)
- // in most cases, it is simply the outer context
- // if we're owned by a constructor, the actual current context and the conceptual context are different when it comes to scoping:
- // the current conceptual scope is the context enclosing the blocks that represent the constructor body
- // (TODO: why is there more than one such block in the outer chain?)
- val scopingCtx =
- if(owner.isConstructor) nextEnclosing(c => !c.tree.isInstanceOf[Block]) // drop the constructor body blocks (they come in varying numbers depending on whether we are in the ctor call in the first statement or after)
- // scopingCtx == the constructor definition (if we were after the ctor call) or the class that contains this constructor (if we are in the ctor call)
- else this
- val nextOuter = scopingCtx.outer
if (implicitsRunId != currentRunId) {
implicitsRunId = currentRunId
diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala
index 3ae99a5bed..eee75e1b2a 100644
--- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala
@@ -25,7 +25,7 @@ trait EtaExpansion { self: Analyzer =>
}
def unapply(tree: Tree): Option[(List[ValDef], Tree, List[Tree])] = tree match {
- case Function(vparams, Apply(fn, args)) if (vparams corresponds args)(isMatch) => // @PP: corresponds
+ case Function(vparams, Apply(fn, args)) if (vparams corresponds args)(isMatch) =>
Some((vparams, fn, args))
case _ =>
None
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 53b4f0dac6..da07723b89 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -28,9 +28,8 @@ trait Implicits {
import global._
import definitions._
-
- def traceImplicits = printTypings
- import global.typer.{printTyping, deindentTyping, indentTyping}
+ import typeDebug.{ ptTree, ptBlock, ptLine }
+ import global.typer.{ printTyping, deindentTyping, indentTyping, printInference }
/** Search for an implicit value. See the comment on `result` at the end of class `ImplicitSearch`
* for more info how the search is conducted.
@@ -46,16 +45,29 @@ trait Implicits {
* @return A search result
*/
def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context): SearchResult = {
- printTyping("Beginning implicit search for "+ tree +" expecting "+ pt + (if(isView) " looking for a view" else ""))
+ printInference("[inferImplicit%s] pt = %s".format(
+ if (isView) " view" else "", pt)
+ )
+ printTyping(
+ ptBlock("infer implicit" + (if (isView) " view" else ""),
+ "tree" -> tree,
+ "pt" -> pt,
+ "undetparams" -> context.outer.undetparams
+ )
+ )
indentTyping()
- val rawTypeStart = startCounter(rawTypeImpl)
+
+ val rawTypeStart = startCounter(rawTypeImpl)
val findMemberStart = startCounter(findMemberImpl)
- val subtypeStart = startCounter(subtypeImpl)
+ val subtypeStart = startCounter(subtypeImpl)
val start = startTimer(implicitNanos)
- if (traceImplicits && !tree.isEmpty && !context.undetparams.isEmpty)
- println("typing implicit with undetermined type params: "+context.undetparams+"\n"+tree)
+ if (printInfers && !tree.isEmpty && !context.undetparams.isEmpty)
+ printTyping("typing implicit: %s %s".format(tree, context.undetparamsString))
+
val result = new ImplicitSearch(tree, pt, isView, context.makeImplicit(reportAmbiguous)).bestImplicit
+ printInference("[inferImplicit] result: " + result)
context.undetparams = context.undetparams filterNot result.subst.fromContains
+
stopTimer(implicitNanos, start)
stopCounter(rawTypeImpl, rawTypeStart)
stopCounter(findMemberImpl, findMemberStart)
@@ -87,7 +99,8 @@ trait Implicits {
* that were instantiated by the winning implicit.
*/
class SearchResult(val tree: Tree, val subst: TreeTypeSubstituter) {
- override def toString = "SearchResult("+tree+", "+subst+")"
+ override def toString = "SearchResult(%s, %s)".format(tree,
+ if (subst.isEmpty) "" else subst)
}
lazy val SearchFailure = new SearchResult(EmptyTree, EmptyTreeTypeSubstituter)
@@ -122,12 +135,9 @@ trait Implicits {
tp.isError
}
- def isCyclicOrErroneous = try {
- containsError(tpe)
- } catch {
- case ex: CyclicReference =>
- true
- }
+ def isCyclicOrErroneous =
+ try containsError(tpe)
+ catch { case _: CyclicReference => true }
override def equals(other: Any) = other match {
case that: ImplicitInfo =>
@@ -137,7 +147,7 @@ trait Implicits {
case _ => false
}
override def hashCode = name.## + pre.## + sym.##
- override def toString = "ImplicitInfo(" + name + "," + pre + "," + sym + ")"
+ override def toString = name + ": " + tpe
}
/** A sentinel indicating no implicit was found */
@@ -222,7 +232,15 @@ trait Implicits {
*/
class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context)
extends Typer(context0) {
- printTyping("begin implicit search: "+(tree, pt, isView, context.outer.undetparams))
+ printTyping(
+ ptBlock("new ImplicitSearch",
+ "tree" -> tree,
+ "pt" -> pt,
+ "isView" -> isView,
+ "context0" -> context0,
+ "undetparams" -> context.outer.undetparams
+ )
+ )
// assert(tree.isEmpty || tree.pos.isDefined, tree)
import infer._
@@ -324,20 +342,29 @@ trait Implicits {
if (isView) {
val found = pt.typeArgs(0)
val req = pt.typeArgs(1)
+ def defaultExplanation =
+ "Note that implicit conversions are not applicable because they are ambiguous:\n "+
+ coreMsg+"are possible conversion functions from "+ found+" to "+req
- /** A nice spot to explain some common situations a little
- * less confusingly.
- */
def explanation = {
- if ((found =:= AnyClass.tpe) && (AnyRefClass.tpe <:< req))
- "Note: Any is not implicitly converted to AnyRef. You can safely\n" +
- "pattern match x: AnyRef or cast x.asInstanceOf[AnyRef] to do so."
- else if ((found <:< AnyValClass.tpe) && (AnyRefClass.tpe <:< req))
- "Note: primitive types are not implicitly converted to AnyRef.\n" +
- "You can safely force boxing by casting x.asInstanceOf[AnyRef]."
- else
- "Note that implicit conversions are not applicable because they are ambiguous:\n "+
- coreMsg+"are possible conversion functions from "+ found+" to "+req
+ val sym = found.typeSymbol
+ // Explain some common situations a bit more clearly.
+ if (AnyRefClass.tpe <:< req) {
+ if (sym == AnyClass || sym == UnitClass) {
+ "Note: " + sym.name + " is not implicitly converted to AnyRef. You can safely\n" +
+ "pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so."
+ }
+ else boxedClass get sym match {
+ case Some(boxed) =>
+ "Note: an implicit exists from " + sym.fullName + " => " + boxed.fullName + ", but\n" +
+ "methods inherited from Object are rendered ambiguous. This is to avoid\n" +
+ "a blanket implicit which would convert any " + sym.fullName + " to any AnyRef.\n" +
+ "You may wish to use a type ascription: `x: " + boxed.fullName + "`."
+ case _ =>
+ defaultExplanation
+ }
+ }
+ else defaultExplanation
}
typeErrorMsg(found, req) + "\n" + explanation
@@ -350,7 +377,6 @@ trait Implicits {
/** The type parameters to instantiate */
val undetParams = if (isView) List() else context.outer.undetparams
- /** Replace undetParams in type `tp` by Any/Nothing, according to variance */
def approximate(tp: Type) =
if (undetParams.isEmpty) tp
else tp.instantiateTypeParams(undetParams, undetParams map (_ => WildcardType))
@@ -364,7 +390,8 @@ trait Implicits {
* @param info The given implicit info describing the implicit definition
* @pre <code>info.tpe</code> does not contain an error
*/
- private def typedImplicit(info: ImplicitInfo, ptChecked: Boolean): SearchResult =
+ private def typedImplicit(info: ImplicitInfo, ptChecked: Boolean): SearchResult = {
+ printInference("[typedImplicit] " + info)
(context.openImplicits find { case (tp, sym) => sym == tree.symbol && dominates(pt, tp)}) match {
case Some(pending) =>
// println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) //@MDEBUG
@@ -390,6 +417,7 @@ trait Implicits {
context.openImplicits = context.openImplicits.tail
}
}
+ }
/** Todo reconcile with definition of stability given in Types.scala */
private def isStable(tp: Type): Boolean = tp match {
@@ -443,9 +471,23 @@ trait Implicits {
private def typedImplicit0(info: ImplicitInfo, ptChecked: Boolean): SearchResult = {
incCounter(plausiblyCompatibleImplicits)
-
- printTyping("typed impl for "+wildPt+"? "+info.name +":"+ depoly(info.tpe)+ " orig info= "+ info.tpe +"/"+undetParams+"/"+isPlausiblyCompatible(info.tpe, wildPt)+"/"+matchesPt(depoly(info.tpe), wildPt, List())+"/"+info.pre+"/"+isStable(info.pre))
- if (ptChecked || matchesPt(depoly(info.tpe), wildPt, List()) && isStable(info.pre))
+ printTyping(
+ ptBlock("typedImplicit0",
+ "info.name" -> info.name,
+ "info.tpe" -> depoly(info.tpe),
+ "ptChecked" -> ptChecked,
+ "pt" -> wildPt,
+ "orig" -> ptBlock("info",
+ "matchesPt" -> matchesPt(depoly(info.tpe), wildPt, Nil),
+ "undetParams" -> undetParams,
+ "isPlausiblyCompatible" -> isPlausiblyCompatible(info.tpe, wildPt),
+ "info.pre" -> info.pre,
+ "isStable" -> isStable(info.pre)
+ ).replaceAll("\\n", "\n ")
+ )
+ )
+
+ if (ptChecked || matchesPt(depoly(info.tpe), wildPt, Nil) && isStable(info.pre))
typedImplicit1(info)
else
SearchFailure
@@ -458,7 +500,10 @@ trait Implicits {
if (info.pre == NoPrefix) Ident(info.name)
else Select(gen.mkAttributedQualifier(info.pre), info.name)
}
- printTyping("typedImplicit0 typing"+ itree +" with wildpt = "+ wildPt +" from implicit "+ info.name+":"+info.tpe)
+ printTyping("typedImplicit1 %s, pt=%s, from implicit %s:%s".format(
+ typeDebug.ptTree(itree), wildPt, info.name, info.tpe)
+ )
+
def fail(reason: String): SearchResult = {
if (settings.XlogImplicits.value)
inform(itree+" is not a valid implicit value for "+pt+" because:\n"+reason)
@@ -479,10 +524,14 @@ trait Implicits {
incCounter(typedImplicits)
- printTyping("typed implicit "+itree1+":"+itree1.tpe+", pt = "+wildPt)
+ printTyping("typed implicit %s:%s, pt=%s".format(itree1, itree1.tpe, wildPt))
val itree2 = if (isView) (itree1: @unchecked) match { case Apply(fun, _) => fun }
else adapt(itree1, EXPRmode, wildPt)
- printTyping("adapted implicit "+itree1.symbol+":"+itree2.tpe+" to "+wildPt)
+
+ printTyping("adapted implicit %s:%s to %s".format(
+ itree1.symbol, itree2.tpe, wildPt)
+ )
+
def hasMatchingSymbol(tree: Tree): Boolean = (tree.symbol == info.sym) || {
tree match {
case Apply(fun, _) => hasMatchingSymbol(fun)
@@ -492,11 +541,26 @@ trait Implicits {
}
}
- if (itree2.tpe.isError) SearchFailure
- else if (hasMatchingSymbol(itree1)) {
+ if (itree2.tpe.isError)
+ SearchFailure
+ else if (!hasMatchingSymbol(itree1))
+ fail("candidate implicit %s is shadowed by other implicit %s".format(
+ info.sym + info.sym.locationString, itree1.symbol + itree1.symbol.locationString))
+ else {
val tvars = undetParams map freshVar
+
if (matchesPt(itree2.tpe, pt.instantiateTypeParams(undetParams, tvars), undetParams)) {
- printTyping("tvars = "+tvars+"/"+(tvars map (_.constr)))
+ printInference(
+ ptBlock("matchesPt",
+ "itree1" -> itree1,
+ "tvars" -> tvars,
+ "undetParams" -> undetParams
+ )
+ )
+
+ if (tvars.nonEmpty)
+ printTyping(ptLine("" + info.sym, "tvars" -> tvars, "tvars.constr" -> tvars.map(_.constr)))
+
val targs = solvedTypes(tvars, undetParams, undetParams map varianceInType(pt),
false, lubDepth(List(itree2.tpe, pt)))
@@ -505,39 +569,42 @@ trait Implicits {
// filter out failures from type inference, don't want to remove them from undetParams!
// we must be conservative in leaving type params in undetparams
- val AdjustedTypeArgs(okParams, okArgs) = adjustTypeArgs(undetParams, targs) // prototype == WildcardType: want to remove all inferred Nothing's
- var subst = EmptyTreeTypeSubstituter
- if (okParams.nonEmpty) {
- subst = new TreeTypeSubstituter(okParams, okArgs)
- subst traverse itree2
- }
+ // prototype == WildcardType: want to remove all inferred Nothings
+ val AdjustedTypeArgs(okParams, okArgs) = adjustTypeArgs(undetParams, targs)
+ val subst: TreeTypeSubstituter =
+ if (okParams.isEmpty) EmptyTreeTypeSubstituter
+ else {
+ val subst = new TreeTypeSubstituter(okParams, okArgs)
+ subst traverse itree2
+ subst
+ }
- // #2421b: since type inference (which may have been performed during implicit search)
- // does not check whether inferred arguments meet the bounds of the corresponding parameter (see note in solvedTypes),
- // must check again here:
- // TODO: I would prefer to just call typed instead of duplicating the code here, but this is probably a hotspot (and you can't just call typed, need to force re-typecheck)
+ // #2421b: since type inference (which may have been
+ // performed during implicit search) does not check whether
+ // inferred arguments meet the bounds of the corresponding
+ // parameter (see note in solvedTypes), must check again
+ // here:
+ // TODO: I would prefer to just call typed instead of
+ // duplicating the code here, but this is probably a
+ // hotspot (and you can't just call typed, need to force
+ // re-typecheck)
+ // TODO: the return tree is ignored. This seems to make
+ // no difference, but it's bad practice regardless.
itree2 match {
- case TypeApply(fun, args) => typedTypeApply(itree2, EXPRmode, fun, args)
+ case TypeApply(fun, args) => typedTypeApply(itree2, EXPRmode, fun, args)
case Apply(TypeApply(fun, args), _) => typedTypeApply(itree2, EXPRmode, fun, args) // t2421c
- case _ =>
+ case t => t
}
-
val result = new SearchResult(itree2, subst)
incCounter(foundImplicits)
- if (traceImplicits) println("RESULT = "+result)
- // println("RESULT = "+itree+"///"+itree1+"///"+itree2)//DEBUG
+ printInference("[typedImplicit1] SearchResult: " + result)
result
- } else {
- printTyping("incompatible: "+itree2.tpe+" does not match "+pt.instantiateTypeParams(undetParams, tvars))
-
- SearchFailure
}
+ else fail("incompatible: %s does not match expected type %s".format(
+ itree2.tpe, pt.instantiateTypeParams(undetParams, tvars)))
}
- else if (settings.XlogImplicits.value)
- fail("candidate implicit "+info.sym+info.sym.locationString+
- " is shadowed by other implicit: "+itree1.symbol+itree1.symbol.locationString)
- else SearchFailure
- } catch {
+ }
+ catch {
case ex: TypeError => fail(ex.getMessage())
}
}
@@ -655,6 +722,16 @@ trait Implicits {
// most frequent one first
matches sortBy (x => if (isView) -x.useCountView else -x.useCountArg)
}
+ def eligibleString = {
+ val args = List(
+ "search" -> pt,
+ "target" -> tree,
+ "isView" -> isView
+ ) ++ eligible.map("eligible" -> _)
+
+ ptBlock("Implicit search in " + context, args: _*)
+ }
+ printInference(eligibleString)
/** Faster implicit search. Overall idea:
* - prune aggressively
@@ -855,7 +932,7 @@ trait Implicits {
val infoMap = new InfoMap
getParts(tp)(infoMap, new mutable.HashSet(), Set())
- if (traceImplicits) println("companion implicits of "+tp+" = "+infoMap)
+ printInference("[companionImplicitMap] "+tp+" = "+infoMap)
infoMap
}
@@ -996,7 +1073,7 @@ trait Implicits {
inferImplicit(tree, appliedType(manifestClass.typeConstructor, List(tp)), true, false, context).tree
def findSubManifest(tp: Type) = findManifest(tp, if (full) FullManifestClass else OptManifestClass)
- def mot(tp0: Type)(implicit from: List[Symbol] = List(), to: List[Type] = List()): SearchResult = {
+ def mot(tp0: Type, from: List[Symbol], to: List[Type]): SearchResult = {
implicit def wrapResult(tree: Tree): SearchResult =
if (tree == EmptyTree) SearchFailure else new SearchResult(tree, new TreeTypeSubstituter(from, to))
@@ -1032,24 +1109,29 @@ trait Implicits {
} else if (sym.isExistentiallyBound && full) {
manifestFactoryCall("wildcardType", tp,
findManifest(tp.bounds.lo), findManifest(tp.bounds.hi))
- } else if(undetParams contains sym) { // looking for a manifest of a type parameter that hasn't been inferred by now, can't do much, but let's not fail
- mot(NothingClass.tpe)(sym :: from, NothingClass.tpe :: to) // #3859: need to include the mapping from sym -> NothingClass.tpe in the SearchResult
+ }
+ // looking for a manifest of a type parameter that hasn't been inferred by now,
+ // can't do much, but let's not fail
+ else if (undetParams contains sym) {
+ // #3859: need to include the mapping from sym -> NothingClass.tpe in the SearchResult
+ mot(NothingClass.tpe, sym :: from, NothingClass.tpe :: to)
} else {
- EmptyTree // a manifest should have been found by normal searchImplicit
+ // a manifest should have been found by normal searchImplicit
+ EmptyTree
}
case RefinedType(parents, decls) =>
// refinement is not generated yet
if (hasLength(parents, 1)) findManifest(parents.head)
- else if (full) manifestFactoryCall("intersectionType", tp, parents map (findSubManifest(_)): _*)
- else mot(erasure.erasure.intersectionDominator(parents))
+ else if (full) manifestFactoryCall("intersectionType", tp, parents map findSubManifest: _*)
+ else mot(erasure.erasure.intersectionDominator(parents), from, to)
case ExistentialType(tparams, result) =>
- mot(tp1.skolemizeExistential)
+ mot(tp1.skolemizeExistential, from, to)
case _ =>
EmptyTree
}
}
- mot(tp)
+ mot(tp, Nil, Nil)
}
def wrapResult(tree: Tree): SearchResult =
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 1bd2487089..fbad63d2c9 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -19,10 +19,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 --------------------------- */
@@ -70,13 +71,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]()
@@ -87,7 +87,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
@@ -126,37 +126,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
}
@@ -264,23 +264,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
@@ -585,73 +584,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 {
@@ -684,9 +692,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
@@ -1076,19 +1083,23 @@ 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
@@ -1096,11 +1107,14 @@ trait Infer {
*/
def inferExprInstance(tree: Tree, tparams: List[Symbol], pt: Type = WildcardType, treeTp0: Type = null, keepNothings: Boolean = true, checkCompat: (Type, Type) => Boolean = isCompatible): 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)
-
+ printInference(
+ ptBlock("inferExprInstance",
+ "tree" -> tree,
+ "tree.tpe"-> tree.tpe,
+ "tparams" -> tparams,
+ "pt" -> pt
+ )
+ )
val targs = exprTypeArgs(tparams, treeTp, pt, checkCompat)
if (keepNothings || (targs eq null)) { //@M: adjustTypeArgs fails if targs==null, neg/t0226
@@ -1108,7 +1122,13 @@ trait Infer {
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
}
@@ -1146,39 +1166,49 @@ trait Infer {
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 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
}
}
@@ -1189,7 +1219,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 _ =>
@@ -1498,14 +1528,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)
@@ -1513,9 +1540,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 {
@@ -1636,9 +1666,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 =>
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index f1eb904c58..56d5ce9842 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -1147,11 +1147,12 @@ abstract class RefChecks extends InfoTransform {
*/
private def checkDeprecated(sym: Symbol, pos: Position) {
if (sym.isDeprecated && !currentOwner.ownerChain.exists(x => x.isDeprecated || x.hasBridgeAnnotation)) {
- val dmsg = sym.deprecationMessage map (": " + _) getOrElse ""
-
- unit.deprecationWarning(pos, sym.fullLocationString + " is deprecated" + dmsg)
+ unit.deprecationWarning(pos, "%s%s is deprecated%s".format(
+ sym, sym.locationString, sym.deprecationMessage map (": " + _) getOrElse "")
+ )
}
}
+
/** Similar to deprecation: check if the symbol is marked with @migration
* indicating it has changed semantics between versions.
*/
@@ -1230,7 +1231,7 @@ abstract class RefChecks extends InfoTransform {
private def transformCaseApply(tree: Tree, ifNot: => Unit) = {
val sym = tree.symbol
- if (sym.isSourceMethod && sym.hasFlag(CASE) && sym.name == nme.apply)
+ if (sym.isSourceMethod && sym.isCase && sym.name == nme.apply)
toConstructor(tree.pos, tree.tpe)
else {
ifNot
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 114fa7ed1b..d2218cd977 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -80,6 +80,7 @@ trait Typers extends Modes {
abstract class Typer(context0: Context) extends TyperDiagnostics {
import context0.unit
+ import typeDebug.{ ptTree, ptBlock, ptLine }
val infer = new Inferencer(context0) {
override def isCoercible(tp: Type, pt: Type): Boolean = undoLog undo { // #3281
@@ -728,7 +729,7 @@ trait Typers extends Modes {
val tree1 = if (tree.isType) tree
else TypeApply(tree, tparams1 map (tparam =>
TypeTree(tparam.tpeHK) setPos tree.pos.focus)) setPos tree.pos //@M/tcpolyinfer: changed tparam.tpe to tparam.tpeHK
- context.undetparams = context.undetparams ::: tparams1
+ context.undetparams ++= tparams1
adapt(tree1 setType restpe.substSym(tparams, tparams1), mode, pt, original)
case mt: MethodType if mt.isImplicit && ((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) => // (4.1)
if (context.undetparams nonEmpty) { // (9) -- should revisit dropped condition `(mode & POLYmode) == 0`
@@ -1315,7 +1316,6 @@ trait Typers extends Modes {
treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType
}
-
/** In order to override this in the TreeCheckers Typer so synthetics aren't re-added
* all the time, it is exposed here the module/class typing methods go through it.
*/
@@ -2973,16 +2973,17 @@ trait Typers extends Modes {
errorTree(tree, treeSymTypeMsg(fun)+" does not take type parameters.")
}
- @inline final def deindentTyping() = if (printTypings) context.typingIndent = context.typingIndent.substring(0, context.typingIndent.length() - 2)
- @inline final def indentTyping() = if (printTypings) context.typingIndent += " "
- @inline final def printTyping(s: => String) = if (printTypings) println(context.typingIndent+s)
+ @inline final def deindentTyping() = context.typingIndentLevel -= 2
+ @inline final def indentTyping() = context.typingIndentLevel += 2
+ @inline final def printTyping(s: => String) = {
+ if (printTypings)
+ println(context.typingIndent + s.replaceAll("\n", "\n" + context.typingIndent))
+ }
+ @inline final def printInference(s: => String) = {
+ if (printInfers)
+ println(s)
+ }
- /**
- * @param tree ...
- * @param mode ...
- * @param pt ...
- * @return ...
- */
protected def typed1(tree: Tree, mode: Int, pt: Type): Tree = {
def isPatternMode = inPatternMode(mode)
@@ -3295,9 +3296,13 @@ trait Typers extends Modes {
case _ => Nil
})
def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == ex.pos)
-
- if (fun :: tree :: args exists errorInResult) {
- printTyping("second try for: "+fun+" and "+args)
+ val retry = fun :: tree :: args exists errorInResult
+ printTyping({
+ val funStr = ptTree(fun) + " and " + (args map ptTree mkString ", ")
+ if (retry) "second try: " + funStr
+ else "no second try: " + funStr + " because error not in result: " + ex.pos+"!="+tree.pos
+ })
+ if (retry) {
val Select(qual, name) = fun
val args1 = tryTypedArgs(args, forArgMode(fun, mode), ex)
val qual1 =
@@ -3307,8 +3312,7 @@ trait Typers extends Modes {
val tree1 = Apply(Select(qual1, name) setPos fun.pos, args1) setPos tree.pos
return typed1(tree1, mode | SNDTRYmode, pt)
}
- } else printTyping("no second try for "+fun+" and "+args+" because error not in result:"+ex.pos+"!="+tree.pos)
-
+ }
reportTypeError(tree.pos, ex)
setError(tree)
}
@@ -4186,7 +4190,9 @@ trait Typers extends Modes {
* @param pt ...
* @return ...
*/
- def typed(tree: Tree, mode: Int, pt: Type): Tree = { indentTyping()
+ def typed(tree: Tree, mode: Int, pt: Type): Tree = {
+ indentTyping()
+
def dropExistential(tp: Type): Type = tp match {
case ExistentialType(tparams, tpe) =>
if (settings.debug.value)
@@ -4199,6 +4205,7 @@ trait Typers extends Modes {
case _ => tp
}
+ var alreadyTyped = false
try {
if (Statistics.enabled) {
val t = currentTime()
@@ -4213,15 +4220,34 @@ trait Typers extends Modes {
tree.tpe = null
if (tree.hasSymbol) tree.symbol = NoSymbol
}
- printTyping("typing "+tree+", pt = "+pt+", undetparams = "+context.undetparams+", implicits-enabled = "+context.implicitsEnabled+", silent = "+context.reportGeneralErrors+", context.owner = "+context.owner) //DEBUG
- var tree1 = if (tree.tpe ne null) tree else typed1(tree, mode, dropExistential(pt))
- printTyping("typed "+tree1+":"+tree1.tpe+(if (isSingleType(tree1.tpe)) " with underlying "+tree1.tpe.widen else "")+", undetparams = "+context.undetparams+", pt = "+pt) //DEBUG
+ alreadyTyped = tree.tpe ne null
+ var tree1: Tree = if (alreadyTyped) tree else {
+ printTyping(
+ ptLine("typing %s: pt = %s".format(ptTree(tree), pt),
+ "undetparams" -> context.undetparams,
+ "implicitsEnabled" -> context.implicitsEnabled,
+ "silent" -> !context.reportGeneralErrors,
+ "context.owner" -> context.owner
+ )
+ )
+ val tree1 = typed1(tree, mode, dropExistential(pt))
+ printTyping("typed %s: %s%s".format(
+ ptTree(tree1), tree1.tpe,
+ if (isSingleType(tree1.tpe)) " with underlying "+tree1.tpe.widen else "")
+ )
+ tree1
+ }
tree1.tpe = addAnnotations(tree1, tree1.tpe)
-
val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, tree)
- printTyping("adapted "+tree1+":"+tree1.tpe.widen+" to "+pt+", "+context.undetparams) //DEBUG
+
+ if (!alreadyTyped) {
+ printTyping("adapted %s: %s to %s, %s".format(
+ tree1, tree1.tpe.widen, pt, context.undetparamsString)
+ ) //DEBUG
+ }
+
// for (t <- tree1.tpe) assert(t != WildcardType)
// if ((mode & TYPEmode) != 0) println("type: "+tree1+" has type "+tree1.tpe)
if (phase.id <= currentRun.typerPhase.id) signalDone(context.asInstanceOf[analyzer.Context], tree, result)
@@ -4229,7 +4255,7 @@ trait Typers extends Modes {
} catch {
case ex: TypeError =>
tree.tpe = null
- printTyping("caught "+ex+" in typed: "+tree) //DEBUG
+ printTyping("caught %s: while typing %s".format(ex, tree)) //DEBUG
reportTypeError(tree.pos, ex)
setError(tree)
case ex: Exception =>
@@ -4242,6 +4268,7 @@ trait Typers extends Modes {
}
finally {
deindentTyping()
+
if (Statistics.enabled) {
val t = currentTime()
microsByType(pendingTreeTypes.head) += ((t - typerTime) / 1000).toInt
diff --git a/test/files/neg/bug278.check b/test/files/neg/bug278.check
index ad0a97371e..b1041b7895 100644
--- a/test/files/neg/bug278.check
+++ b/test/files/neg/bug278.check
@@ -1,5 +1,5 @@
bug278.scala:5: error: overloaded method value a with alternatives:
- => (C.this.A) => Unit <and>
+ => C.this.A => Unit <and>
=> () => Unit
does not take type parameters
a[A]
diff --git a/test/files/neg/bug588.check b/test/files/neg/bug588.check
index 5f55c95619..8c01ac5b09 100644
--- a/test/files/neg/bug588.check
+++ b/test/files/neg/bug588.check
@@ -1,6 +1,6 @@
bug588.scala:3: error: double definition:
-method visit:(f: (Int) => String)Boolean and
-method visit:(f: (Int) => Unit)Boolean at line 2
+method visit:(f: Int => String)Boolean and
+method visit:(f: Int => Unit)Boolean at line 2
have same type after erasure: (f: Function1)Boolean
def visit(f: Int => String): Boolean
^
diff --git a/test/files/neg/bug752.check b/test/files/neg/bug752.check
index dddab530e4..9262f38f01 100644
--- a/test/files/neg/bug752.check
+++ b/test/files/neg/bug752.check
@@ -1,6 +1,6 @@
bug752.scala:6: error: type mismatch;
- found : (String) => Unit
- required: (Int) => Unit
+ found : String => Unit
+ required: Int => Unit
f(g _)
^
one error found
diff --git a/test/files/neg/divergent-implicit.check b/test/files/neg/divergent-implicit.check
index 07af8510d9..5f20df1b91 100644
--- a/test/files/neg/divergent-implicit.check
+++ b/test/files/neg/divergent-implicit.check
@@ -3,15 +3,15 @@ divergent-implicit.scala:4: error: type mismatch;
required: String
val x1: String = 1
^
-divergent-implicit.scala:5: error: diverging implicit expansion for type (Int) => String
+divergent-implicit.scala:5: error: diverging implicit expansion for type Int => String
starting with method cast in object Test1
val x2: String = cast[Int, String](1)
^
-divergent-implicit.scala:14: error: diverging implicit expansion for type (Test2.Baz) => Test2.Bar
+divergent-implicit.scala:14: error: diverging implicit expansion for type Test2.Baz => Test2.Bar
starting with method baz2bar in object Test2
val x: Bar = new Foo
^
-divergent-implicit.scala:15: error: diverging implicit expansion for type (Test2.Foo) => Test2.Bar
+divergent-implicit.scala:15: error: diverging implicit expansion for type Test2.Foo => Test2.Bar
starting with method foo2bar in object Test2
val y: Bar = new Baz
^
diff --git a/test/files/neg/t0003.check b/test/files/neg/t0003.check
index fb5bb5671d..1913dde9dd 100644
--- a/test/files/neg/t0003.check
+++ b/test/files/neg/t0003.check
@@ -1,6 +1,6 @@
t0003.scala:2: error: type mismatch;
- found : (A) => (B) => B
- required: (A) => B
+ found : A => B => B
+ required: A => B
def foo[A, B, C](l: List[A], f: A => B=>B, g: B=>B=>C): List[C] = l map (g compose f)
^
one error found
diff --git a/test/files/neg/t0015.check b/test/files/neg/t0015.check
index eb25fc46c8..43adc22f72 100644
--- a/test/files/neg/t0015.check
+++ b/test/files/neg/t0015.check
@@ -1,6 +1,6 @@
t0015.scala:5: error: type mismatch;
found : () => Nothing
- required: (Nothing) => ?
+ required: Nothing => ?
Nil.map(f _)
^
one error found
diff --git a/test/files/neg/unit2anyref.check b/test/files/neg/unit2anyref.check
index 2616fd35f9..10fe1861f5 100644
--- a/test/files/neg/unit2anyref.check
+++ b/test/files/neg/unit2anyref.check
@@ -1,8 +1,8 @@
unit2anyref.scala:2: error: type mismatch;
found : Unit
required: AnyRef
-Note: primitive types are not implicitly converted to AnyRef.
-You can safely force boxing by casting x.asInstanceOf[AnyRef].
+Note: Unit is not implicitly converted to AnyRef. You can safely
+pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so.
val x: AnyRef = () // this should not succeed.
^
one error found
diff --git a/test/files/neg/variances.check b/test/files/neg/variances.check
index d395e45e4e..4eaab56cef 100644
--- a/test/files/neg/variances.check
+++ b/test/files/neg/variances.check
@@ -7,7 +7,7 @@ variances.scala:14: error: covariant type A occurs in contravariant position in
variances.scala:16: error: covariant type A occurs in invariant position in supertype test.C[A] with ScalaObject of object Baz
object Baz extends C[A]
^
-variances.scala:63: error: covariant type A occurs in contravariant position in type => test.Covariant.T[A]{val m: (A) => A} of value x
+variances.scala:63: error: covariant type A occurs in contravariant position in type => test.Covariant.T[A]{val m: A => A} of value x
val x: T[A] {
^
variances.scala:79: error: covariant type T occurs in contravariant position in type => test.TestAlias.B[C.this.A] of method foo