From ffad39e17feb393fdece69aab04ed82d721a6be1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 8 Feb 2024 08:52:42 +0100 Subject: [PATCH] Fix module symbol recovery from `NoClassDefFoundError` Fixes #19601 [Cherry-picked bab74d7714f92657099fd9a119e849116be7a651] --- .../dotty/tools/dotc/quoted/Interpreter.scala | 13 ++++-- tests/neg-macros/i19601/Macro.scala | 42 ++++++++++++++++++ tests/neg-macros/i19601/Test.scala | 44 +++++++++++++++++++ 3 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 tests/neg-macros/i19601/Macro.scala create mode 100644 tests/neg-macros/i19601/Test.scala diff --git a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala index e42d809853e3..d7a02a94f9a2 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala @@ -353,11 +353,16 @@ object Interpreter: if !ctx.compilationUnit.isSuspendable then None else targetException match case _: NoClassDefFoundError | _: ClassNotFoundException => - val className = targetException.getMessage - if className eq null then None + val message = targetException.getMessage + if message eq null then None else - val sym = staticRef(className.toTypeName).symbol - if (sym.isDefinedInCurrentRun) Some(sym) else None + val className = message.replace('/', '.') + val sym = + if className.endsWith(str.MODULE_SUFFIX) then staticRef(className.toTermName).symbol.moduleClass + else staticRef(className.toTypeName).symbol + // If the symbol does not a a position we assume that it came from the current run and it has an error + if sym.isDefinedInCurrentRun || (sym.exists && !sym.srcPos.span.exists) then Some(sym) + else None case _ => None } } diff --git a/tests/neg-macros/i19601/Macro.scala b/tests/neg-macros/i19601/Macro.scala new file mode 100644 index 000000000000..8d6d22005017 --- /dev/null +++ b/tests/neg-macros/i19601/Macro.scala @@ -0,0 +1,42 @@ +package prelude +import scala.quoted.* + +object Macros { + def validateInlineImpl[A: Type](assertionExpr: Expr[Assertion[A]], a: Expr[A])(using + Quotes + ): Expr[Unit] = { + import quotes.reflect.* + val crashRoot = assertionExpr.value + '{ () } + + } + given [A](using Type[A]): FromExpr[Assertion[A]] with { + def unapply(assertion: Expr[Assertion[A]])(using Quotes): Option[Assertion[A]] = { + import quotes.reflect.* + + assertion match { + case '{ Assertion.greaterThanOrEqualTo[A](${ LiteralUnlift(value) })($_) } => + Some(Assertion.greaterThanOrEqualTo(value)(orderingForValue(value))) + case _ => None + } + } + } + + object LiteralUnlift { + def unapply[A: Type](expr: Expr[A])(using Quotes): Option[A] = expr match { + case '{ ${ Expr(int) }: Int } => Some(int) + case '{ Int.MaxValue } => Some(Int.MaxValue.asInstanceOf[A]) + case '{ Int.MinValue } => Some(Int.MinValue.asInstanceOf[A]) + case '{ ${ Expr(string) }: String } => Some(string) + case '{ ${ Expr(double) }: Double } => Some(double) + case '{ ${ Expr(float) }: Float } => Some(float) + case '{ ${ Expr(long) }: Long } => Some(long) + case '{ ${ Expr(short) }: Short } => Some(short) + case '{ ${ Expr(byte) }: Byte } => Some(byte) + case '{ ${ Expr(char) }: Char } => Some(char) + case _ => None + } + } + + private def orderingForValue(any: Any)(using Quotes): Ordering[Any] = null.asInstanceOf[Ordering[Any]] +} diff --git a/tests/neg-macros/i19601/Test.scala b/tests/neg-macros/i19601/Test.scala new file mode 100644 index 000000000000..f10a06badaf7 --- /dev/null +++ b/tests/neg-macros/i19601/Test.scala @@ -0,0 +1,44 @@ +package prelude + +sealed trait Assertion[-A]: + def unary_! : Assertion[A] = ??? + def apply(a: A): Either[AssertionError, Unit] = ??? +object Assertion: + val anything: Assertion[Any] = ??? + def greaterThanOrEqualTo[A](value: A)(implicit ordering: Ordering[A]): Assertion[A] = ??? + +sealed trait AssertionError + +abstract class NewtypeCustom[A] { + type Type + protected inline def validateInline(inline value: A): Unit + + inline def apply(inline a1: A): Type = { + validateInline(a1) + a1.asInstanceOf[Type] + } +} +abstract class Newtype[A] extends NewtypeCustom[A] { + def assertion: Assertion[A] = Assertion.anything + protected inline def validateInline(inline value: A): Unit = ${ + Macros.validateInlineImpl[A]('assertion, 'value) + } +} + + +abstract class Subtype[A] extends Newtype[A] { + type Type <: A +} + + +package object newtypes { + type Natural = Natural.Type + object Natural extends Subtype[Int] { + + override inline def assertion = Assertion.greaterThanOrEqualTo(0) + + val one: Natural = Natural(1) // triggers macro + } +} + +// nopos-error: Cyclic macro dependencies in tests/pos-macros/i19601/Test.scala. Compilation stopped since no further progress can be made.