Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error instead of StaleSymbol crash for certain cyclic macro dependencies #19549

Merged
merged 2 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/Driver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions compiler/src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions tests/neg/i19351.check
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions tests/neg/i19351/A.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//object A:
val x: Int = 1
inline def myMacro(): x.type = ${myMacroExpr} // error
def test = myMacro()

3 changes: 3 additions & 0 deletions tests/neg/i19351/B.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import scala.quoted.*
//import A.*
def myMacroExpr(using Quotes): Expr[x.type] = '{???}
12 changes: 12 additions & 0 deletions tests/neg/i19351a.check
Original file line number Diff line number Diff line change
@@ -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`
13 changes: 13 additions & 0 deletions tests/neg/i19351a/Macro.scala
Original file line number Diff line number Diff line change
@@ -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)}
15 changes: 15 additions & 0 deletions tests/neg/i19351a/Test.scala
Original file line number Diff line number Diff line change
@@ -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)))