Skip to content

Commit 89ec44c

Browse files
Backport "Error instead of StaleSymbol crash for certain cyclic macro dependencies" to LTS (#20867)
Backports #19549 to the LTS branch. PR submitted by the release tooling. [skip ci]
2 parents 56cb527 + a69ac87 commit 89ec44c

File tree

9 files changed

+68
-3
lines changed

9 files changed

+68
-3
lines changed

compiler/src/dotty/tools/dotc/Driver.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ class Driver {
5454
if (ctx.settings.XprintSuspension.value)
5555
report.echo(i"compiling suspended $suspendedUnits%, %")
5656
val run1 = compiler.newRun
57-
for unit <- suspendedUnits do unit.suspended = false
58-
run1.compileUnits(suspendedUnits)
57+
run1.compileSuspendedUnits(suspendedUnits)
5958
finish(compiler, run1)(using MacroClassLoader.init(ctx.fresh))
6059

6160
protected def initCtx: Context = (new ContextBase).initialCtx

compiler/src/dotty/tools/dotc/Run.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,17 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
357357
compiling = false
358358
}
359359

360+
private var myCompilingSuspended: Boolean = false
361+
362+
/** Is this run started via a compilingSuspended? */
363+
def isCompilingSuspended: Boolean = myCompilingSuspended
364+
365+
/** Compile units `us` which were suspended in a previous run */
366+
def compileSuspendedUnits(us: List[CompilationUnit]): Unit =
367+
myCompilingSuspended = true
368+
for unit <- us do unit.suspended = false
369+
compileUnits(us)
370+
360371
/** Enter top-level definitions of classes and objects contained in source file `file`.
361372
* The newly added symbols replace any previously entered symbols.
362373
* If `typeCheck = true`, also run typer on the compilation unit, and set

compiler/src/dotty/tools/dotc/core/Denotations.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,10 @@ object Denotations {
945945
}
946946

947947
def staleSymbolError(using Context): Nothing =
948-
throw new StaleSymbol(staleSymbolMsg)
948+
if symbol.isPackageObject && ctx.run != null && ctx.run.nn.isCompilingSuspended then
949+
throw TypeError(em"Cyclic macro dependency; macro refers to a toplevel symbol in ${symbol.source} from which the macro is called")
950+
else
951+
throw new StaleSymbol(staleSymbolMsg)
949952

950953
def staleSymbolMsg(using Context): String = {
951954
def ownerMsg = this match {

tests/neg/i19351.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- Error: tests/neg/i19351/A.scala:3:35 --------------------------------------------------------------------------------
2+
3 | inline def myMacro(): x.type = ${myMacroExpr} // error
3+
| ^
4+
|Cyclic macro dependency; macro refers to a toplevel symbol in tests/neg/i19351/A.scala from which the macro is called

tests/neg/i19351/A.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//object A:
2+
val x: Int = 1
3+
inline def myMacro(): x.type = ${myMacroExpr} // error
4+
def test = myMacro()
5+

tests/neg/i19351/B.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import scala.quoted.*
2+
//import A.*
3+
def myMacroExpr(using Quotes): Expr[x.type] = '{???}

tests/neg/i19351a.check

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- Error: tests/neg/i19351a/Test.scala:8:34 ----------------------------------------------------------------------------
2+
8 |inline def not(b: Bool): Bool = ${notMacro('b)} // error // error
3+
| ^
4+
|Cyclic macro dependency; macro refers to a toplevel symbol in tests/neg/i19351a/Test.scala from which the macro is called
5+
-- [E046] Cyclic Error: tests/neg/i19351a/Test.scala:8:46 --------------------------------------------------------------
6+
8 |inline def not(b: Bool): Bool = ${notMacro('b)} // error // error
7+
| ^
8+
| Cyclic reference involving method $anonfun
9+
|
10+
| Run with -explain-cyclic for more details.
11+
|
12+
| longer explanation available when compiling with `-explain`

tests/neg/i19351a/Macro.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Macro class
2+
package test
3+
4+
import scala.quoted.*
5+
6+
def notMacro(bool: Expr[Bool])(using Quotes): Expr[Bool] =
7+
'{$bool(False, True)}
8+
9+
def showMacro(bool: Expr[Bool])(using Quotes): Expr[String] =
10+
'{$bool("TRUE", "FALSE")}
11+
12+
def foldMacro[T: Type](bool: Expr[Bool], t: Expr[T], f: Expr[T])(using Quotes): Expr[T] =
13+
'{$bool($t, $f)}

tests/neg/i19351a/Test.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//Test class
2+
package test
3+
4+
type Bool = [R] => (R, R) => R
5+
val True: Bool = [R] => (t: R, _: R) => t
6+
val False: Bool = [R] => (_: R, f: R) => f
7+
8+
inline def not(b: Bool): Bool = ${notMacro('b)} // error // error
9+
inline def show(b: Bool): String = ${showMacro('b)}
10+
//inline def not(b: Bool): Bool = ${foldMacro('b, 'False, 'True)}
11+
//inline def show(b: Bool): String = ${foldMacro('b, '{"TRUE"}, '{"FALSE"})}
12+
13+
14+
@main def testing =
15+
println(show(not(True)))

0 commit comments

Comments
 (0)