diff --git a/community-build/src/scala/dotty/communitybuild/projects.scala b/community-build/src/scala/dotty/communitybuild/projects.scala index 155d08eb9db5..0224b4b4c6b0 100644 --- a/community-build/src/scala/dotty/communitybuild/projects.scala +++ b/community-build/src/scala/dotty/communitybuild/projects.scala @@ -129,7 +129,7 @@ final case class SbtCommunityProject( object SbtCommunityProject: def scalacOptions = List( - "-Ycheck:macros", + "-Xcheck-macros", "-Ycheck-init", ) @@ -458,7 +458,7 @@ object projects: project = "verify", sbtTestCommand = "verifyJVM/test", sbtDocCommand = "verifyJVM/doc", - scalacOptions = SbtCommunityProject.scalacOptions.filter(_ != "-Ycheck:macros") // TODO enable -Ycheck:macros + scalacOptions = SbtCommunityProject.scalacOptions.filter(_ != "-Xcheck-macros") // TODO enable -Xcheck-macros ) lazy val discipline = SbtCommunityProject( diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index d30db3124c99..551ce760d95f 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -150,6 +150,7 @@ class ScalaSettings extends Settings.SettingGroup with CommonScalaSettings { val XimportSuggestionTimeout: Setting[Int] = IntSetting("-Ximport-suggestion-timeout", "Timeout (in ms) for searching for import suggestions when errors are reported.", 8000) val Xsemanticdb: Setting[Boolean] = BooleanSetting("-Xsemanticdb", "Store information in SemanticDB.", aliases = List("-Ysemanticdb")) 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")) + val XcheckMacros: Setting[Boolean] = BooleanSetting("-Xcheck-macros", "Check some invariants of macro generated code while expanding macros", aliases = List("--Xcheck-macros")) val XmixinForceForwarders = ChoiceSetting( name = "-Xmixin-force-forwarders", diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index d23e40796034..c0ef6b329728 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -36,8 +36,7 @@ object QuotesImpl { class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler, QuoteMatching: - private val yCheck: Boolean = - ctx.settings.Ycheck.value(using ctx).exists(x => x == "all" || x == "macros") + private val xCheckMacro: Boolean = ctx.settings.XcheckMacros.value extension [T](self: scala.quoted.Expr[T]) def show: String = @@ -254,10 +253,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object DefDef extends DefDefModule: def apply(symbol: Symbol, rhsFn: List[List[Tree]] => Option[Term]): DefDef = withDefaultPos(tpd.DefDef(symbol.asTerm, prefss => - yCheckedOwners(yCheckValidExpr(rhsFn(prefss)), symbol).getOrElse(tpd.EmptyTree) + xCheckMacroedOwners(xCheckMacroValidExpr(rhsFn(prefss)), symbol).getOrElse(tpd.EmptyTree) )) def copy(original: Tree)(name: String, paramss: List[ParamClause], tpt: TypeTree, rhs: Option[Term]): DefDef = - tpd.cpy.DefDef(original)(name.toTermName, paramss, tpt, yCheckedOwners(rhs, original.symbol).getOrElse(tpd.EmptyTree)) + tpd.cpy.DefDef(original)(name.toTermName, paramss, tpt, xCheckMacroedOwners(rhs, original.symbol).getOrElse(tpd.EmptyTree)) def unapply(ddef: DefDef): (String, List[ParamClause], TypeTree, Option[Term]) = (ddef.name.toString, ddef.paramss, ddef.tpt, optional(ddef.rhs)) end DefDef @@ -283,9 +282,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object ValDef extends ValDefModule: def apply(symbol: Symbol, rhs: Option[Term]): ValDef = - tpd.ValDef(symbol.asTerm, yCheckedOwners(yCheckValidExpr(rhs), symbol).getOrElse(tpd.EmptyTree)) + tpd.ValDef(symbol.asTerm, xCheckMacroedOwners(xCheckMacroValidExpr(rhs), symbol).getOrElse(tpd.EmptyTree)) def copy(original: Tree)(name: String, tpt: TypeTree, rhs: Option[Term]): ValDef = - tpd.cpy.ValDef(original)(name.toTermName, tpt, yCheckedOwners(yCheckValidExpr(rhs), original.symbol).getOrElse(tpd.EmptyTree)) + tpd.cpy.ValDef(original)(name.toTermName, tpt, xCheckMacroedOwners(xCheckMacroValidExpr(rhs), original.symbol).getOrElse(tpd.EmptyTree)) def unapply(vdef: ValDef): (String, TypeTree, Option[Term]) = (vdef.name.toString, vdef.tpt, optional(vdef.rhs)) @@ -558,9 +557,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object NamedArg extends NamedArgModule: def apply(name: String, arg: Term): NamedArg = - withDefaultPos(tpd.NamedArg(name.toTermName, yCheckValidExpr(arg))) + withDefaultPos(tpd.NamedArg(name.toTermName, xCheckMacroValidExpr(arg))) def copy(original: Tree)(name: String, arg: Term): NamedArg = - tpd.cpy.NamedArg(original)(name.toTermName, yCheckValidExpr(arg)) + tpd.cpy.NamedArg(original)(name.toTermName, xCheckMacroValidExpr(arg)) def unapply(x: NamedArg): (String, Term) = (x.name.toString, x.value) end NamedArg @@ -582,10 +581,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Apply extends ApplyModule: def apply(fun: Term, args: List[Term]): Apply = - yCheckValidExprs(args) + xCheckMacroValidExprs(args) withDefaultPos(tpd.Apply(fun, args)) def copy(original: Tree)(fun: Term, args: List[Term]): Apply = - yCheckValidExprs(args) + xCheckMacroValidExprs(args) tpd.cpy.Apply(original)(fun, args) def unapply(x: Apply): (Term, List[Term]) = (x.fun, x.args) @@ -657,9 +656,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Typed extends TypedModule: def apply(expr: Term, tpt: TypeTree): Typed = - withDefaultPos(tpd.Typed(yCheckValidExpr(expr), tpt)) + withDefaultPos(tpd.Typed(xCheckMacroValidExpr(expr), tpt)) def copy(original: Tree)(expr: Term, tpt: TypeTree): Typed = - tpd.cpy.Typed(original)(yCheckValidExpr(expr), tpt) + tpd.cpy.Typed(original)(xCheckMacroValidExpr(expr), tpt) def unapply(x: Typed): (Term, TypeTree) = (x.expr, x.tpt) end Typed @@ -681,9 +680,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Assign extends AssignModule: def apply(lhs: Term, rhs: Term): Assign = - withDefaultPos(tpd.Assign(lhs, yCheckValidExpr(rhs))) + withDefaultPos(tpd.Assign(lhs, xCheckMacroValidExpr(rhs))) def copy(original: Tree)(lhs: Term, rhs: Term): Assign = - tpd.cpy.Assign(original)(lhs, yCheckValidExpr(rhs)) + tpd.cpy.Assign(original)(lhs, xCheckMacroValidExpr(rhs)) def unapply(x: Assign): (Term, Term) = (x.lhs, x.rhs) end Assign @@ -746,7 +745,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Lambda extends LambdaModule: def apply(owner: Symbol, tpe: MethodType, rhsFn: (Symbol, List[Tree]) => Tree): Block = val meth = dotc.core.Symbols.newSymbol(owner, nme.ANON_FUN, Synthetic | Method, tpe) - tpd.Closure(meth, tss => yCheckedOwners(yCheckValidExpr(rhsFn(meth, tss.head.map(withDefaultPos))), meth)) + tpd.Closure(meth, tss => xCheckMacroedOwners(xCheckMacroValidExpr(rhsFn(meth, tss.head.map(withDefaultPos))), meth)) def unapply(tree: Block): Option[(List[ValDef], Term)] = tree match { 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 object If extends IfModule: def apply(cond: Term, thenp: Term, elsep: Term): If = - withDefaultPos(tpd.If(yCheckValidExpr(cond), yCheckValidExpr(thenp), yCheckValidExpr(elsep))) + withDefaultPos(tpd.If(xCheckMacroValidExpr(cond), xCheckMacroValidExpr(thenp), xCheckMacroValidExpr(elsep))) def copy(original: Tree)(cond: Term, thenp: Term, elsep: Term): If = - tpd.cpy.If(original)(yCheckValidExpr(cond), yCheckValidExpr(thenp), yCheckValidExpr(elsep)) + tpd.cpy.If(original)(xCheckMacroValidExpr(cond), xCheckMacroValidExpr(thenp), xCheckMacroValidExpr(elsep)) def unapply(tree: If): (Term, Term, Term) = (tree.cond, tree.thenp, tree.elsep) end If @@ -792,10 +791,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Match extends MatchModule: def apply(selector: Term, cases: List[CaseDef]): Match = - withDefaultPos(tpd.Match(yCheckValidExpr(selector), cases)) + withDefaultPos(tpd.Match(xCheckMacroValidExpr(selector), cases)) def copy(original: Tree)(selector: Term, cases: List[CaseDef]): Match = - tpd.cpy.Match(original)(yCheckValidExpr(selector), cases) + tpd.cpy.Match(original)(xCheckMacroValidExpr(selector), cases) def unapply(x: Match): (Term, List[CaseDef]) = (x.scrutinee, x.cases) @@ -842,9 +841,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Try extends TryModule: def apply(expr: Term, cases: List[CaseDef], finalizer: Option[Term]): Try = - withDefaultPos(tpd.Try(yCheckValidExpr(expr), cases, finalizer.getOrElse(tpd.EmptyTree))) + withDefaultPos(tpd.Try(xCheckMacroValidExpr(expr), cases, finalizer.getOrElse(tpd.EmptyTree))) def copy(original: Tree)(expr: Term, cases: List[CaseDef], finalizer: Option[Term]): Try = - tpd.cpy.Try(original)(yCheckValidExpr(expr), cases, finalizer.getOrElse(tpd.EmptyTree)) + tpd.cpy.Try(original)(xCheckMacroValidExpr(expr), cases, finalizer.getOrElse(tpd.EmptyTree)) def unapply(x: Try): (Term, List[CaseDef], Option[Term]) = (x.body, x.cases, optional(x.finalizer)) end Try @@ -867,9 +866,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Return extends ReturnModule: def apply(expr: Term, from: Symbol): Return = - withDefaultPos(tpd.Return(yCheckValidExpr(expr), from)) + withDefaultPos(tpd.Return(xCheckMacroValidExpr(expr), from)) def copy(original: Tree)(expr: Term, from: Symbol): Return = - tpd.cpy.Return(original)(yCheckValidExpr(expr), tpd.ref(from)) + tpd.cpy.Return(original)(xCheckMacroValidExpr(expr), tpd.ref(from)) def unapply(x: Return): (Term, Symbol) = (x.expr, x.from.symbol) end Return @@ -891,10 +890,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Repeated extends RepeatedModule: def apply(elems: List[Term], elemtpt: TypeTree): Repeated = - yCheckValidExprs(elems) + xCheckMacroValidExprs(elems) withDefaultPos(tpd.SeqLiteral(elems, elemtpt)) def copy(original: Tree)(elems: List[Term], elemtpt: TypeTree): Repeated = - yCheckValidExprs(elems) + xCheckMacroValidExprs(elems) tpd.cpy.SeqLiteral(original)(elems, elemtpt) def unapply(x: Repeated): (List[Term], TypeTree) = (x.elems, x.elemtpt) @@ -917,9 +916,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Inlined extends InlinedModule: def apply(call: Option[Tree], bindings: List[Definition], expansion: Term): Inlined = - withDefaultPos(tpd.Inlined(call.getOrElse(tpd.EmptyTree), bindings.map { case b: tpd.MemberDef => b }, yCheckValidExpr(expansion))) + withDefaultPos(tpd.Inlined(call.getOrElse(tpd.EmptyTree), bindings.map { case b: tpd.MemberDef => b }, xCheckMacroValidExpr(expansion))) def copy(original: Tree)(call: Option[Tree], bindings: List[Definition], expansion: Term): Inlined = - tpd.cpy.Inlined(original)(call.getOrElse(tpd.EmptyTree), bindings.asInstanceOf[List[tpd.MemberDef]], yCheckValidExpr(expansion)) + tpd.cpy.Inlined(original)(call.getOrElse(tpd.EmptyTree), bindings.asInstanceOf[List[tpd.MemberDef]], xCheckMacroValidExpr(expansion)) def unapply(x: Inlined): (Option[Tree /* Term | TypeTree */], List[Definition], Term) = (optional(x.call), x.bindings, x.body) end Inlined @@ -972,9 +971,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object While extends WhileModule: def apply(cond: Term, body: Term): While = - withDefaultPos(tpd.WhileDo(yCheckValidExpr(cond), yCheckValidExpr(body))) + withDefaultPos(tpd.WhileDo(xCheckMacroValidExpr(cond), xCheckMacroValidExpr(body))) def copy(original: Tree)(cond: Term, body: Term): While = - tpd.cpy.WhileDo(original)(yCheckValidExpr(cond), yCheckValidExpr(body)) + tpd.cpy.WhileDo(original)(xCheckMacroValidExpr(cond), xCheckMacroValidExpr(body)) def unapply(x: While): (Term, Term) = (x.cond, x.body) end While @@ -1501,7 +1500,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object TermParamClause extends TermParamClauseModule: def apply(params: List[ValDef]): TermParamClause = - if yCheck then + if xCheckMacro then val implicitParams = params.count(_.symbol.is(dotc.core.Flags.Implicit)) assert(implicitParams == 0 || implicitParams == params.size, "Expected all or non of parameters to be implicit") params @@ -2780,26 +2779,26 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler /** Checks that all definitions in this tree have the expected owner. * Nested definitions are ignored and assumed to be correct by construction. */ - private def yCheckedOwners(tree: Option[Tree], owner: Symbol): tree.type = - if yCheck then + private def xCheckMacroedOwners(tree: Option[Tree], owner: Symbol): tree.type = + if xCheckMacro then tree match case Some(tree) => - yCheckOwners(tree, owner) + xCheckMacroOwners(tree, owner) case _ => tree /** Checks that all definitions in this tree have the expected owner. * Nested definitions are ignored and assumed to be correct by construction. */ - private def yCheckedOwners(tree: Tree, owner: Symbol): tree.type = - if yCheck then - yCheckOwners(tree, owner) + private def xCheckMacroedOwners(tree: Tree, owner: Symbol): tree.type = + if xCheckMacro then + xCheckMacroOwners(tree, owner) tree /** Checks that all definitions in this tree have the expected owner. * Nested definitions are ignored and assumed to be correct by construction. */ - private def yCheckOwners(tree: Tree, owner: Symbol): Unit = + private def xCheckMacroOwners(tree: Tree, owner: Symbol): Unit = new tpd.TreeTraverser { def traverse(t: Tree)(using Context): Unit = t match @@ -2824,14 +2823,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler case _ => traverseChildren(t) }.traverse(tree) - private def yCheckValidExprs(terms: List[Term]): terms.type = - if yCheck then terms.foreach(yCheckValidExpr) + private def xCheckMacroValidExprs(terms: List[Term]): terms.type = + if xCheckMacro then terms.foreach(xCheckMacroValidExpr) terms - private def yCheckValidExpr(termOpt: Option[Term]): termOpt.type = - if yCheck then termOpt.foreach(yCheckValidExpr) + private def xCheckMacroValidExpr(termOpt: Option[Term]): termOpt.type = + if xCheckMacro then termOpt.foreach(xCheckMacroValidExpr) termOpt - private def yCheckValidExpr(term: Term): term.type = - if yCheck then + private def xCheckMacroValidExpr(term: Term): term.type = + if xCheckMacro then assert(!term.tpe.widenDealias.isInstanceOf[dotc.core.Types.MethodicType], "Reference to a method must be eta-expanded before it is used as an expression: " + term.show) term diff --git a/compiler/src/scala/quoted/runtime/impl/ScopeException.scala b/compiler/src/scala/quoted/runtime/impl/ScopeException.scala index c0507495cbc2..ae16d7434a18 100644 --- a/compiler/src/scala/quoted/runtime/impl/ScopeException.scala +++ b/compiler/src/scala/quoted/runtime/impl/ScopeException.scala @@ -10,8 +10,7 @@ object ScopeException: if scope.root != currentScope.root then throw new ScopeException(s"Cannot use $kind oustide of the macro splice `$${...}` or the scala.quoted.staging.run(...)` where it was defined") - val yCheck = ctx.settings.Ycheck.value(using ctx).exists(x => x == "all" || x == "macros") - if yCheck && !scope.isOuterScopeOf(currentScope) then + if ctx.settings.XcheckMacros.value && !scope.isOuterScopeOf(currentScope) then throw new ScopeException( if scope.atSameLocation(currentScope) then s"""Type created in a splice, extruded from that splice and then used in a subsequent evaluation of that same splice. diff --git a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala index e4eae741edbd..0925490e7293 100644 --- a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala @@ -25,7 +25,7 @@ class BootstrappedOnlyCompilationTests { implicit val testGroup: TestGroup = TestGroup("compilePosMacros") aggregateTests( compileFilesInDir("tests/bench", defaultOptions.without("-Yno-deep-subtypes")), - compileFilesInDir("tests/pos-macros", defaultOptions), + compileFilesInDir("tests/pos-macros", defaultOptions.and("-Xcheck-macros")), compileFilesInDir("tests/pos-custom-args/semanticdb", defaultOptions.and("-Xsemanticdb")), compileDir("tests/pos-special/i7592", defaultOptions.and("-Yretain-trees")), compileDir("tests/pos-special/i11331.1", defaultOptions), @@ -98,7 +98,7 @@ class BootstrappedOnlyCompilationTests { @Test def negMacros: Unit = { implicit val testGroup: TestGroup = TestGroup("compileNegWithCompiler") aggregateTests( - compileFilesInDir("tests/neg-macros", defaultOptions), + compileFilesInDir("tests/neg-macros", defaultOptions.and("-Xcheck-macros")), compileFile("tests/pos-macros/i9570.scala", defaultOptions.and("-Xfatal-warnings")), ).checkExpectedErrors() } @@ -116,9 +116,9 @@ class BootstrappedOnlyCompilationTests { @Test def runMacros: Unit = { implicit val testGroup: TestGroup = TestGroup("runMacros") aggregateTests( - compileFilesInDir("tests/run-macros", defaultOptions), + compileFilesInDir("tests/run-macros", defaultOptions.and("-Xcheck-macros")), compileFilesInDir("tests/run-custom-args/Yretain-trees", defaultOptions and "-Yretain-trees"), - compileFilesInDir("tests/run-custom-args/run-macros-erased", defaultOptions and "-Yerased-terms"), + compileFilesInDir("tests/run-custom-args/run-macros-erased", defaultOptions.and("-Yerased-terms").and("-Xcheck-macros")), ) }.checkRuns() diff --git a/docs/docs/internals/debug-macros.md b/docs/docs/internals/debug-macros.md index afb4c16b4369..4d5cab52c568 100644 --- a/docs/docs/internals/debug-macros.md +++ b/docs/docs/internals/debug-macros.md @@ -6,6 +6,11 @@ title: "Debug Macros" Complex macros may break invariants of the compiler, which leads to compiler crashes. Here we list common compiler crashes and how to deal with them. +## Enable checks + +* Always enable `-Xcheck-macros` +* May also enable `-Ycheck:all` + ## position not set For this problem, here is the log that is usually shown: diff --git a/docs/docs/reference/metaprogramming/macros.md b/docs/docs/reference/metaprogramming/macros.md index dd23041db205..910762730245 100644 --- a/docs/docs/reference/metaprogramming/macros.md +++ b/docs/docs/reference/metaprogramming/macros.md @@ -3,6 +3,8 @@ layout: doc-page title: "Macros" --- +> When developing macros enable `-Xcheck-macros` scalac option flag to have extra runtime checks. + ## Macros: Quotes and Splices Macros are built on two well-known fundamental operations: quotation and