From 1bff5703d28a0232dc772769dd6017862114a4a2 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 10 May 2012 13:44:29 -0700 Subject: More useful crash reports. If you can't get your hands on something which crashes scalac - I know, I know - you can try this ready-made crasher. % cat test/pending/pos/t4717.scala trait Bounds[@specialized A] { // okay without `>: A` def x[B >: A]: Unit = new Bounds[B] { lazy val it = ??? // def or val okay it } } % scalac -d /tmp test/pending/pos/t4717.scala error: while compiling: test/pending/pos/t4717.scala during phase: specialize library version: version 2.10.0-20120510-134429-ce1d68ed19 compiler version: version 2.10.0-20120510-152646-ba4dfd1e63 reconstructed args: -d /tmp last tree to typer: Select(This(trait Bounds$mcZ$sp), x$mcZ$sp) symbol: method x$mcZ$sp in trait Bounds$mcZ$sp (flags: override ) symbol definition: override def x$mcZ$sp[B >: Boolean](): Unit tpe: [B >: Boolean]()Unit symbol owners: method x$mcZ$sp -> trait Bounds$mcZ$sp -> package context owners: value it -> anonymous class $anon -> method x$mcZ$sp -> trait Bounds$mcZ$sp -> package == Enclosing template or block == Block( Assign( $anon.this."it " Apply( // def ???(): Nothing in object Predef, tree.tpe=Nothing scala.this."Predef"."$qmark$qmark$qmark" // def ???(): Nothing in object Predef, tree.tpe=()Nothing Nil ) ) $anon.this."it " // lazy private[this] var it: Nothing, tree.tpe=Nothing ) == Expanded type of tree == PolyType( typeParams = List(TypeParam(B >: Boolean)) resultType = NullaryMethodType( resultType = TypeRef(TypeSymbol(final class Unit extends AnyVal)) ) ) // And then the usual stack trace --- src/compiler/scala/reflect/internal/Symbols.scala | 2 +- src/compiler/scala/reflect/internal/Trees.scala | 5 +- src/compiler/scala/tools/nsc/Global.scala | 80 ++++++++++++++++++---- .../scala/tools/nsc/typechecker/Typers.scala | 8 ++- test/pending/pos/t4717.scala | 10 +-- 5 files changed, 81 insertions(+), 24 deletions(-) diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 2a5b759f94..154d82c736 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -2232,7 +2232,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } def infosString = infos.toString - def debugLocationString = fullLocationString + " " + debugFlagString + def debugLocationString = fullLocationString + " (flags: " + debugFlagString + ")" private def defStringCompose(infoString: String) = compose( flagString, diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala index e2d9310424..3e7f23800c 100644 --- a/src/compiler/scala/reflect/internal/Trees.scala +++ b/src/compiler/scala/reflect/internal/Trees.scala @@ -145,11 +145,12 @@ trait Trees extends api.Trees { self: SymbolTable => */ def summaryString: String = tree match { case Literal(const) => "Literal(" + const + ")" - case Select(qual, name) => qual.summaryString + "." + name.decode + case Ident(name) => "Ident(%s)".format(name.decode) + case Select(qual, name) => "Select(%s, %s)".format(qual.summaryString, name.decode) case t: NameTree => t.name.longString case t => t.shortClass + ( - if (t.symbol != null && t.symbol != NoSymbol) " " + t.symbol + if (t.symbol != null && t.symbol != NoSymbol) "(" + t.symbol + ")" else "" ) } diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index d3564017f9..de270a76f1 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -162,7 +162,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb /** Register new context; called for every created context */ - def registerContext(c: analyzer.Context) {} + def registerContext(c: analyzer.Context) { + lastSeenContext = c + } /** Register top level class (called on entering the class) */ @@ -894,6 +896,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb curRun = null } + object typeDeconstruct extends { + val global: Global.this.type = Global.this + } with interpreter.StructuredTypeStrings + /** There are common error conditions where when the exception hits * here, currentRun.currentUnit is null. This robs us of the knowledge * of what file was being compiled when it broke. Since I really @@ -901,6 +907,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb */ private var lastSeenSourceFile: SourceFile = NoSourceFile + /** Let's share a lot more about why we crash all over the place. + * People will be very grateful. + */ + private var lastSeenContext: analyzer.Context = null + /** The currently active run */ def currentRun: Run = curRun @@ -929,25 +940,64 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb @inline final def beforeTyper[T](op: => T): T = beforePhase(currentRun.typerPhase)(op) @inline final def beforeUncurry[T](op: => T): T = beforePhase(currentRun.uncurryPhase)(op) + def explainContext(c: analyzer.Context): String = ( + if (c == null) "" else ( + """| context owners: %s + | + |Enclosing block or template: + |%s""".format( + c.owner.ownerChain.takeWhile(!_.isPackageClass).mkString(" -> "), + nodePrinters.nodeToString(c.enclClassOrMethod.tree) + ) + ) + ) + // Owners up to and including the first package class. + private def ownerChainString(sym: Symbol): String = ( + if (sym == null) "" + else sym.ownerChain.span(!_.isPackageClass) match { + case (xs, pkg :: _) => (xs :+ pkg) mkString " -> " + case _ => sym.ownerChain mkString " -> " // unlikely + } + ) + private def formatExplain(pairs: (String, Any)*): String = ( + pairs.toList collect { case (k, v) if v != null => "%20s: %s".format(k, v) } mkString "\n" + ) + + def explainTree(t: Tree): String = formatExplain( + ) + /** Don't want to introduce new errors trying to report errors, * so swallow exceptions. */ override def supplementErrorMessage(errorMessage: String): String = try { - """| - | while compiling: %s - | current phase: %s - | library version: %s - | compiler version: %s - | reconstructed args: %s - | - |%s""".stripMargin.format( - currentSource.path, - phase, - scala.util.Properties.versionString, - Properties.versionString, - settings.recreateArgs.mkString(" "), - if (opt.debug) "Current unit body:\n" + currentUnit.body + "\n" + errorMessage else errorMessage + val tree = analyzer.lastTreeToTyper + val sym = tree.symbol + val tpe = tree.tpe + val enclosing = lastSeenContext.enclClassOrMethod.tree + + val info1 = formatExplain( + "while compiling" -> currentSource.path, + "during phase" -> phase, + "library version" -> scala.util.Properties.versionString, + "compiler version" -> Properties.versionString, + "reconstructed args" -> settings.recreateArgs.mkString(" ") + ) + val info2 = formatExplain( + "last tree to typer" -> tree.summaryString, + "symbol" -> Option(sym).fold("null")(_.debugLocationString), + "symbol definition" -> Option(sym).fold("null")(_.defString), + "tpe" -> tpe, + "symbol owners" -> ownerChainString(sym), + "context owners" -> ownerChainString(lastSeenContext.owner) ) + val info3: List[String] = ( + ( List("== Enclosing template or block ==", nodePrinters.nodeToString(enclosing).trim) ) + ++ ( if (tpe eq null) Nil else List("== Expanded type of tree ==", typeDeconstruct.show(tpe)) ) + ++ ( if (!opt.debug) Nil else List("== Current unit body ==", nodePrinters.nodeToString(currentUnit.body)) ) + ++ ( List(errorMessage) ) + ) + + ("\n" + info1) :: info2 :: info3 mkString "\n\n" } catch { case x: Exception => errorMessage } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 343636ff1e..34e1aaedfd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -31,7 +31,6 @@ trait Typers extends Modes with Adaptations with Taggings { import global._ import definitions._ - import patmat.DefaultOverrideMatchAttachment final def forArgMode(fun: Tree, mode: Int) = @@ -85,6 +84,12 @@ trait Typers extends Modes with Adaptations with Taggings { private def isPastTyper = phase.id > currentRun.typerPhase.id + // 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 @@ -4972,6 +4977,7 @@ trait Typers extends Modes with Adaptations with Taggings { * @return ... */ def typed(tree: Tree, mode: Int, pt: Type): Tree = { + lastTreeToTyper = tree indentTyping() var alreadyTyped = false diff --git a/test/pending/pos/t4717.scala b/test/pending/pos/t4717.scala index 43cf412bc6..7eaa3dd487 100644 --- a/test/pending/pos/t4717.scala +++ b/test/pending/pos/t4717.scala @@ -1,7 +1,7 @@ -trait Bug1[@specialized +A] extends TraversableOnce[A] { - def ++[B >: A](that: TraversableOnce[B]): Iterator[B] = new Iterator[B] { - lazy val it = that.toIterator - def hasNext = it.hasNext - def next = it.next +trait Bounds[@specialized A] { + // okay without `>: A` + def x[B >: A]: Unit = new Bounds[B] { + lazy val it = ??? // def or val okay + it } } \ No newline at end of file -- cgit v1.2.3