summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2013-06-15 14:28:23 -0400
committerSimon Ochsenreither <simon@ochsenreither.de>2013-07-17 23:38:28 +0200
commit4d6be05c28c95dcd26922059d773a8bfed6014ef (patch)
tree4cbb711d44ad2b073dd21d03811fdf3d7198b4d0 /src
parentaeb733147881d8da68d1e520b14112dc826a3977 (diff)
downloadscala-4d6be05c28c95dcd26922059d773a8bfed6014ef.tar.gz
scala-4d6be05c28c95dcd26922059d773a8bfed6014ef.tar.bz2
scala-4d6be05c28c95dcd26922059d773a8bfed6014ef.zip
Make -Ytyper-debug output readable.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala7
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala10
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala127
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala53
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala260
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala180
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala14
-rw-r--r--src/reflect/scala/reflect/internal/TypeDebugging.scala123
9 files changed, 481 insertions, 299 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index ea6543bb71..20cb1dab5b 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -70,6 +70,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
override def settings = currentSettings
+ /** Switch to turn on detailed type logs */
+ var printTypings = settings.Ytyperdebug.value
+
def this(reporter: Reporter) =
this(new Settings(err => reporter.error(null, err)), reporter)
@@ -417,10 +420,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
}
}
- /** Switch to turn on detailed type logs */
- val printTypings = settings.Ytyperdebug.value
- val printInfers = settings.Yinferdebug.value
-
// phaseName = "parser"
lazy val syntaxAnalyzer = new {
val global: Global.this.type = Global.this
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index 1f8f13ae02..60641d6752 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -267,10 +267,6 @@ trait Contexts { self: Analyzer =>
/** Saved type bounds for type parameters which are narrowed in a GADT. */
var savedTypeBounds: List[(Symbol, Type)] = List()
- /** Indentation level, in columns, for output under -Ytyper-debug */
- var typingIndentLevel: Int = 0
- def typingIndent = " " * typingIndentLevel
-
/** The next enclosing context (potentially `this`) that is owned by a class or method */
def enclClassOrMethod: Context =
if ((owner eq NoSymbol) || (owner.isClass) || (owner.isMethod)) this
@@ -282,6 +278,11 @@ trait Contexts { self: Analyzer =>
/** ...or an Apply. */
def enclosingApply = nextEnclosing(_.tree.isInstanceOf[Apply])
+ def siteString = {
+ def what_s = if (owner.isConstructor) "" else owner.kindString
+ def where_s = if (owner.isClass) "" else "in " + enclClass.owner.decodedName
+ List(what_s, owner.decodedName, where_s) filterNot (_ == "") mkString " "
+ }
//
// Tracking undetermined type parameters for type argument inference.
//
@@ -445,7 +446,6 @@ trait Contexts { self: Analyzer =>
// Fields that are directly propagated
c.variance = variance
c.diagnostic = diagnostic
- c.typingIndentLevel = typingIndentLevel
c.openImplicits = openImplicits
c.contextMode = contextMode // note: ConstructorSuffix, a bit within `mode`, is conditionally overwritten below.
c._reportBuffer = reportBuffer
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 8e79b56814..100112fec1 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -31,8 +31,8 @@ trait Implicits {
import global._
import definitions._
import ImplicitsStats._
- import typeDebug.{ ptBlock, ptLine }
- import global.typer.{ printTyping, deindentTyping, indentTyping, printInference }
+ import typingStack.{ printTyping }
+ import typeDebug._
def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context): SearchResult =
inferImplicit(tree, pt, reportAmbiguous, isView, context, saveAmbiguousDivergent = true, tree.pos)
@@ -60,25 +60,13 @@ trait Implicits {
* @return A search result
*/
def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean, pos: Position): SearchResult = {
- printInference("[infer %s] %s with pt=%s in %s".format(
- if (isView) "view" else "implicit",
- tree, pt, context.owner.enclClass)
- )
- printTyping(
- ptBlock("infer implicit" + (if (isView) " view" else ""),
- "tree" -> tree,
- "pt" -> pt,
- "undetparams" -> context.outer.undetparams
- )
- )
- indentTyping()
-
+ val shouldPrint = printTypings && !context.undetparams.isEmpty
val rawTypeStart = if (Statistics.canEnable) Statistics.startCounter(rawTypeImpl) else null
val findMemberStart = if (Statistics.canEnable) Statistics.startCounter(findMemberImpl) else null
val subtypeStart = if (Statistics.canEnable) Statistics.startCounter(subtypeImpl) else null
val start = if (Statistics.canEnable) Statistics.startTimer(implicitNanos) else null
- if (printInfers && !tree.isEmpty && !context.undetparams.isEmpty)
- printTyping("typing implicit: %s %s".format(tree, context.undetparamsString))
+ if (shouldPrint)
+ typingStack.printTyping(tree, "typing implicit: %s %s".format(tree, context.undetparamsString))
val implicitSearchContext = context.makeImplicit(reportAmbiguous)
val result = new ImplicitSearch(tree, pt, isView, implicitSearchContext, pos).bestImplicit
if (result.isFailure && saveAmbiguousDivergent && implicitSearchContext.hasErrors) {
@@ -88,15 +76,13 @@ trait Implicits {
})
debuglog("update buffer: " + implicitSearchContext.reportBuffer.errors)
}
- printInference("[infer implicit] inferred " + result)
context.undetparams = context.undetparams filterNot result.subst.from.contains
if (Statistics.canEnable) Statistics.stopTimer(implicitNanos, start)
if (Statistics.canEnable) Statistics.stopCounter(rawTypeImpl, rawTypeStart)
if (Statistics.canEnable) Statistics.stopCounter(findMemberImpl, findMemberStart)
if (Statistics.canEnable) Statistics.stopCounter(subtypeImpl, subtypeStart)
- deindentTyping()
- printTyping("Implicit search yielded: "+ result)
+
result
}
@@ -143,6 +129,7 @@ trait Implicits {
private val implicitsCache = new LinkedHashMap[Type, Infoss]
private val infoMapCache = new LinkedHashMap[Symbol, InfoMap]
private val improvesCache = perRunCaches.newMap[(ImplicitInfo, ImplicitInfo), Boolean]()
+ private val implicitSearchId = { var id = 1 ; () => try id finally id += 1 }
private def isInvalidConversionTarget(tpe: Type): Boolean = tpe match {
case Function1(_, out) => AnyRefClass.tpe <:< out
@@ -325,18 +312,23 @@ trait Implicits {
* (useful when we infer synthetic stuff and pass EmptyTree in the `tree` argument)
* If it's set to NoPosition, then position-based services will use `tree.pos`
*/
- class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context, pos0: Position = NoPosition)
- extends Typer(context0) with ImplicitsContextErrors {
- printTyping(
- ptBlock("new ImplicitSearch",
- "tree" -> tree,
- "pt" -> pt,
- "isView" -> isView,
- "context0" -> context0,
- "undetparams" -> context.outer.undetparams
- )
- )
-// assert(tree.isEmpty || tree.pos.isDefined, tree)
+ class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context, pos0: Position = NoPosition) extends Typer(context0) with ImplicitsContextErrors {
+ val searchId = implicitSearchId()
+ private def typingLog(what: String, msg: String) =
+ typingStack.printTyping(tree, f"[search #$searchId] $what $msg")
+
+ import infer._
+ if (Statistics.canEnable) Statistics.incCounter(implicitSearchCount)
+
+ /** The type parameters to instantiate */
+ val undetParams = if (isView) Nil else context.outer.undetparams
+ val wildPt = approximate(pt)
+
+ def undet_s = if (undetParams.isEmpty) "" else undetParams.mkString(" inferring ", ", ", "")
+ def tree_s = typeDebug ptTree tree
+ def ctx_s = fullSiteString(context)
+ typingLog("start", s"`$tree_s`$undet_s, searching for adaptation to pt=$pt $ctx_s")
+
def pos = if (pos0 != NoPosition) pos0 else tree.pos
def failure(what: Any, reason: String, pos: Position = this.pos): SearchResult = {
@@ -344,8 +336,6 @@ trait Implicits {
reporter.echo(pos, what+" is not a valid implicit value for "+pt+" because:\n"+reason)
SearchFailure
}
-
- import infer._
/** Is implicit info `info1` better than implicit info `info2`?
*/
def improves(info1: ImplicitInfo, info2: ImplicitInfo) = {
@@ -418,14 +408,8 @@ trait Implicits {
overlaps(dtor1, dted1) && (dtor1 =:= dted1 || complexity(dtor1) > complexity(dted1))
}
- if (Statistics.canEnable) Statistics.incCounter(implicitSearchCount)
-
- /** The type parameters to instantiate */
- val undetParams = if (isView) List() else context.outer.undetparams
-
/** The expected type with all undetermined type parameters replaced with wildcards. */
def approximate(tp: Type) = deriveTypeWithWildcards(undetParams)(tp)
- val wildPt = approximate(pt)
/** Try to construct a typed tree from given implicit info with given
* expected type.
@@ -582,22 +566,12 @@ trait Implicits {
private def typedImplicit0(info: ImplicitInfo, ptChecked: Boolean, isLocal: Boolean): SearchResult = {
if (Statistics.canEnable) Statistics.incCounter(plausiblyCompatibleImplicits)
- printTyping (
- ptBlock("typedImplicit0",
- "info.name" -> info.name,
- "ptChecked" -> ptChecked,
- "pt" -> wildPt,
- "orig" -> ptBlock("info",
- "undetParams" -> undetParams,
- "info.pre" -> info.pre
- ).replaceAll("\\n", "\n ")
- )
- )
-
- if (ptChecked || matchesPt(info))
- typedImplicit1(info, isLocal)
- else
- SearchFailure
+ val ok = ptChecked || matchesPt(info) && {
+ def word = if (isLocal) "local " else ""
+ typingLog("match", s"$word$info")
+ true
+ }
+ if (ok) typedImplicit1(info, isLocal) else SearchFailure
}
private def typedImplicit1(info: ImplicitInfo, isLocal: Boolean): SearchResult = {
@@ -618,9 +592,7 @@ trait Implicits {
Select(gen.mkAttributedQualifier(info.pre), implicitMemberName)
}
}
- printTyping("typedImplicit1 %s, pt=%s, from implicit %s:%s".format(
- typeDebug.ptTree(itree), wildPt, info.name, info.tpe)
- )
+ typingLog("considering", typeDebug.ptTree(itree))
def fail(reason: String): SearchResult = failure(itree, reason)
def fallback = typed1(itree, EXPRmode, wildPt)
@@ -643,13 +615,10 @@ trait Implicits {
if (Statistics.canEnable) Statistics.incCounter(typedImplicits)
- 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 %s:%s to %s".format(
- itree1.symbol, itree2.tpe, wildPt)
- )
+ typingStack.showAdapt(itree, itree2, pt, context)
def hasMatchingSymbol(tree: Tree): Boolean = (tree.symbol == info.sym) || {
tree match {
@@ -669,15 +638,9 @@ trait Implicits {
val tvars = undetParams map freshVar
def ptInstantiated = pt.instantiateTypeParams(undetParams, tvars)
- printInference("[search] considering %s (pt contains %s) trying %s against pt=%s".format(
- if (undetParams.isEmpty) "no tparams" else undetParams.map(_.name).mkString(", "),
- typeVarsInType(ptInstantiated) filterNot (_.isGround) match { case Nil => "no tvars" ; case tvs => tvs.mkString(", ") },
- itree2.tpe, pt
- ))
-
if (matchesPt(itree2.tpe, ptInstantiated, undetParams)) {
if (tvars.nonEmpty)
- printTyping(ptLine("" + info.sym, "tvars" -> tvars, "tvars.constr" -> tvars.map(_.constr)))
+ typingLog("solve", ptLine("tvars" -> tvars, "tvars.constr" -> tvars.map(_.constr)))
val targs = solvedTypes(tvars, undetParams, undetParams map varianceInType(pt),
upper = false, lubDepth(List(itree2.tpe, pt)))
@@ -729,7 +692,7 @@ trait Implicits {
case None =>
val result = new SearchResult(itree2, subst)
if (Statistics.canEnable) Statistics.incCounter(foundImplicits)
- printInference("[success] found %s for pt %s".format(result, ptInstantiated))
+ typingLog("success", s"inferred value of type $ptInstantiated is $result")
result
}
}
@@ -868,10 +831,7 @@ trait Implicits {
matches sortBy (x => if (isView) -x.useCountView else -x.useCountArg)
}
if (eligible.nonEmpty)
- printInference("[search%s] %s with pt=%s in %s, eligible:\n %s".format(
- if (isView) " view" else "",
- tree, pt, context.owner.enclClass, eligible.mkString("\n "))
- )
+ printTyping(tree, eligible.size + s" eligible for pt=$pt at ${fullSiteString(context)}")
/** Faster implicit search. Overall idea:
* - prune aggressively
@@ -898,10 +858,7 @@ trait Implicits {
try improves(i, alt)
catch {
case e: CyclicReference =>
- if (printInfers) {
- println(i+" discarded because cyclic reference occurred")
- e.printStackTrace()
- }
+ debugwarn(s"Discarding $i during implicit search due to cyclic reference")
true
}
})
@@ -1044,9 +1001,7 @@ trait Implicits {
tp match {
case TypeRef(pre, sym, args) =>
if (sym.isClass) {
- if (!((sym.name == tpnme.REFINE_CLASS_NAME) ||
- (sym.name startsWith tpnme.ANON_CLASS_NAME) ||
- (sym.name == tpnme.ROOT))) {
+ if (!sym.isAnonOrRefinementClass && !sym.isRoot) {
if (sym.isStatic && !(pending contains sym))
infoMap ++= {
infoMapCache get sym match {
@@ -1060,7 +1015,7 @@ trait Implicits {
}
else
getClassParts(tp)
- args foreach (getParts(_))
+ args foreach getParts
}
} else if (sym.isAliasType) {
getParts(tp.normalize) // SI-7180 Normalize needed to expand HK type refs
@@ -1088,9 +1043,9 @@ trait Implicits {
val infoMap = new InfoMap
getParts(tp)(infoMap, new mutable.HashSet(), Set())
- printInference(
- ptBlock("companionImplicitMap " + tp, infoMap.toSeq.map({ case (k, v) => ("" + k, v.mkString(", ")) }): _*)
- )
+ if (infoMap.nonEmpty)
+ printTyping(tree, infoMap.size + " implicits in companion scope")
+
infoMap
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 961ef484d8..06892053fa 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -20,8 +20,8 @@ trait Infer extends Checkable {
import global._
import definitions._
- import typer.printInference
import typeDebug.ptBlock
+ import typingStack.{ printTyping }
/** The formal parameter types corresponding to `formals`.
* If `formals` has a repeated last parameter, a list of
@@ -216,8 +216,10 @@ trait Infer extends Checkable {
def solvedTypes(tvars: List[TypeVar], tparams: List[Symbol],
variances: List[Variance], upper: Boolean, depth: Int): List[Type] = {
- if (tvars.nonEmpty)
- printInference("[solve types] solving for " + tparams.map(_.name).mkString(", ") + " in " + tvars.mkString(", "))
+ if (tvars.nonEmpty) {
+ def tp_s = (tparams, tvars).zipped map { case (tp, tv) => s"${tp.name}/$tv" } mkString ","
+ printTyping(s"solving for $tp_s")
+ }
if (!solve(tvars, tparams, variances, upper, depth)) {
// no panic, it's good enough to just guess a solution, we'll find out
@@ -987,21 +989,13 @@ trait Infer extends Checkable {
* attempts fail, an error is produced.
*/
def inferArgumentInstance(tree: Tree, undetparams: List[Symbol], strictPt: Type, lenientPt: Type) {
- printInference(
- ptBlock("inferArgumentInstance",
- "tree" -> tree,
- "tree.tpe" -> tree.tpe,
- "undetparams" -> undetparams,
- "strictPt" -> strictPt,
- "lenientPt" -> lenientPt
- )
- )
+ printTyping(tree, s"inferring arg instance based on pt0=$strictPt, pt1=$lenientPt")
var targs = exprTypeArgs(undetparams, tree.tpe, strictPt, useWeaklyCompatible = false)
if ((targs eq null) || !(tree.tpe.subst(undetparams, targs) <:< strictPt))
targs = exprTypeArgs(undetparams, tree.tpe, lenientPt, useWeaklyCompatible = false)
substExpr(tree, undetparams, targs, lenientPt)
- printInference("[inferArgumentInstance] finished, targs = " + targs)
+ printTyping(tree, s"infer arg instance from pt0=$strictPt, pt1=$lenientPt; targs=$targs")
}
/** Infer type arguments `targs` for `tparams` of polymorphic expression in `tree`, given prototype `pt`.
@@ -1013,29 +1007,20 @@ trait Infer extends Checkable {
val treeTp = if (treeTp0 eq null) tree.tpe else treeTp0 // can't refer to tree in default for treeTp0
val tvars = tparams map freshVar
val targs = exprTypeArgs(tvars, tparams, treeTp, pt, useWeaklyCompatible)
- printInference(
- ptBlock("inferExprInstance",
- "tree" -> tree,
- "tree.tpe"-> tree.tpe,
- "tparams" -> tparams,
- "pt" -> pt,
- "targs" -> targs,
- "tvars" -> tvars
- )
- )
+ def infer_s = map3(tparams, tvars, targs)((tparam, tvar, targ) => s"$tparam=$tvar/$targ") mkString ","
+ printTyping(tree, s"infer expr instance from pt=$pt, $infer_s")
if (keepNothings || (targs eq null)) { //@M: adjustTypeArgs fails if targs==null, neg/t0226
substExpr(tree, tparams, targs, pt)
List()
} else {
val AdjustedTypeArgs.Undets(okParams, okArgs, leftUndet) = adjustTypeArgs(tparams, tvars, targs)
- printInference(
- ptBlock("inferExprInstance/AdjustedTypeArgs",
- "okParams" -> okParams,
- "okArgs" -> okArgs,
- "leftUndet" -> leftUndet
- )
- )
+ def solved_s = map2(okParams, okArgs)((p, a) => s"$p=$a") mkString ","
+ def undet_s = leftUndet match {
+ case Nil => ""
+ case ps => ps.mkString(", undet=", ",", "")
+ }
+ printTyping(tree, s"infer solved $solved_s$undet_s")
substExpr(tree, okParams, okArgs, pt)
leftUndet
}
@@ -1077,14 +1062,6 @@ trait Infer extends Checkable {
val AdjustedTypeArgs.AllArgsAndUndets(okparams, okargs, allargs, leftUndet) =
methTypeArgs(undetparams, formals, restpe, argtpes, pt)
- printInference("[infer method] solving for %s in %s based on (%s)%s (%s)".format(
- undetparams.map(_.name).mkString(", "),
- fn.tpe,
- argtpes.mkString(", "),
- restpe,
- (okparams map (_.name), okargs).zipped.map(_ + "=" + _).mkString("solved: ", ", ", "")
- ))
-
if (checkBounds(fn, NoPrefix, NoSymbol, undetparams, allargs, "inferred ")) {
val treeSubst = new TreeTypeSubstituter(okparams, okargs)
treeSubst traverseTrees fn :: args
diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
index a93baabc51..b4a37f9943 100644
--- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
@@ -167,10 +167,10 @@ trait TypeDiagnostics {
def explainAlias(tp: Type) = {
// Don't automatically normalize standard aliases; they still will be
// expanded if necessary to disambiguate simple identifiers.
- if ((tp eq tp.normalize) || tp.typeSymbolDirect.isInDefaultNamespace) ""
- else {
+ val deepDealias = DealiasedType(tp)
+ if (tp eq deepDealias) "" else {
// A sanity check against expansion being identical to original.
- val s = "" + DealiasedType(tp)
+ val s = "" + deepDealias
if (s == "" + tp) ""
else "\n (which expands to) " + s
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 1a9a30c2ad..f9e34106ec 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -26,7 +26,7 @@ import Mode._
* @author Martin Odersky
* @version 1.0
*/
-trait Typers extends Adaptations with Tags {
+trait Typers extends Adaptations with Tags with TypersTracking {
self: Analyzer =>
import global._
@@ -90,12 +90,6 @@ trait Typers extends Adaptations with Tags {
private final val InterpolatorCodeRegex = """\$\{.*?\}""".r
private final val InterpolatorIdentRegex = """\$\w+""".r
- // To enable decent error messages when the typer crashes.
- // TODO - this only catches trees which go through def typed,
- // but there are all kinds of back ways - typedClassDef, etc. etc.
- // Funnel everything through one doorway.
- var lastTreeToTyper: Tree = EmptyTree
-
// when true:
// - we may virtualize matches (if -Xexperimental and there's a suitable __match in scope)
// - we synthesize PartialFunction implementations for `x => x match {...}` and `match {...}` when the expected type is PartialFunction
@@ -104,7 +98,7 @@ trait Typers extends Adaptations with Tags {
abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with TyperContextErrors {
import context0.unit
- import typeDebug.{ ptTree, ptBlock, ptLine }
+ import typeDebug.{ ptTree, ptBlock, ptLine, inGreen, inRed }
import TyperErrorGen._
val infer = new Inferencer(context0) {
@@ -1703,14 +1697,14 @@ trait Typers extends Adaptations with Tags {
* So we strip the duplicates before typer.
*/
private def fixDuplicateSyntheticParents(parents: List[Tree]): List[Tree] = parents match {
- case Nil => Nil
- case x :: xs =>
- val sym = x.symbol
+ case Nil => Nil
+ case x :: xs =>
+ val sym = x.symbol
x :: fixDuplicateSyntheticParents(
- if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym)
- else xs
- )
- }
+ if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym)
+ else xs
+ )
+ }
def typedParentTypes(templ: Template): List[Tree] = templ.parents match {
case Nil => List(atPos(templ.pos)(TypeTree(AnyRefTpe)))
@@ -1730,17 +1724,18 @@ trait Typers extends Adaptations with Tags {
typedPrimaryConstrBody(templ)(EmptyTree)
supertpts mapConserve (tpt => checkNoEscaping.privates(context.owner, tpt))
- } catch {
- case ex: TypeError =>
- // fallback in case of cyclic errors
- // @H none of the tests enter here but I couldn't rule it out
+ }
+ catch {
+ case ex: TypeError =>
+ // fallback in case of cyclic errors
+ // @H none of the tests enter here but I couldn't rule it out
// upd. @E when a definition inherits itself, we end up here
// because `typedParentType` triggers `initialize` for parent types symbols
- log("Type error calculating parents in template " + templ)
- log("Error: " + ex)
- ParentTypesError(templ, ex)
- List(TypeTree(AnyRefTpe))
- }
+ log("Type error calculating parents in template " + templ)
+ log("Error: " + ex)
+ ParentTypesError(templ, ex)
+ List(TypeTree(AnyRefTpe))
+ }
}
/** <p>Check that</p>
@@ -3510,7 +3505,7 @@ trait Typers extends Adaptations with Tags {
else None
case _ => None
- }
+ }
}
/**
@@ -3698,8 +3693,7 @@ trait Typers extends Adaptations with Tags {
val Function(arg :: Nil, rhs) = typed(func, mode, funcType)
rhs.substituteSymbols(arg.symbol :: Nil, selfsym :: Nil)
- }
-
+ }
def annInfo(t: Tree): AnnotationInfo = t match {
case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) =>
AnnotationInfo(annType, args, List()).setOriginal(typedAnn).setPos(t.pos)
@@ -3725,21 +3719,22 @@ trait Typers extends Adaptations with Tags {
if ((typedAnn.tpe == null) || typedAnn.tpe.isErroneous) ErroneousAnnotation
else annInfo(typedAnn)
- })
}
+ )
+ }
/** Compute an existential type from raw hidden symbols `syms` and type `tp`
*/
def packSymbols(hidden: List[Symbol], tp: Type): Type = global.packSymbols(hidden, tp, context0.owner)
def isReferencedFrom(ctx: Context, sym: Symbol): Boolean = (
- ctx.owner.isTerm &&
- (ctx.scope.exists { dcl => dcl.isInitialized && (dcl.info contains sym) }) ||
- {
- var ctx1 = ctx.outer
- while ((ctx1 != NoContext) && (ctx1.scope eq ctx.scope)) ctx1 = ctx1.outer
- (ctx1 != NoContext) && isReferencedFrom(ctx1, sym)
- }
+ ctx.owner.isTerm && (ctx.scope.exists { dcl => dcl.isInitialized && (dcl.info contains sym) }) || {
+ var ctx1 = ctx.outer
+ while ((ctx1 != NoContext) && (ctx1.scope eq ctx.scope))
+ ctx1 = ctx1.outer
+
+ (ctx1 != NoContext) && isReferencedFrom(ctx1, sym)
+ }
)
def isCapturedExistential(sym: Symbol) = (
@@ -4023,20 +4018,7 @@ trait Typers extends Adaptations with Tags {
}
}
}
-
- def wrapErrors(tree: Tree, typeTree: Typer => Tree): Tree =
- silent(typeTree) orElse (err => DynamicRewriteError(tree, err))
- }
-
- final def deindentTyping() = context.typingIndentLevel -= 2
- 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)
+ def wrapErrors(tree: Tree, typeTree: Typer => Tree): Tree = silent(typeTree) orElse (err => DynamicRewriteError(tree, err))
}
def typed1(tree: Tree, mode: Mode, pt: Type): Tree = {
@@ -4054,7 +4036,7 @@ trait Typers extends Adaptations with Tags {
else lookupInOwner(qual.tpe.typeSymbol, name) orElse {
NotAMemberError(tree, qual, name)
NoSymbol
- }
+ }
)
def typedAnnotated(atd: Annotated): Tree = {
@@ -4422,56 +4404,55 @@ trait Typers extends Adaptations with Tags {
val start = if (Statistics.canEnable) Statistics.startTimer(failedApplyNanos) else null
def onError(typeError: AbsTypeError): Tree = {
- if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, start)
-
- // If the problem is with raw types, copnvert to existentials and try again.
- // See #4712 for a case where this situation arises,
- if ((fun.symbol ne null) && fun.symbol.isJavaDefined) {
- val newtpe = rawToExistential(fun.tpe)
- if (fun.tpe ne newtpe) {
- // println("late cooking: "+fun+":"+fun.tpe) // DEBUG
- return tryTypedApply(fun setType newtpe, args)
- }
+ if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, start)
+
+ // If the problem is with raw types, copnvert to existentials and try again.
+ // See #4712 for a case where this situation arises,
+ if ((fun.symbol ne null) && fun.symbol.isJavaDefined) {
+ val newtpe = rawToExistential(fun.tpe)
+ if (fun.tpe ne newtpe) {
+ // println("late cooking: "+fun+":"+fun.tpe) // DEBUG
+ return tryTypedApply(fun setType newtpe, args)
}
+ }
+ def treesInResult(tree: Tree): List[Tree] = tree :: (tree match {
+ case Block(_, r) => treesInResult(r)
+ case Match(_, cases) => cases
+ case CaseDef(_, _, r) => treesInResult(r)
+ case Annotated(_, r) => treesInResult(r)
+ case If(_, t, e) => treesInResult(t) ++ treesInResult(e)
+ case Try(b, catches, _) => treesInResult(b) ++ catches
+ case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r)
+ case _ => Nil
+ })
+ def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == typeError.errPos)
- def treesInResult(tree: Tree): List[Tree] = tree :: (tree match {
- case Block(_, r) => treesInResult(r)
- case Match(_, cases) => cases
- case CaseDef(_, _, r) => treesInResult(r)
- case Annotated(_, r) => treesInResult(r)
- case If(_, t, e) => treesInResult(t) ++ treesInResult(e)
- case Try(b, catches, _) => treesInResult(b) ++ catches
- case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r)
- case _ => Nil
- })
- def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == typeError.errPos)
-
- val retry = (typeError.errPos != null) && (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: " + typeError.errPos+"!="+tree.pos
- }
- if (retry) {
- val Select(qual, name) = fun
- tryTypedArgs(args, forArgMode(fun, mode)) match {
- case Some(args1) =>
- val qual1 =
- if (!pt.isError) adaptToArguments(qual, name, args1, pt, reportAmbiguous = true, saveErrors = true)
- else qual
- if (qual1 ne qual) {
- val tree1 = Apply(Select(qual1, name) setPos fun.pos, args1) setPos tree.pos
- return context withinSecondTry typed1(tree1, mode, pt)
- }
- case _ => ()
- }
+ val retry = (typeError.errPos != null) && (fun :: tree :: args exists errorInResult)
+ typingStack.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: " + typeError.errPos+"!="+tree.pos
+ })
+ if (retry) {
+ val Select(qual, name) = fun
+ tryTypedArgs(args, forArgMode(fun, mode)) match {
+ case Some(args1) =>
+ val qual1 =
+ if (!pt.isError) adaptToArguments(qual, name, args1, pt, reportAmbiguous = true, saveErrors = true)
+ else qual
+ if (qual1 ne qual) {
+ val tree1 = Apply(Select(qual1, name) setPos fun.pos, args1) setPos tree.pos
+ return context withinSecondTry typed1(tree1, mode, pt)
+ }
+ case _ => ()
}
- issue(typeError)
- setError(treeCopy.Apply(tree, fun, args))
+ }
+ issue(typeError)
+ setError(treeCopy.Apply(tree, fun, args))
}
silent(_.doTypedApply(tree, fun, args, mode, pt)) orElse onError
- }
+ }
def normalTypedApply(tree: Tree, fun: Tree, args: List[Tree]) = {
// TODO: replace `fun.symbol.isStable` by `treeInfo.isStableIdentifierPattern(fun)`
@@ -5319,51 +5300,47 @@ trait Typers extends Adaptations with Tags {
def typed(tree: Tree, mode: Mode, pt: Type): Tree = {
lastTreeToTyper = tree
- indentTyping()
-
- val ptPlugins = pluginsPt(pt, this, tree, mode)
-
+ def body = (
+ if (printTypings && !phase.erasedTypes && !noPrintTyping(tree))
+ typingStack.nextTyped(tree, mode, pt, context)(typedInternal(tree, mode, pt))
+ else
+ typedInternal(tree, mode, pt)
+ )
val startByType = if (Statistics.canEnable) Statistics.pushTimer(byTypeStack, byTypeNanos(tree.getClass)) else null
if (Statistics.canEnable) Statistics.incCounter(visitsByType, tree.getClass)
- try {
- if (context.retyping &&
- (tree.tpe ne null) && (tree.tpe.isErroneous || !(tree.tpe <:< ptPlugins))) {
- tree.clearType()
- if (tree.hasSymbolField) tree.symbol = NoSymbol
- }
+ try body
+ finally if (Statistics.canEnable) Statistics.popTimer(byTypeStack, startByType)
+ }
+ private def typedInternal(tree: Tree, mode: Mode, pt: Type): Tree = {
+ val ptPlugins = pluginsPt(pt, this, tree, mode)
+ def retypingOk = (
+ context.retyping
+ && (tree.tpe ne null)
+ && (tree.tpe.isErroneous || !(tree.tpe <:< ptPlugins))
+ )
+ def runTyper(): Tree = {
+ if (retypingOk) {
+ tree.tpe = null
+ if (tree.hasSymbol) tree.symbol = NoSymbol
+ }
val alreadyTyped = tree.tpe ne null
- val tree1: Tree = if (alreadyTyped) tree else {
- printTyping(
- ptLine("typing %s: pt = %s".format(ptTree(tree), ptPlugins),
- "undetparams" -> context.undetparams,
- "implicitsEnabled" -> context.implicitsEnabled,
- "enrichmentEnabled" -> context.enrichmentEnabled,
- "mode" -> mode,
- "silent" -> context.bufferErrors,
- "context.owner" -> context.owner
- )
- )
- val ptWild = if (mode.inPatternMode)
- ptPlugins // SI-5022 don't widen pt for patterns as types flow from it to the case body.
- else
- dropExistential(ptPlugins) // FIXME: document why this is done.
+ val shouldPrint = !alreadyTyped && !phase.erasedTypes
+ val ptWild = if (mode.inPatternMode)
+ ptPlugins // SI-5022 don't widen pt for patterns as types flow from it to the case body.
+ else
+ dropExistential(ptPlugins) // FIXME: document why this is done.
+ val tree1: Tree = if (alreadyTyped) tree else typed1(tree, mode, ptWild)
+ if (shouldPrint)
+ typingStack.showTyped(tree1)
- typed1(tree, mode, ptWild)
- }
// Can happen during erroneous compilation - error(s) have been
// reported, but we need to avoid causing an NPE with this tree
if (tree1.tpe eq null)
return setError(tree)
- if (!alreadyTyped) {
- printTyping("typed %s: %s%s".format(
- ptTree(tree1), tree1.tpe,
- if (isSingleType(tree1.tpe)) " with underlying "+tree1.tpe.widen else "")
- )
- }
-
tree1 modifyType (pluginsTyped(_, this, tree1, mode, ptPlugins))
+
val result =
if (tree1.isEmpty) tree1
else {
@@ -5371,32 +5348,29 @@ trait Typers extends Adaptations with Tags {
if (hasPendingMacroExpansions) macroExpandAll(this, result) else result
}
- if (!alreadyTyped) {
- printTyping("adapted %s: %s to %s, %s".format(
- tree1, tree1.tpe.widen, ptPlugins, context.undetparamsString)
- ) //DEBUG
- }
- if (!isPastTyper) signalDone(context.asInstanceOf[analyzer.Context], tree, result)
+ if (shouldPrint)
+ typingStack.showAdapt(tree1, result, ptPlugins, context)
+
+ if (!isPastTyper)
+ signalDone(context.asInstanceOf[analyzer.Context], tree, result)
+
result
- } catch {
+ }
+
+ try runTyper() catch {
case ex: TypeError =>
tree.clearType()
// The only problematic case are (recoverable) cyclic reference errors which can pop up almost anywhere.
- printTyping("caught %s: while typing %s".format(ex, tree)) //DEBUG
-
+ typingStack.printTyping(tree, "caught %s: while typing %s".format(ex, tree)) //DEBUG
reportTypeError(context, tree.pos, ex)
setError(tree)
case ex: Exception =>
- if (settings.debug) // @M causes cyclic reference error
- Console.println("exception when typing "+tree+", pt = "+ptPlugins)
+ // @M causes cyclic reference error
+ devWarning(s"exception when typing $tree, pt=$ptPlugins")
if (context != null && context.unit.exists && tree != null)
logError("AT: " + (tree.pos).dbgString, ex)
throw ex
}
- finally {
- deindentTyping()
- if (Statistics.canEnable) Statistics.popTimer(byTypeStack, startByType)
- }
}
def atOwner(owner: Symbol): Typer =
diff --git a/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala b/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala
new file mode 100644
index 0000000000..f44fd412fd
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala
@@ -0,0 +1,180 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2013 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package typechecker
+
+import scala.collection.mutable
+import scala.reflect.internal.util.{ BatchSourceFile, Statistics }
+import mutable.ListBuffer
+import symtab.Flags._
+import Mode._
+
+trait TypersTracking {
+ self: Analyzer =>
+
+ import global._
+ import definitions._
+ import typeDebug._
+
+ // To enable decent error messages when the typer crashes.
+ // TODO - this only catches trees which go through def typed,
+ // but there are all kinds of back ways - typedClassDef, etc. etc.
+ // Funnel everything through one doorway.
+ var lastTreeToTyper: Tree = EmptyTree
+
+ def fullSiteString(context: Context): String = {
+ def owner_long_s = (
+ if (settings.debug.value) {
+ def flags_s = context.owner.debugFlagString match {
+ case "" => ""
+ case s => " with flags " + inLightMagenta(s)
+ }
+ s", a ${context.owner.shortSymbolClass}$flags_s"
+ }
+ else ""
+ )
+ def marker = if (context.bufferErrors) "silent" else "site"
+ def undet_s = context.undetparams match {
+ case Nil => ""
+ case ps => ps.mkString(" solving: ", ",", "")
+ }
+ def implicits_s = (
+ if (context.enrichmentEnabled)
+ if (context.implicitsEnabled) ""
+ else inLightRed("enrichment only")
+ else inLightRed("implicits disabled")
+ )
+
+ s"($marker$undet_s: ${context.siteString}$owner_long_s) $implicits_s"
+ }
+
+ object typingStack {
+ val out = new java.io.PrintWriter(System.err, true)
+ def println(msg: Any) = out println "" + msg
+
+ // TODO - account for colors so the color of a multiline string
+ // doesn't infect the connector lines
+ private def currentIndent = "| " * depth
+
+ private var trees: List[Frame] = Nil
+ private var depth = 0
+ private def atLowerIndent[T](body: => T): T = {
+ depth -= 1
+ try body finally depth += 1
+ }
+ private def resetIfEmpty(s: String) = if (trees.isEmpty) resetColor(s) else s
+
+ private def truncAndOneLine(s: String): String = {
+ val s1 = s.replaceAll("\\s+", " ")
+ if (s1.length < 60 || settings.debug.value) s1 else s1.take(57) + "..."
+ }
+
+ private val nextId = { var x = 1 ; () => try x finally x += 1 }
+ private class Frame(val tree: Tree) {
+ val stamp = System.nanoTime
+ val id = nextId()
+ }
+ private object NoFrame extends Frame(EmptyTree) { }
+ private def greenType(tp: Type): String = tpe_s(tp, inGreen)
+ private def greenType(tree: Tree): String = tree match {
+ case null => "[exception]"
+ case md: MemberDef if md.tpe == NoType => inBlue(s"[${md.keyword} ${md.name}]") + " " + greenType(md.symbol.tpe)
+ case _ if tree.tpe.isComplete => greenType(tree.tpe)
+ case _ => "<?>"
+ }
+ def indented(s: String): String =
+ if (s == "") "" else currentIndent + s.replaceAll("\n", "\n" + currentIndent)
+
+ @inline final def runWith[T](t: Tree)(body: => T): T = {
+ push(t)
+ try body finally pop(t)
+ }
+ def push(t: Tree): Unit = {
+ trees ::= new Frame(t)
+ depth += 1
+ }
+ def pop(t: Tree): Unit = {
+ val frame = trees.head
+ assert(frame.tree eq t, ((frame.tree, t)))
+ trees = trees.tail
+ depth -= 1
+ }
+ def show(s: String) { if (s != "") out.println(s) }
+
+ def showPush(tree: Tree, context: Context) {
+ showPush(tree, NOmode, WildcardType, context)
+ }
+ def showPush(tree: Tree, mode: Mode, pt: Type, context: Context) {
+ val alreadyTyped = tree.tpe ne null
+ def tree_s = truncAndOneLine(ptTree(tree))
+ def pt_s = if (pt.isWildcard || context.inTypeConstructorAllowed) "" else s": pt=$pt"
+ def all_s = List(tree_s, pt_s, mode, fullSiteString(context)) filterNot (_ == "") mkString " "
+
+ atLowerIndent(show(indented("""|-- """ + all_s)))
+ }
+ def showPop(typedTree: Tree): Tree = {
+ val s = greenType(typedTree)
+ show(resetIfEmpty(indented("""\-> """ + s)))
+ typedTree
+ }
+ def showAdapt(original: Tree, adapted: Tree, pt: Type, context: Context) {
+ if (!noPrintAdapt(original, adapted)) {
+ def tree_s1 = inLightCyan(truncAndOneLine(ptTree(original)))
+ def pt_s = if (pt.isWildcard) "" else s" based on pt $pt"
+ def tree_s2 = adapted match {
+ case tt: TypeTree => "is now a TypeTree(" + tpe_s(tt.tpe, inCyan) + ")"
+ case _ => "adapted to " + inCyan(truncAndOneLine(ptTree(adapted))) + pt_s
+ }
+ show(indented(s"[adapt] $tree_s1 $tree_s2"))
+ }
+ }
+ def showTyped(tree: Tree) {
+ def class_s = tree match {
+ case _: RefTree => ""
+ case _ => " " + tree.shortClass
+ }
+ if (!noPrintTyping(tree))
+ show(indented(s"[typed$class_s] " + truncAndOneLine(ptTree(tree))))
+ }
+
+ def nextTyped(tree: Tree, mode: Mode, pt: Type, context: Context)(body: => Tree): Tree =
+ nextTypedInternal(tree, showPush(tree, mode, pt, context))(body)
+
+ def nextTyped(tree: Tree, context: Context)(body: => Tree): Tree =
+ nextTypedInternal(tree, showPush(tree, context))(body)
+
+ def nextTypedInternal(tree: Tree, pushFn: => Unit)(body: => Tree): Tree = (
+ if (noPrintTyping(tree))
+ body
+ else
+ runWith(tree) { pushFn ; showPop(body) }
+ )
+
+ @inline final def printTyping(tree: Tree, s: => String) = {
+ if (printTypings && !noPrintTyping(tree))
+ show(indented(s))
+ }
+ @inline final def printTyping(s: => String) = {
+ if (printTypings)
+ show(indented(s))
+ }
+ }
+ def tpe_s(tp: Type, colorize: String => String): String = tp match {
+ case OverloadedType(pre, alts) => alts map (alt => tpe_s(pre memberType alt, colorize)) mkString " <and> "
+ case _ => colorize(tp.toLongString)
+ }
+ // def sym_s(s: Symbol) = if (s eq null) "" + s else s.getClass.getName split '.' last;
+
+ // Some trees which are typed with mind-numbing frequency and
+ // which add nothing by being printed. Did () type to Unit? Let's
+ // gamble on yes.
+ private def printingOk(t: Tree) = printTypings && (settings.debug.value || !noPrint(t))
+ def noPrintTyping(t: Tree) = (t.tpe ne null) || !printingOk(t)
+ def noPrintAdapt(tree1: Tree, tree2: Tree) = !printingOk(tree1) || (
+ (tree1.tpe == tree2.tpe)
+ && (tree1.symbol == tree2.symbol)
+ )
+}
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index 424296c212..e41038cafc 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -3152,6 +3152,20 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
override def children = childSet
override def addChild(sym: Symbol) { childSet = childSet + sym }
+ def anonOrRefinementString = {
+ if (hasCompleteInfo) {
+ val label = if (isAnonymousClass) "$anon:" else "refinement of"
+ val parents = parentsString(info.parents map functionNBaseType filterNot (_.typeSymbol == SerializableClass))
+ s"<$label $parents>"
+ }
+ else if (isAnonymousClass) "$anon"
+ else nameString
+ }
+ override def toString = (
+ if (isAnonOrRefinementClass) anonOrRefinementString
+ else super.toString
+ )
+
if (Statistics.hotEnabled) Statistics.incCounter(classSymbolCount)
}
implicit val ClassSymbolTag = ClassTag[ClassSymbol](classOf[ClassSymbol])
diff --git a/src/reflect/scala/reflect/internal/TypeDebugging.scala b/src/reflect/scala/reflect/internal/TypeDebugging.scala
index 71f84ab557..9c1342e68e 100644
--- a/src/reflect/scala/reflect/internal/TypeDebugging.scala
+++ b/src/reflect/scala/reflect/internal/TypeDebugging.scala
@@ -7,11 +7,73 @@ package scala
package reflect
package internal
+import util.shortClassOfInstance
+
trait TypeDebugging {
self: SymbolTable =>
- // @M toString that is safe during debugging (does not normalize, ...)
+ import definitions._
+
+ /** There's a whole lot of implementation detail which is nothing but noise when
+ * you are trying to see what's going on. This is my attempt to filter it out.
+ */
+ object noPrint extends (Tree => Boolean) {
+ def skipScalaName(name: Name) = name match {
+ case tpnme.Any | tpnme.Nothing | tpnme.AnyRef => true
+ case _ => false
+ }
+ def skipRefTree(t: RefTree) = t match {
+ case Select(Select(Ident(nme.ROOTPKG), nme.scala_), name) if skipScalaName(name) => true
+ case Select(sel, name) if sel.symbol == ScalaPackage && skipScalaName(name) => true
+ case Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR) => true
+ case Ident(nme.ROOTPKG) => true
+ case _ => skipSym(t.symbol)
+ }
+ def skipSym(sym: Symbol): Boolean = sym match {
+ case null => false
+ case NothingClass | AnyClass => true
+ case PredefModule => true
+ case ObjectClass => true
+ case _ => sym.hasPackageFlag
+ }
+ def skipType(tpe: Type): Boolean = skipSym(tpe.typeSymbolDirect)
+
+ def skip(t: Tree): Boolean = t match {
+ case EmptyTree => true
+ case PackageDef(_, _) => true
+ case t: RefTree => skipRefTree(t)
+ case TypeBoundsTree(lo, hi) => skip(lo) && skip(hi)
+ case Block(Nil, expr) => skip(expr)
+ case Apply(fn, Nil) => skip(fn)
+ case Block(stmt :: Nil, expr) => skip(stmt) && skip(expr)
+ case DefDef(_, nme.CONSTRUCTOR, Nil, Nil :: Nil, _, rhs) => skip(rhs)
+ case Literal(Constant(())) => true
+ case tt @ TypeTree() => skipType(tt.tpe)
+ case _ => skipSym(t.symbol)
+ }
+ def apply(t: Tree) = skip(t)
+ }
+
+ /** Light color wrappers.
+ */
object typeDebug {
+ import scala.Console._
+
+ private val colorsOk = sys.props contains "scala.color"
+ private def inColor(s: String, color: String) = if (colorsOk && s != "") color + s + RESET else s
+ private def inBold(s: String, color: String) = if (colorsOk && s != "") color + BOLD + s + RESET else s
+
+ def inLightRed(s: String) = inColor(s, RED)
+ def inLightGreen(s: String) = inColor(s, GREEN)
+ def inLightMagenta(s: String) = inColor(s, MAGENTA)
+ def inLightCyan(s: String): String = inColor(s, CYAN)
+ def inGreen(s: String): String = inBold(s, GREEN)
+ def inRed(s: String): String = inBold(s, RED)
+ def inBlue(s: String): String = inBold(s, BLUE)
+ def inCyan(s: String): String = inBold(s, CYAN)
+ def inMagenta(s: String) = inBold(s, MAGENTA)
+ def resetColor(s: String): String = if (colorsOk) s + RESET else s
+
private def to_s(x: Any): String = x match {
// otherwise case classes are caught looking like products
case _: Tree | _: Type => "" + x
@@ -29,16 +91,32 @@ trait TypeDebugging {
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 ptLine(pairs: (String, Any)*): String = (
+ pairs
+ map { case (k, v) => (k, to_s(v)) }
+ filterNot { case (_, v) => v == "" }
+ map { case ("", v) => v ; case (k, v) => s"$k=$v" }
+ mkString ", "
+ )
+ def ptTree(t: Tree): String = t match {
+ case PackageDef(pid, _) => s"package $pid"
+ case ModuleDef(_, name, _) => s"object $name"
+ case DefDef(_, name, tparams, _, _, _) => "def " + name + ptTypeParams(tparams)
+ case ClassDef(_, name, Nil, _) if t.symbol != null && t.symbol.isModuleClass => s"module class $name"
+ case ClassDef(_, name, tparams, _) => "class " + name + ptTypeParams(tparams)
+ case td: TypeDef => ptTypeParam(td)
+ case TypeBoundsTree(lo, hi) =>
+ val lo_s = if (noPrint(lo)) "" else " >: " + ptTree(lo)
+ val hi_s = if (noPrint(hi)) "" else " <: " + ptTree(hi)
+ lo_s + hi_s
+ case _ if (t.symbol eq null) || (t.symbol eq NoSymbol) => to_s(t)
+ case _ => "" + t.symbol.tpe
}
- 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)
+ def ptTypeParam(td: TypeDef): String = {
+ val TypeDef(mods, name, tparams, rhs) = td
+ name + ptTypeParams(tparams) + ptTree(rhs)
}
+ def ptTypeParams(tparams: List[TypeDef]): String = str brackets (tparams map ptTypeParam)
object str {
def parentheses(xs: List[_]): String = xs.mkString("(", ", ", ")")
@@ -46,19 +124,24 @@ trait TypeDebugging {
def tparams(tparams: List[Type]): String = brackets(tparams map debug)
def parents(ps: List[Type]): String = (ps map debug).mkString(" with ")
def refine(defs: Scope): String = defs.toList.mkString("{", " ;\n ", "}")
+ def bounds(lo: Type, hi: Type): String = {
+ val lo_s = if (typeIsNothing(lo)) "" else s" >: $lo"
+ val hi_s = if (typeIsAny(hi)) "" else s" <: $hi"
+ lo_s + hi_s
+ }
}
-
+ import str._
private def debug(tp: Type): String = tp match {
- case TypeRef(pre, sym, args) => debug(pre) + "." + sym.nameString + str.tparams(args)
- case ThisType(sym) => sym.nameString + ".this"
- case SingleType(pre, sym) => debug(pre) +"."+ sym.nameString +".type"
- case RefinedType(parents, defs) => str.parents(parents) + str.refine(defs)
- case ClassInfoType(parents, defs, clazz) => "class "+ clazz.nameString + str.parents(parents) + str.refine(defs)
- case PolyType(tparams, result) => str.brackets(tparams) + " " + debug(result)
- case TypeBounds(lo, hi) => ">: "+ debug(lo) +" <: "+ debug(hi)
- case tv @ TypeVar(_, _) => tv.toString
- case ExistentialType(tparams, qtpe) => "forSome "+ str.brackets(tparams) + " " + debug(qtpe)
- case _ => "?"+tp.getClass.getName+"?"//tp.toString might produce cyclic error...
+ case TypeRef(pre, sym, args) => s"${debug(pre)}.${sym.nameString}.${tparams(args)}"
+ case ThisType(sym) => s"${sym.nameString}.this"
+ case SingleType(pre, sym) => s"${debug(pre)}.${sym.nameString}.type"
+ case RefinedType(ps, decls) => s"${parents(ps)} ${refine(decls)}"
+ case ClassInfoType(ps, decls, clazz) => s"class ${clazz.nameString} ${parents(ps)} ${refine(decls)}"
+ case PolyType(tparams, result) => s"${brackets(tparams)}${debug(result)}"
+ case TypeBounds(lo, hi) => bounds(lo, hi)
+ case tv @ TypeVar(_, _) => "" + tv
+ case ExistentialType(tparams, qtpe) => s"forSome ${brackets(tparams)} ${debug(qtpe)}"
+ case _ => s"?${shortClassOfInstance(tp)}?" // tp.toString might produce cyclic error...
}
def debugString(tp: Type) = debug(tp)
}