Skip to content

Commit bab74d7

Browse files
committed
Fix module symbol recovery from NoClassDefFoundError
Fixes #19601
1 parent 551eae4 commit bab74d7

File tree

3 files changed

+95
-4
lines changed

3 files changed

+95
-4
lines changed

compiler/src/dotty/tools/dotc/quoted/Interpreter.scala

+9-4
Original file line numberDiff line numberDiff line change
@@ -353,11 +353,16 @@ object Interpreter:
353353
if !ctx.compilationUnit.isSuspendable then None
354354
else targetException match
355355
case _: NoClassDefFoundError | _: ClassNotFoundException =>
356-
val className = targetException.getMessage
357-
if className eq null then None
356+
val message = targetException.getMessage
357+
if message eq null then None
358358
else
359-
val sym = staticRef(className.toTypeName).symbol
360-
if (sym.isDefinedInCurrentRun) Some(sym) else None
359+
val className = message.replace('/', '.')
360+
val sym =
361+
if className.endsWith(str.MODULE_SUFFIX) then staticRef(className.toTermName).symbol.moduleClass
362+
else staticRef(className.toTypeName).symbol
363+
// If the symbol does not a a position we assume that it came from the current run and it has an error
364+
if sym.isDefinedInCurrentRun || (sym.exists && !sym.srcPos.span.exists) then Some(sym)
365+
else None
361366
case _ => None
362367
}
363368
}

tests/neg-macros/i19601/Macro.scala

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package prelude
2+
import scala.quoted.*
3+
4+
object Macros {
5+
def validateInlineImpl[A: Type](assertionExpr: Expr[Assertion[A]], a: Expr[A])(using
6+
Quotes
7+
): Expr[Unit] = {
8+
import quotes.reflect.*
9+
val crashRoot = assertionExpr.value
10+
'{ () }
11+
12+
}
13+
given [A](using Type[A]): FromExpr[Assertion[A]] with {
14+
def unapply(assertion: Expr[Assertion[A]])(using Quotes): Option[Assertion[A]] = {
15+
import quotes.reflect.*
16+
17+
assertion match {
18+
case '{ Assertion.greaterThanOrEqualTo[A](${ LiteralUnlift(value) })($_) } =>
19+
Some(Assertion.greaterThanOrEqualTo(value)(orderingForValue(value)))
20+
case _ => None
21+
}
22+
}
23+
}
24+
25+
object LiteralUnlift {
26+
def unapply[A: Type](expr: Expr[A])(using Quotes): Option[A] = expr match {
27+
case '{ ${ Expr(int) }: Int } => Some(int)
28+
case '{ Int.MaxValue } => Some(Int.MaxValue.asInstanceOf[A])
29+
case '{ Int.MinValue } => Some(Int.MinValue.asInstanceOf[A])
30+
case '{ ${ Expr(string) }: String } => Some(string)
31+
case '{ ${ Expr(double) }: Double } => Some(double)
32+
case '{ ${ Expr(float) }: Float } => Some(float)
33+
case '{ ${ Expr(long) }: Long } => Some(long)
34+
case '{ ${ Expr(short) }: Short } => Some(short)
35+
case '{ ${ Expr(byte) }: Byte } => Some(byte)
36+
case '{ ${ Expr(char) }: Char } => Some(char)
37+
case _ => None
38+
}
39+
}
40+
41+
private def orderingForValue(any: Any)(using Quotes): Ordering[Any] = null.asInstanceOf[Ordering[Any]]
42+
}

tests/neg-macros/i19601/Test.scala

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package prelude
2+
3+
sealed trait Assertion[-A]:
4+
def unary_! : Assertion[A] = ???
5+
def apply(a: A): Either[AssertionError, Unit] = ???
6+
object Assertion:
7+
val anything: Assertion[Any] = ???
8+
def greaterThanOrEqualTo[A](value: A)(implicit ordering: Ordering[A]): Assertion[A] = ???
9+
10+
sealed trait AssertionError
11+
12+
abstract class NewtypeCustom[A] {
13+
type Type
14+
protected inline def validateInline(inline value: A): Unit
15+
16+
inline def apply(inline a1: A): Type = {
17+
validateInline(a1)
18+
a1.asInstanceOf[Type]
19+
}
20+
}
21+
abstract class Newtype[A] extends NewtypeCustom[A] {
22+
def assertion: Assertion[A] = Assertion.anything
23+
protected inline def validateInline(inline value: A): Unit = ${
24+
Macros.validateInlineImpl[A]('assertion, 'value)
25+
}
26+
}
27+
28+
29+
abstract class Subtype[A] extends Newtype[A] {
30+
type Type <: A
31+
}
32+
33+
34+
package object newtypes {
35+
type Natural = Natural.Type
36+
object Natural extends Subtype[Int] {
37+
38+
override inline def assertion = Assertion.greaterThanOrEqualTo(0)
39+
40+
val one: Natural = Natural(1) // triggers macro
41+
}
42+
}
43+
44+
// nopos-error: Cyclic macro dependencies in tests/pos-macros/i19601/Test.scala. Compilation stopped since no further progress can be made.

0 commit comments

Comments
 (0)