Permalink
Browse files

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
  • Loading branch information...
paulp committed May 10, 2012
1 parent 63a53e3 commit 1bff5703d28a0232dc772769dd6017862114a4a2
@@ -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,
@@ -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 ""
)
}
@@ -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,13 +896,22 @@ 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
* really want to know, this hack.
*/
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 }
@@ -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
@@ -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
}
}

2 comments on commit 1bff570

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm May 11, 2012

Member

excellent! now all we need is a "send feedback to EPFL" button

Member

adriaanm replied May 11, 2012

excellent! now all we need is a "send feedback to EPFL" button

@paulp

This comment has been minimized.

Show comment
Hide comment
@paulp

paulp May 11, 2012

Contributor

Or is that the last thing we need? Dilemmas, dilemmas...

Contributor

paulp replied May 11, 2012

Or is that the last thing we need? Dilemmas, dilemmas...

Please sign in to comment.