diff --git a/compiler/src/dotty/tools/dotc/ast/Positioned.scala b/compiler/src/dotty/tools/dotc/ast/Positioned.scala index 7ee0f4798164..a794bf7bae7e 100644 --- a/compiler/src/dotty/tools/dotc/ast/Positioned.scala +++ b/compiler/src/dotty/tools/dotc/ast/Positioned.scala @@ -10,6 +10,9 @@ import core.Flags.{JavaDefined, Extension} import core.StdNames.nme import annotation.constructorOnly import annotation.internal.sharable +import reporting.Reporter + +import java.io.{ PrintWriter } /** A base class for things that have positions (currently: modifiers and trees) */ @@ -24,13 +27,11 @@ abstract class Positioned(implicit @constructorOnly src: SourceFile) extends Pro def uniqueId: Int = myUniqueId def uniqueId_=(id: Int): Unit = { - if (Positioned.debugId == id) { - def printTrace() = { - val stack = Thread.currentThread().getStackTrace().map("> " + _) - System.err.println(stack.mkString(s"> Debug tree (id=${Positioned.debugId}) creation \n> $this\n", "\n", "\n")) - } - printTrace() + def printTrace() = { + println(s"Debug tree (id=${Positioned.debugId}) creation \n$this\n") + Reporter.displayPrompt(Console.in, new PrintWriter(Console.err, true)) } + if (Positioned.debugId == id) printTrace() myUniqueId = id } diff --git a/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala b/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala index 8eb9dda8f7b1..e6be267d60cf 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala @@ -25,7 +25,7 @@ class ConsoleReporter( val didPrint = m match { case m: Error => printMessage(messageAndPos(m.contained(), m.pos, diagnosticLevel(m))) - if (ctx.settings.Xprompt.value) displayPrompt() + if (ctx.settings.Xprompt.value) Reporter.displayPrompt(reader, writer) true case m: ConditionalWarning if !m.enablingOption.value => false @@ -40,28 +40,5 @@ class ConsoleReporter( printMessage("\nlonger explanation available when compiling with `-explain`") } - /** Show prompt if `-Xprompt` is passed as a flag to the compiler */ - def displayPrompt(): Unit = { - writer.println() - writer.print("a)bort, s)tack, r)esume: ") - writer.flush() - if (reader != null) { - def loop(): Unit = reader.read match { - case 'a' | 'A' => - new Throwable().printStackTrace(writer) - System.exit(1) - case 's' | 'S' => - new Throwable().printStackTrace(writer) - writer.println() - writer.flush() - case 'r' | 'R' => - () - case _ => - loop() - } - loop() - } - } - override def flush()(implicit ctx: Context): Unit = { writer.flush() } } diff --git a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala index cf5184f198b3..b742d59d392e 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala @@ -8,7 +8,6 @@ import core.Contexts._ import util.{SourcePosition, NoSourcePosition} import core.Decorators.PhaseListDecorator import collection.mutable -import java.lang.System.currentTimeMillis import core.Mode import dotty.tools.dotc.core.Symbols.{Symbol, NoSymbol} import diagnostic.messages._ @@ -16,6 +15,10 @@ import diagnostic._ import ast.{tpd, Trees} import Message._ +import java.lang.System.currentTimeMillis +import java.io.{ BufferedReader, PrintWriter } + + object Reporter { /** Convert a SimpleReporter into a real Reporter */ def fromSimpleReporter(simple: interfaces.SimpleReporter): Reporter = @@ -37,6 +40,29 @@ object Reporter { private val defaultIncompleteHandler: ErrorHandler = (mc, ctx) => ctx.reporter.report(mc)(ctx) + + /** Show prompt if `-Xprompt` is passed as a flag to the compiler */ + def displayPrompt(reader: BufferedReader, writer: PrintWriter): Unit = { + writer.println() + writer.print("a)bort, s)tack, r)esume: ") + writer.flush() + if (reader != null) { + def loop(): Unit = reader.read match { + case 'a' | 'A' => + new Throwable().printStackTrace(writer) + System.exit(1) + case 's' | 'S' => + new Throwable().printStackTrace(writer) + writer.println() + writer.flush() + case 'r' | 'R' => + () + case _ => + loop() + } + loop() + } + } } trait Reporting { this: Context => diff --git a/docs/docs/internals/debug-macros.md b/docs/docs/internals/debug-macros.md new file mode 100644 index 000000000000..88c25705bae4 --- /dev/null +++ b/docs/docs/internals/debug-macros.md @@ -0,0 +1,32 @@ +--- +layout: doc-page +title: "Debug Macros" +--- + +Complex macros may break invariants of the compiler, which leads to compiler crashes. +Here we lists common compiler crashes and how to deal with them. + +## position not set + +For this problem, here is the log that is usually shown: + +``` +[error] assertion failed: position not set for org.scalactic.anyvals.PosZInt.+$extension3(SizeParam.this.minSize)( +[error] org.scalactic.anyvals.PosZInt.widenToInt(SizeParam.this.sizeRange) +[error] ) # 2326942 of class dotty.tools.dotc.ast.Trees$Apply in library/src-bootstrapped/scala/tasty/reflect/utils/TreeUtils.scala +``` + +To debug why the position is not set, note the tree id `2326942`, and enable +the following compiler option: + +``` +-Ydebug-tree-with-id 2326942 +``` + +With the option above, the compiler will crash when the tree is created. From +the stack trace, we will be able to figure out where the tree is created. + +If the position is in the compiler, then either report a compiler bug or +fix the problem with `.withSpan(tree.span)`. The following fix is an example: + +- https://github.com/lampepfl/dotty/pull/6581 diff --git a/docs/sidebar.yml b/docs/sidebar.yml index a25063ae4746..d648dc6e0a3b 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -183,6 +183,8 @@ sidebar: url: docs/internals/type-system.html - title: "Dotty Internals 1: Trees & Symbols (Meeting Notes)" url: docs/internals/dotty-internals-1-notes.html + - title: Debug Macros + url: docs/internals/debug-macros.html - title: Resources subsection: - title: Talks