From e4e1c57168bac79df7c4affff38d408127e9caaa Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 27 Jan 2024 21:10:14 +0100 Subject: [PATCH 1/2] Error instead of StaleSymbol crash for certain cyclic macro dependencies If a suspended macro refers back to a symbol in a previously compiled package object, report this as an error instead of crashing with a StaleSymbol exception. There's unfortunately not a lot of info available at the point where the error is raised, so the error message is a bit vague. But it's better than crashing. --- compiler/src/dotty/tools/dotc/Driver.scala | 3 +-- compiler/src/dotty/tools/dotc/Run.scala | 11 +++++++++++ .../src/dotty/tools/dotc/core/Denotations.scala | 5 ++++- tests/neg/i19531.check | 4 ++++ tests/neg/i19531/A.scala | 5 +++++ tests/neg/i19531/B.scala | 3 +++ tests/neg/i19531a.check | 12 ++++++++++++ tests/neg/i19531a/Macro.scala | 13 +++++++++++++ tests/neg/i19531a/Test.scala | 15 +++++++++++++++ 9 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 tests/neg/i19531.check create mode 100644 tests/neg/i19531/A.scala create mode 100644 tests/neg/i19531/B.scala create mode 100644 tests/neg/i19531a.check create mode 100644 tests/neg/i19531a/Macro.scala create mode 100644 tests/neg/i19531a/Test.scala 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/i19531.check b/tests/neg/i19531.check new file mode 100644 index 000000000000..017946e2a4b9 --- /dev/null +++ b/tests/neg/i19531.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i19531/A.scala:3:35 -------------------------------------------------------------------------------- +3 | inline def myMacro(): x.type = ${myMacroExpr} // error + | ^ + |Cyclic macro dependency; macro refers to a toplevel symbol in tests/neg/i19531/A.scala from which the macro is called diff --git a/tests/neg/i19531/A.scala b/tests/neg/i19531/A.scala new file mode 100644 index 000000000000..f2258631a15a --- /dev/null +++ b/tests/neg/i19531/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/i19531/B.scala b/tests/neg/i19531/B.scala new file mode 100644 index 000000000000..8c8c071f3607 --- /dev/null +++ b/tests/neg/i19531/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/i19531a.check b/tests/neg/i19531a.check new file mode 100644 index 000000000000..009807dd41fb --- /dev/null +++ b/tests/neg/i19531a.check @@ -0,0 +1,12 @@ +-- Error: tests/neg/i19531a/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/i19531a/Test.scala from which the macro is called +-- [E046] Cyclic Error: tests/neg/i19531a/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/i19531a/Macro.scala b/tests/neg/i19531a/Macro.scala new file mode 100644 index 000000000000..8e691771d69a --- /dev/null +++ b/tests/neg/i19531a/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/i19531a/Test.scala b/tests/neg/i19531a/Test.scala new file mode 100644 index 000000000000..51f608aa46ea --- /dev/null +++ b/tests/neg/i19531a/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 From 9cb9d4bad8b490d08506298678894b7da7d131dc Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 27 Jan 2024 21:29:43 +0100 Subject: [PATCH 2/2] Fix issue number in test file names --- tests/neg/{i19531.check => i19351.check} | 4 ++-- tests/neg/{i19531 => i19351}/A.scala | 0 tests/neg/{i19531 => i19351}/B.scala | 0 tests/neg/{i19531a.check => i19351a.check} | 6 +++--- tests/neg/{i19531a => i19351a}/Macro.scala | 0 tests/neg/{i19531a => i19351a}/Test.scala | 0 6 files changed, 5 insertions(+), 5 deletions(-) rename tests/neg/{i19531.check => i19351.check} (64%) rename tests/neg/{i19531 => i19351}/A.scala (100%) rename tests/neg/{i19531 => i19351}/B.scala (100%) rename tests/neg/{i19531a.check => i19351a.check} (77%) rename tests/neg/{i19531a => i19351a}/Macro.scala (100%) rename tests/neg/{i19531a => i19351a}/Test.scala (100%) diff --git a/tests/neg/i19531.check b/tests/neg/i19351.check similarity index 64% rename from tests/neg/i19531.check rename to tests/neg/i19351.check index 017946e2a4b9..a9ee10510b32 100644 --- a/tests/neg/i19531.check +++ b/tests/neg/i19351.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i19531/A.scala:3:35 -------------------------------------------------------------------------------- +-- 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/i19531/A.scala from which the macro is called + |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/i19531/A.scala b/tests/neg/i19351/A.scala similarity index 100% rename from tests/neg/i19531/A.scala rename to tests/neg/i19351/A.scala diff --git a/tests/neg/i19531/B.scala b/tests/neg/i19351/B.scala similarity index 100% rename from tests/neg/i19531/B.scala rename to tests/neg/i19351/B.scala diff --git a/tests/neg/i19531a.check b/tests/neg/i19351a.check similarity index 77% rename from tests/neg/i19531a.check rename to tests/neg/i19351a.check index 009807dd41fb..3c1353811f3d 100644 --- a/tests/neg/i19531a.check +++ b/tests/neg/i19351a.check @@ -1,8 +1,8 @@ --- Error: tests/neg/i19531a/Test.scala:8:34 ---------------------------------------------------------------------------- +-- 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/i19531a/Test.scala from which the macro is called --- [E046] Cyclic Error: tests/neg/i19531a/Test.scala:8:46 -------------------------------------------------------------- + |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 diff --git a/tests/neg/i19531a/Macro.scala b/tests/neg/i19351a/Macro.scala similarity index 100% rename from tests/neg/i19531a/Macro.scala rename to tests/neg/i19351a/Macro.scala diff --git a/tests/neg/i19531a/Test.scala b/tests/neg/i19351a/Test.scala similarity index 100% rename from tests/neg/i19531a/Test.scala rename to tests/neg/i19351a/Test.scala