summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-05-10 13:44:29 -0700
committerPaul Phillips <paulp@improving.org>2012-05-10 15:56:44 -0700
commit1bff5703d28a0232dc772769dd6017862114a4a2 (patch)
tree47847356c4daca5a4db1d76a0b29decb8d1f8bf8 /src/compiler/scala/tools/nsc
parent63a53e339598be1c8ffcb6e30a01d84b473d64fa (diff)
downloadscala-1bff5703d28a0232dc772769dd6017862114a4a2.tar.gz
scala-1bff5703d28a0232dc772769dd6017862114a4a2.tar.bz2
scala-1bff5703d28a0232dc772769dd6017862114a4a2.zip
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 <method> <specialized>) 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 <empty> context owners: value it -> anonymous class $anon -> method x$mcZ$sp -> trait Bounds$mcZ$sp -> package <empty> == 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
Diffstat (limited to 'src/compiler/scala/tools/nsc')
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala80
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala8
2 files changed, 72 insertions, 16 deletions
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