diff --git a/compiler/src/dotty/tools/dotc/Driver.scala b/compiler/src/dotty/tools/dotc/Driver.scala index 4207baa57470..fc5367d2ccba 100644 --- a/compiler/src/dotty/tools/dotc/Driver.scala +++ b/compiler/src/dotty/tools/dotc/Driver.scala @@ -54,8 +54,7 @@ class Driver { if (ctx.settings.XprintSuspension.value) report.echo(i"compiling suspended $suspendedUnits%, %") val run1 = compiler.newRun - for unit <- suspendedUnits do unit.suspended = false - run1.compileUnits(suspendedUnits) + run1.compileSuspendedUnits(suspendedUnits) finish(compiler, run1)(using MacroClassLoader.init(ctx.fresh)) protected def initCtx: Context = (new ContextBase).initialCtx diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index c3a91ef1ce1b..d5d49496ba30 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -358,6 +358,17 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint compiling = false } + private var myCompilingSuspended: Boolean = false + + /** Is this run started via a compilingSuspended? */ + def isCompilingSuspended: Boolean = myCompilingSuspended + + /** Compile units `us` which were suspended in a previous run */ + def compileSuspendedUnits(us: List[CompilationUnit]): Unit = + myCompilingSuspended = true + for unit <- us do unit.suspended = false + compileUnits(us) + /** Enter top-level definitions of classes and objects contained in source file `file`. * The newly added symbols replace any previously entered symbols. * If `typeCheck = true`, also run typer on the compilation unit, and set diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 3608f16e3478..7f390490e0fc 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -954,7 +954,10 @@ object Denotations { } def staleSymbolError(using Context): Nothing = - throw new StaleSymbol(staleSymbolMsg) + if symbol.isPackageObject && ctx.run != null && ctx.run.nn.isCompilingSuspended then + throw TypeError(em"Cyclic macro dependency; macro refers to a toplevel symbol in ${symbol.source} from which the macro is called") + else + throw new StaleSymbol(staleSymbolMsg) def staleSymbolMsg(using Context): String = { def ownerMsg = this match { diff --git a/tests/neg/i19351.check b/tests/neg/i19351.check new file mode 100644 index 000000000000..a9ee10510b32 --- /dev/null +++ b/tests/neg/i19351.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i19351/A.scala:3:35 -------------------------------------------------------------------------------- +3 | inline def myMacro(): x.type = ${myMacroExpr} // error + | ^ + |Cyclic macro dependency; macro refers to a toplevel symbol in tests/neg/i19351/A.scala from which the macro is called diff --git a/tests/neg/i19351/A.scala b/tests/neg/i19351/A.scala new file mode 100644 index 000000000000..f2258631a15a --- /dev/null +++ b/tests/neg/i19351/A.scala @@ -0,0 +1,5 @@ +//object A: + val x: Int = 1 + inline def myMacro(): x.type = ${myMacroExpr} // error + def test = myMacro() + diff --git a/tests/neg/i19351/B.scala b/tests/neg/i19351/B.scala new file mode 100644 index 000000000000..8c8c071f3607 --- /dev/null +++ b/tests/neg/i19351/B.scala @@ -0,0 +1,3 @@ +import scala.quoted.* +//import A.* +def myMacroExpr(using Quotes): Expr[x.type] = '{???} \ No newline at end of file diff --git a/tests/neg/i19351a.check b/tests/neg/i19351a.check new file mode 100644 index 000000000000..3c1353811f3d --- /dev/null +++ b/tests/neg/i19351a.check @@ -0,0 +1,12 @@ +-- Error: tests/neg/i19351a/Test.scala:8:34 ---------------------------------------------------------------------------- +8 |inline def not(b: Bool): Bool = ${notMacro('b)} // error // error + | ^ + |Cyclic macro dependency; macro refers to a toplevel symbol in tests/neg/i19351a/Test.scala from which the macro is called +-- [E046] Cyclic Error: tests/neg/i19351a/Test.scala:8:46 -------------------------------------------------------------- +8 |inline def not(b: Bool): Bool = ${notMacro('b)} // error // error + | ^ + | Cyclic reference involving method $anonfun + | + | Run with -explain-cyclic for more details. + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i19351a/Macro.scala b/tests/neg/i19351a/Macro.scala new file mode 100644 index 000000000000..8e691771d69a --- /dev/null +++ b/tests/neg/i19351a/Macro.scala @@ -0,0 +1,13 @@ +// Macro class +package test + +import scala.quoted.* + +def notMacro(bool: Expr[Bool])(using Quotes): Expr[Bool] = + '{$bool(False, True)} + +def showMacro(bool: Expr[Bool])(using Quotes): Expr[String] = + '{$bool("TRUE", "FALSE")} + +def foldMacro[T: Type](bool: Expr[Bool], t: Expr[T], f: Expr[T])(using Quotes): Expr[T] = + '{$bool($t, $f)} \ No newline at end of file diff --git a/tests/neg/i19351a/Test.scala b/tests/neg/i19351a/Test.scala new file mode 100644 index 000000000000..51f608aa46ea --- /dev/null +++ b/tests/neg/i19351a/Test.scala @@ -0,0 +1,15 @@ +//Test class +package test + +type Bool = [R] => (R, R) => R +val True: Bool = [R] => (t: R, _: R) => t +val False: Bool = [R] => (_: R, f: R) => f + +inline def not(b: Bool): Bool = ${notMacro('b)} // error // error +inline def show(b: Bool): String = ${showMacro('b)} +//inline def not(b: Bool): Bool = ${foldMacro('b, 'False, 'True)} +//inline def show(b: Bool): String = ${foldMacro('b, '{"TRUE"}, '{"FALSE"})} + + +@main def testing = + println(show(not(True))) \ No newline at end of file