Skip to content
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.

Copy link
Member

@adriaanm adriaanm replied May 11, 2012

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

@paulp

This comment has been minimized.

Copy link
Contributor Author

@paulp paulp replied May 11, 2012

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

Please sign in to comment.
You can’t perform that action at this time.