From b9c7cad893f4f3701ca3625aaea9a15088d15798 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Tue, 9 Apr 2024 13:36:27 +0200 Subject: [PATCH] Improve incorrect classloader reporting in staging * do not println staging crashes to stdout * enrich the errors coming from the compiler with additional hints about using the correct classloader --- compiler/src/dotty/tools/dotc/Run.scala | 2 ++ .../src/scala/quoted/staging/QuoteCompiler.scala | 4 +++- .../src/scala/quoted/staging/QuoteDriver.scala | 16 +++++++++++++++- tests/run-staging/i19170c.check | 1 + tests/run-staging/i19170c.scala | 16 ++++++++++++++++ tests/run-staging/i19176b.check | 1 + tests/run-staging/i19176b.scala | 14 ++++++++++++++ 7 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 tests/run-staging/i19170c.check create mode 100644 tests/run-staging/i19170c.scala create mode 100644 tests/run-staging/i19176b.check create mode 100644 tests/run-staging/i19176b.scala diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index 64e216a39b2a..fa827432460a 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -661,4 +661,6 @@ object Run { report.enrichErrorMessage(errorMessage) else errorMessage + def doNotEnrichErrorMessage: Unit = + if run != null then run.myEnrichedErrorMessage = true } diff --git a/staging/src/scala/quoted/staging/QuoteCompiler.scala b/staging/src/scala/quoted/staging/QuoteCompiler.scala index cf24b1de369a..dea40cd1035d 100644 --- a/staging/src/scala/quoted/staging/QuoteCompiler.scala +++ b/staging/src/scala/quoted/staging/QuoteCompiler.scala @@ -48,7 +48,9 @@ private class QuoteCompiler extends Compiler: override def newRun(implicit ctx: Context): ExprRun = reset() - new ExprRun(this, ctx.addMode(Mode.ReadPositions)) + val run = new ExprRun(this, ctx.addMode(Mode.ReadPositions)) + run.doNotEnrichErrorMessage + run def outputClassName: TypeName = "Generated$Code$From$Quoted".toTypeName diff --git a/staging/src/scala/quoted/staging/QuoteDriver.scala b/staging/src/scala/quoted/staging/QuoteDriver.scala index e894a7bc40f2..7eb99bce4ff8 100644 --- a/staging/src/scala/quoted/staging/QuoteDriver.scala +++ b/staging/src/scala/quoted/staging/QuoteDriver.scala @@ -8,6 +8,7 @@ import dotty.tools.dotc.quoted.QuotesCache import dotty.tools.io.{AbstractFile, Directory, PlainDirectory, VirtualDirectory} import dotty.tools.repl.AbstractFileClassLoader import dotty.tools.dotc.reporting._ +import dotty.tools.dotc.config.Settings.Setting.value import dotty.tools.dotc.util.ClasspathFromClassloader import scala.quoted._ import scala.quoted.staging.Compiler @@ -40,7 +41,20 @@ private class QuoteDriver(appClassloader: ClassLoader) extends Driver: setCompilerSettings(ctx1.fresh.setSetting(ctx1.settings.outputDir, outDir), settings) } - new QuoteCompiler().newRun(ctx).compileExpr(exprBuilder) match + val compiledExpr = + try + new QuoteCompiler().newRun(ctx).compileExpr(exprBuilder) + catch case ex: dotty.tools.FatalError => + val enrichedMessage = + s"""An unhandled exception was thrown in the staging compiler. + |This might be caused by using an incorrect classloader + |when creating the `staging.Compiler` instance with `staging.Compiler.make`. + |For details, please refer to the documentation. + |For non-enriched exceptions, compile with -Yno-enrich-error-messages.""".stripMargin + if ctx.settings.YnoEnrichErrorMessages.value(using ctx) then throw ex + else throw new Exception(enrichedMessage, ex) + + compiledExpr match case Right(value) => value.asInstanceOf[T] diff --git a/tests/run-staging/i19170c.check b/tests/run-staging/i19170c.check new file mode 100644 index 000000000000..581ccb11e364 --- /dev/null +++ b/tests/run-staging/i19170c.check @@ -0,0 +1 @@ +exception thrown, no additional printlns diff --git a/tests/run-staging/i19170c.scala b/tests/run-staging/i19170c.scala new file mode 100644 index 000000000000..24b7faa8a323 --- /dev/null +++ b/tests/run-staging/i19170c.scala @@ -0,0 +1,16 @@ +import scala.quoted.* + +given staging.Compiler = + staging.Compiler.make(getClass.getClassLoader.getParent) // different classloader that 19170b.scala +class A(i: Int) + +def f(i: Expr[Int])(using Quotes): Expr[A] = { '{ new A($i) } } + +@main def Test = { + try + val g: Int => A = staging.run { '{ (i: Int) => ${ f('{i}) } } } + println(g(3)) + catch case ex: Exception => + assert(ex.getMessage().startsWith("An unhandled exception was thrown in the staging compiler."), ex.getMessage()) + println("exception thrown, no additional printlns") +} diff --git a/tests/run-staging/i19176b.check b/tests/run-staging/i19176b.check new file mode 100644 index 000000000000..581ccb11e364 --- /dev/null +++ b/tests/run-staging/i19176b.check @@ -0,0 +1 @@ +exception thrown, no additional printlns diff --git a/tests/run-staging/i19176b.scala b/tests/run-staging/i19176b.scala new file mode 100644 index 000000000000..d3f1657d03da --- /dev/null +++ b/tests/run-staging/i19176b.scala @@ -0,0 +1,14 @@ +import scala.quoted.* + +given staging.Compiler = + staging.Compiler.make(getClass.getClassLoader.getParent) // we want to make sure the classloader is incorrect + +class A + +@main def Test = + try + val f: (A, Int) => Int = staging.run { '{ (q: A, x: Int) => x } } + f(new A, 3) + catch case ex: Exception => + assert(ex.getMessage().startsWith("An unhandled exception was thrown in the staging compiler."), ex.getMessage()) + println("exception thrown, no additional printlns")