diff options
Diffstat (limited to 'src')
11 files changed, 491 insertions, 303 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 |