Skip to content

Commit 3a1143b

Browse files
Merge pull request #11655 from dotty-staging/add--Xcheck-macros
Add -Xcheck-macros scalac option
2 parents c7054c2 + 1fe06cb commit 3a1143b

File tree

7 files changed

+57
-51
lines changed

7 files changed

+57
-51
lines changed

community-build/src/scala/dotty/communitybuild/projects.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ final case class SbtCommunityProject(
129129

130130
object SbtCommunityProject:
131131
def scalacOptions = List(
132-
"-Ycheck:macros",
132+
"-Xcheck-macros",
133133
"-Ycheck-init",
134134
)
135135

@@ -458,7 +458,7 @@ object projects:
458458
project = "verify",
459459
sbtTestCommand = "verifyJVM/test",
460460
sbtDocCommand = "verifyJVM/doc",
461-
scalacOptions = SbtCommunityProject.scalacOptions.filter(_ != "-Ycheck:macros") // TODO enable -Ycheck:macros
461+
scalacOptions = SbtCommunityProject.scalacOptions.filter(_ != "-Xcheck-macros") // TODO enable -Xcheck-macros
462462
)
463463

464464
lazy val discipline = SbtCommunityProject(

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ class ScalaSettings extends Settings.SettingGroup with CommonScalaSettings {
150150
val XimportSuggestionTimeout: Setting[Int] = IntSetting("-Ximport-suggestion-timeout", "Timeout (in ms) for searching for import suggestions when errors are reported.", 8000)
151151
val Xsemanticdb: Setting[Boolean] = BooleanSetting("-Xsemanticdb", "Store information in SemanticDB.", aliases = List("-Ysemanticdb"))
152152
val Xtarget: Setting[String] = ChoiceSetting("-Xtarget", "target", "Emit bytecode for the specified version of the Java platform. This might produce bytecode that will break at runtime. When on JDK 9+, consider -release as a safer alternative.", supportedTargetVersions, "", aliases = List("--Xtarget"))
153+
val XcheckMacros: Setting[Boolean] = BooleanSetting("-Xcheck-macros", "Check some invariants of macro generated code while expanding macros", aliases = List("--Xcheck-macros"))
153154

154155
val XmixinForceForwarders = ChoiceSetting(
155156
name = "-Xmixin-force-forwarders",

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Lines changed: 42 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ object QuotesImpl {
3636

3737
class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler, QuoteMatching:
3838

39-
private val yCheck: Boolean =
40-
ctx.settings.Ycheck.value(using ctx).exists(x => x == "all" || x == "macros")
39+
private val xCheckMacro: Boolean = ctx.settings.XcheckMacros.value
4140

4241
extension [T](self: scala.quoted.Expr[T])
4342
def show: String =
@@ -254,10 +253,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
254253
object DefDef extends DefDefModule:
255254
def apply(symbol: Symbol, rhsFn: List[List[Tree]] => Option[Term]): DefDef =
256255
withDefaultPos(tpd.DefDef(symbol.asTerm, prefss =>
257-
yCheckedOwners(yCheckValidExpr(rhsFn(prefss)), symbol).getOrElse(tpd.EmptyTree)
256+
xCheckMacroedOwners(xCheckMacroValidExpr(rhsFn(prefss)), symbol).getOrElse(tpd.EmptyTree)
258257
))
259258
def copy(original: Tree)(name: String, paramss: List[ParamClause], tpt: TypeTree, rhs: Option[Term]): DefDef =
260-
tpd.cpy.DefDef(original)(name.toTermName, paramss, tpt, yCheckedOwners(rhs, original.symbol).getOrElse(tpd.EmptyTree))
259+
tpd.cpy.DefDef(original)(name.toTermName, paramss, tpt, xCheckMacroedOwners(rhs, original.symbol).getOrElse(tpd.EmptyTree))
261260
def unapply(ddef: DefDef): (String, List[ParamClause], TypeTree, Option[Term]) =
262261
(ddef.name.toString, ddef.paramss, ddef.tpt, optional(ddef.rhs))
263262
end DefDef
@@ -283,9 +282,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
283282

284283
object ValDef extends ValDefModule:
285284
def apply(symbol: Symbol, rhs: Option[Term]): ValDef =
286-
tpd.ValDef(symbol.asTerm, yCheckedOwners(yCheckValidExpr(rhs), symbol).getOrElse(tpd.EmptyTree))
285+
tpd.ValDef(symbol.asTerm, xCheckMacroedOwners(xCheckMacroValidExpr(rhs), symbol).getOrElse(tpd.EmptyTree))
287286
def copy(original: Tree)(name: String, tpt: TypeTree, rhs: Option[Term]): ValDef =
288-
tpd.cpy.ValDef(original)(name.toTermName, tpt, yCheckedOwners(yCheckValidExpr(rhs), original.symbol).getOrElse(tpd.EmptyTree))
287+
tpd.cpy.ValDef(original)(name.toTermName, tpt, xCheckMacroedOwners(xCheckMacroValidExpr(rhs), original.symbol).getOrElse(tpd.EmptyTree))
289288
def unapply(vdef: ValDef): (String, TypeTree, Option[Term]) =
290289
(vdef.name.toString, vdef.tpt, optional(vdef.rhs))
291290

@@ -558,9 +557,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
558557

559558
object NamedArg extends NamedArgModule:
560559
def apply(name: String, arg: Term): NamedArg =
561-
withDefaultPos(tpd.NamedArg(name.toTermName, yCheckValidExpr(arg)))
560+
withDefaultPos(tpd.NamedArg(name.toTermName, xCheckMacroValidExpr(arg)))
562561
def copy(original: Tree)(name: String, arg: Term): NamedArg =
563-
tpd.cpy.NamedArg(original)(name.toTermName, yCheckValidExpr(arg))
562+
tpd.cpy.NamedArg(original)(name.toTermName, xCheckMacroValidExpr(arg))
564563
def unapply(x: NamedArg): (String, Term) =
565564
(x.name.toString, x.value)
566565
end NamedArg
@@ -582,10 +581,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
582581

583582
object Apply extends ApplyModule:
584583
def apply(fun: Term, args: List[Term]): Apply =
585-
yCheckValidExprs(args)
584+
xCheckMacroValidExprs(args)
586585
withDefaultPos(tpd.Apply(fun, args))
587586
def copy(original: Tree)(fun: Term, args: List[Term]): Apply =
588-
yCheckValidExprs(args)
587+
xCheckMacroValidExprs(args)
589588
tpd.cpy.Apply(original)(fun, args)
590589
def unapply(x: Apply): (Term, List[Term]) =
591590
(x.fun, x.args)
@@ -657,9 +656,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
657656

658657
object Typed extends TypedModule:
659658
def apply(expr: Term, tpt: TypeTree): Typed =
660-
withDefaultPos(tpd.Typed(yCheckValidExpr(expr), tpt))
659+
withDefaultPos(tpd.Typed(xCheckMacroValidExpr(expr), tpt))
661660
def copy(original: Tree)(expr: Term, tpt: TypeTree): Typed =
662-
tpd.cpy.Typed(original)(yCheckValidExpr(expr), tpt)
661+
tpd.cpy.Typed(original)(xCheckMacroValidExpr(expr), tpt)
663662
def unapply(x: Typed): (Term, TypeTree) =
664663
(x.expr, x.tpt)
665664
end Typed
@@ -681,9 +680,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
681680

682681
object Assign extends AssignModule:
683682
def apply(lhs: Term, rhs: Term): Assign =
684-
withDefaultPos(tpd.Assign(lhs, yCheckValidExpr(rhs)))
683+
withDefaultPos(tpd.Assign(lhs, xCheckMacroValidExpr(rhs)))
685684
def copy(original: Tree)(lhs: Term, rhs: Term): Assign =
686-
tpd.cpy.Assign(original)(lhs, yCheckValidExpr(rhs))
685+
tpd.cpy.Assign(original)(lhs, xCheckMacroValidExpr(rhs))
687686
def unapply(x: Assign): (Term, Term) =
688687
(x.lhs, x.rhs)
689688
end Assign
@@ -746,7 +745,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
746745
object Lambda extends LambdaModule:
747746
def apply(owner: Symbol, tpe: MethodType, rhsFn: (Symbol, List[Tree]) => Tree): Block =
748747
val meth = dotc.core.Symbols.newSymbol(owner, nme.ANON_FUN, Synthetic | Method, tpe)
749-
tpd.Closure(meth, tss => yCheckedOwners(yCheckValidExpr(rhsFn(meth, tss.head.map(withDefaultPos))), meth))
748+
tpd.Closure(meth, tss => xCheckMacroedOwners(xCheckMacroValidExpr(rhsFn(meth, tss.head.map(withDefaultPos))), meth))
750749

751750
def unapply(tree: Block): Option[(List[ValDef], Term)] = tree match {
752751
case Block((ddef @ DefDef(_, TermParamClause(params) :: Nil, _, Some(body))) :: Nil, Closure(meth, _))
@@ -766,9 +765,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
766765

767766
object If extends IfModule:
768767
def apply(cond: Term, thenp: Term, elsep: Term): If =
769-
withDefaultPos(tpd.If(yCheckValidExpr(cond), yCheckValidExpr(thenp), yCheckValidExpr(elsep)))
768+
withDefaultPos(tpd.If(xCheckMacroValidExpr(cond), xCheckMacroValidExpr(thenp), xCheckMacroValidExpr(elsep)))
770769
def copy(original: Tree)(cond: Term, thenp: Term, elsep: Term): If =
771-
tpd.cpy.If(original)(yCheckValidExpr(cond), yCheckValidExpr(thenp), yCheckValidExpr(elsep))
770+
tpd.cpy.If(original)(xCheckMacroValidExpr(cond), xCheckMacroValidExpr(thenp), xCheckMacroValidExpr(elsep))
772771
def unapply(tree: If): (Term, Term, Term) =
773772
(tree.cond, tree.thenp, tree.elsep)
774773
end If
@@ -792,10 +791,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
792791

793792
object Match extends MatchModule:
794793
def apply(selector: Term, cases: List[CaseDef]): Match =
795-
withDefaultPos(tpd.Match(yCheckValidExpr(selector), cases))
794+
withDefaultPos(tpd.Match(xCheckMacroValidExpr(selector), cases))
796795

797796
def copy(original: Tree)(selector: Term, cases: List[CaseDef]): Match =
798-
tpd.cpy.Match(original)(yCheckValidExpr(selector), cases)
797+
tpd.cpy.Match(original)(xCheckMacroValidExpr(selector), cases)
799798

800799
def unapply(x: Match): (Term, List[CaseDef]) =
801800
(x.scrutinee, x.cases)
@@ -842,9 +841,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
842841

843842
object Try extends TryModule:
844843
def apply(expr: Term, cases: List[CaseDef], finalizer: Option[Term]): Try =
845-
withDefaultPos(tpd.Try(yCheckValidExpr(expr), cases, finalizer.getOrElse(tpd.EmptyTree)))
844+
withDefaultPos(tpd.Try(xCheckMacroValidExpr(expr), cases, finalizer.getOrElse(tpd.EmptyTree)))
846845
def copy(original: Tree)(expr: Term, cases: List[CaseDef], finalizer: Option[Term]): Try =
847-
tpd.cpy.Try(original)(yCheckValidExpr(expr), cases, finalizer.getOrElse(tpd.EmptyTree))
846+
tpd.cpy.Try(original)(xCheckMacroValidExpr(expr), cases, finalizer.getOrElse(tpd.EmptyTree))
848847
def unapply(x: Try): (Term, List[CaseDef], Option[Term]) =
849848
(x.body, x.cases, optional(x.finalizer))
850849
end Try
@@ -867,9 +866,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
867866

868867
object Return extends ReturnModule:
869868
def apply(expr: Term, from: Symbol): Return =
870-
withDefaultPos(tpd.Return(yCheckValidExpr(expr), from))
869+
withDefaultPos(tpd.Return(xCheckMacroValidExpr(expr), from))
871870
def copy(original: Tree)(expr: Term, from: Symbol): Return =
872-
tpd.cpy.Return(original)(yCheckValidExpr(expr), tpd.ref(from))
871+
tpd.cpy.Return(original)(xCheckMacroValidExpr(expr), tpd.ref(from))
873872
def unapply(x: Return): (Term, Symbol) =
874873
(x.expr, x.from.symbol)
875874
end Return
@@ -891,10 +890,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
891890

892891
object Repeated extends RepeatedModule:
893892
def apply(elems: List[Term], elemtpt: TypeTree): Repeated =
894-
yCheckValidExprs(elems)
893+
xCheckMacroValidExprs(elems)
895894
withDefaultPos(tpd.SeqLiteral(elems, elemtpt))
896895
def copy(original: Tree)(elems: List[Term], elemtpt: TypeTree): Repeated =
897-
yCheckValidExprs(elems)
896+
xCheckMacroValidExprs(elems)
898897
tpd.cpy.SeqLiteral(original)(elems, elemtpt)
899898
def unapply(x: Repeated): (List[Term], TypeTree) =
900899
(x.elems, x.elemtpt)
@@ -917,9 +916,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
917916

918917
object Inlined extends InlinedModule:
919918
def apply(call: Option[Tree], bindings: List[Definition], expansion: Term): Inlined =
920-
withDefaultPos(tpd.Inlined(call.getOrElse(tpd.EmptyTree), bindings.map { case b: tpd.MemberDef => b }, yCheckValidExpr(expansion)))
919+
withDefaultPos(tpd.Inlined(call.getOrElse(tpd.EmptyTree), bindings.map { case b: tpd.MemberDef => b }, xCheckMacroValidExpr(expansion)))
921920
def copy(original: Tree)(call: Option[Tree], bindings: List[Definition], expansion: Term): Inlined =
922-
tpd.cpy.Inlined(original)(call.getOrElse(tpd.EmptyTree), bindings.asInstanceOf[List[tpd.MemberDef]], yCheckValidExpr(expansion))
921+
tpd.cpy.Inlined(original)(call.getOrElse(tpd.EmptyTree), bindings.asInstanceOf[List[tpd.MemberDef]], xCheckMacroValidExpr(expansion))
923922
def unapply(x: Inlined): (Option[Tree /* Term | TypeTree */], List[Definition], Term) =
924923
(optional(x.call), x.bindings, x.body)
925924
end Inlined
@@ -972,9 +971,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
972971

973972
object While extends WhileModule:
974973
def apply(cond: Term, body: Term): While =
975-
withDefaultPos(tpd.WhileDo(yCheckValidExpr(cond), yCheckValidExpr(body)))
974+
withDefaultPos(tpd.WhileDo(xCheckMacroValidExpr(cond), xCheckMacroValidExpr(body)))
976975
def copy(original: Tree)(cond: Term, body: Term): While =
977-
tpd.cpy.WhileDo(original)(yCheckValidExpr(cond), yCheckValidExpr(body))
976+
tpd.cpy.WhileDo(original)(xCheckMacroValidExpr(cond), xCheckMacroValidExpr(body))
978977
def unapply(x: While): (Term, Term) =
979978
(x.cond, x.body)
980979
end While
@@ -1501,7 +1500,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
15011500

15021501
object TermParamClause extends TermParamClauseModule:
15031502
def apply(params: List[ValDef]): TermParamClause =
1504-
if yCheck then
1503+
if xCheckMacro then
15051504
val implicitParams = params.count(_.symbol.is(dotc.core.Flags.Implicit))
15061505
assert(implicitParams == 0 || implicitParams == params.size, "Expected all or non of parameters to be implicit")
15071506
params
@@ -2781,26 +2780,26 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
27812780
/** Checks that all definitions in this tree have the expected owner.
27822781
* Nested definitions are ignored and assumed to be correct by construction.
27832782
*/
2784-
private def yCheckedOwners(tree: Option[Tree], owner: Symbol): tree.type =
2785-
if yCheck then
2783+
private def xCheckMacroedOwners(tree: Option[Tree], owner: Symbol): tree.type =
2784+
if xCheckMacro then
27862785
tree match
27872786
case Some(tree) =>
2788-
yCheckOwners(tree, owner)
2787+
xCheckMacroOwners(tree, owner)
27892788
case _ =>
27902789
tree
27912790

27922791
/** Checks that all definitions in this tree have the expected owner.
27932792
* Nested definitions are ignored and assumed to be correct by construction.
27942793
*/
2795-
private def yCheckedOwners(tree: Tree, owner: Symbol): tree.type =
2796-
if yCheck then
2797-
yCheckOwners(tree, owner)
2794+
private def xCheckMacroedOwners(tree: Tree, owner: Symbol): tree.type =
2795+
if xCheckMacro then
2796+
xCheckMacroOwners(tree, owner)
27982797
tree
27992798

28002799
/** Checks that all definitions in this tree have the expected owner.
28012800
* Nested definitions are ignored and assumed to be correct by construction.
28022801
*/
2803-
private def yCheckOwners(tree: Tree, owner: Symbol): Unit =
2802+
private def xCheckMacroOwners(tree: Tree, owner: Symbol): Unit =
28042803
new tpd.TreeTraverser {
28052804
def traverse(t: Tree)(using Context): Unit =
28062805
t match
@@ -2825,14 +2824,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
28252824
case _ => traverseChildren(t)
28262825
}.traverse(tree)
28272826

2828-
private def yCheckValidExprs(terms: List[Term]): terms.type =
2829-
if yCheck then terms.foreach(yCheckValidExpr)
2827+
private def xCheckMacroValidExprs(terms: List[Term]): terms.type =
2828+
if xCheckMacro then terms.foreach(xCheckMacroValidExpr)
28302829
terms
2831-
private def yCheckValidExpr(termOpt: Option[Term]): termOpt.type =
2832-
if yCheck then termOpt.foreach(yCheckValidExpr)
2830+
private def xCheckMacroValidExpr(termOpt: Option[Term]): termOpt.type =
2831+
if xCheckMacro then termOpt.foreach(xCheckMacroValidExpr)
28332832
termOpt
2834-
private def yCheckValidExpr(term: Term): term.type =
2835-
if yCheck then
2833+
private def xCheckMacroValidExpr(term: Term): term.type =
2834+
if xCheckMacro then
28362835
assert(!term.tpe.widenDealias.isInstanceOf[dotc.core.Types.MethodicType],
28372836
"Reference to a method must be eta-expanded before it is used as an expression: " + term.show)
28382837
term

compiler/src/scala/quoted/runtime/impl/ScopeException.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ object ScopeException:
1010
if scope.root != currentScope.root then
1111
throw new ScopeException(s"Cannot use $kind oustide of the macro splice `$${...}` or the scala.quoted.staging.run(...)` where it was defined")
1212

13-
val yCheck = ctx.settings.Ycheck.value(using ctx).exists(x => x == "all" || x == "macros")
14-
if yCheck && !scope.isOuterScopeOf(currentScope) then
13+
if ctx.settings.XcheckMacros.value && !scope.isOuterScopeOf(currentScope) then
1514
throw new ScopeException(
1615
if scope.atSameLocation(currentScope) then
1716
s"""Type created in a splice, extruded from that splice and then used in a subsequent evaluation of that same splice.

compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class BootstrappedOnlyCompilationTests {
2525
implicit val testGroup: TestGroup = TestGroup("compilePosMacros")
2626
aggregateTests(
2727
compileFilesInDir("tests/bench", defaultOptions.without("-Yno-deep-subtypes")),
28-
compileFilesInDir("tests/pos-macros", defaultOptions),
28+
compileFilesInDir("tests/pos-macros", defaultOptions.and("-Xcheck-macros")),
2929
compileFilesInDir("tests/pos-custom-args/semanticdb", defaultOptions.and("-Xsemanticdb")),
3030
compileDir("tests/pos-special/i7592", defaultOptions.and("-Yretain-trees")),
3131
compileDir("tests/pos-special/i11331.1", defaultOptions),
@@ -98,7 +98,7 @@ class BootstrappedOnlyCompilationTests {
9898
@Test def negMacros: Unit = {
9999
implicit val testGroup: TestGroup = TestGroup("compileNegWithCompiler")
100100
aggregateTests(
101-
compileFilesInDir("tests/neg-macros", defaultOptions),
101+
compileFilesInDir("tests/neg-macros", defaultOptions.and("-Xcheck-macros")),
102102
compileFile("tests/pos-macros/i9570.scala", defaultOptions.and("-Xfatal-warnings")),
103103
).checkExpectedErrors()
104104
}
@@ -116,9 +116,9 @@ class BootstrappedOnlyCompilationTests {
116116
@Test def runMacros: Unit = {
117117
implicit val testGroup: TestGroup = TestGroup("runMacros")
118118
aggregateTests(
119-
compileFilesInDir("tests/run-macros", defaultOptions),
119+
compileFilesInDir("tests/run-macros", defaultOptions.and("-Xcheck-macros")),
120120
compileFilesInDir("tests/run-custom-args/Yretain-trees", defaultOptions and "-Yretain-trees"),
121-
compileFilesInDir("tests/run-custom-args/run-macros-erased", defaultOptions and "-Yerased-terms"),
121+
compileFilesInDir("tests/run-custom-args/run-macros-erased", defaultOptions.and("-Yerased-terms").and("-Xcheck-macros")),
122122
)
123123
}.checkRuns()
124124

docs/docs/internals/debug-macros.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ title: "Debug Macros"
66
Complex macros may break invariants of the compiler, which leads to compiler crashes.
77
Here we list common compiler crashes and how to deal with them.
88

9+
## Enable checks
10+
11+
* Always enable `-Xcheck-macros`
12+
* May also enable `-Ycheck:all`
13+
914
## position not set
1015

1116
For this problem, here is the log that is usually shown:

docs/docs/reference/metaprogramming/macros.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ layout: doc-page
33
title: "Macros"
44
---
55

6+
> When developing macros enable `-Xcheck-macros` scalac option flag to have extra runtime checks.
7+
68
## Macros: Quotes and Splices
79

810
Macros are built on two well-known fundamental operations: quotation and

0 commit comments

Comments
 (0)