diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 9ae095ae9bca..45cc3406a736 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -234,6 +234,8 @@ private sealed trait XSettings: def isTruthy(using Context) = XmixinForceForwarders.value == "true" def isAtLeastJunit(using Context) = isTruthy || XmixinForceForwarders.value == "junit" } + + val XmacroSettings: Setting[List[String]] = MultiStringSetting("-Xmacro-settings", "setting1,setting2,..settingN", "List of settings which exposed to the macros") end XSettings /** -Y "Forking" as in forked tongue or "Private" settings */ diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 3de7f0bfc670..b36a0570666a 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -78,6 +78,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object CompilationInfo extends CompilationInfoModule: def isWhileTyping: Boolean = !ctx.isAfterTyper + def XmacroSettings: List[String] = ctx.settings.XmacroSettings.value end CompilationInfo extension (expr: Expr[Any]) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 87e273b786ec..cfb6cf3f65c7 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -207,6 +207,8 @@ class CompilationTests { compileFile("tests/run-custom-args/defaults-serizaliable-no-forwarders.scala", defaultOptions and "-Xmixin-force-forwarders:false"), compileFilesInDir("tests/run-custom-args/erased", defaultOptions.and("-language:experimental.erasedDefinitions")), compileFilesInDir("tests/run-custom-args/fatal-warnings", defaultOptions.and("-Xfatal-warnings")), + compileDir("tests/run-custom-args/Xmacro-settings/simple", defaultOptions.and("-Xmacro-settings:one,two,three")), + compileDir("tests/run-custom-args/Xmacro-settings/compileTimeEnv", defaultOptions.and("-Xmacro-settings:a,b=1,c.b.a=x.y.z=1,myLogger.level=INFO")), compileFilesInDir("tests/run-deep-subtype", allowDeepSubtypes), compileFilesInDir("tests/run", defaultOptions.and("-Ysafe-init")) ).checkRuns() diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index a375709844db..f5decee041f4 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -238,6 +238,15 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * This will be true when the macro is used in a transparent inline. */ def isWhileTyping: Boolean + + /** Expose macro-specific settings as a list of strings. + * Settings can be set from command line with help of -Xmacro-settings options. + * + * These will be used to expand any transparent macros or any non-transparent macro that is forced to expand while expanding the transparent macro. + * Non-transparent macros are not guaranteed to be expanded with the same set of settings. + */ + @experimental + def XmacroSettings: List[String] } diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 338826343885..097a82ff2ef0 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -17,6 +17,8 @@ object MiMaFilters { ProblemFilters.exclude[MissingClassProblem]("scala.compiletime.ops.float$"), ProblemFilters.exclude[MissingClassProblem]("scala.compiletime.ops.long"), ProblemFilters.exclude[MissingClassProblem]("scala.compiletime.ops.long$"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#CompilationInfoModule.XmacroSettings"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#CompilationInfoModule.XmacroSettings"), // Should have been added in 3.1.0 // These are only allowed on imports and therefore should not be present in binaries emitted before diff --git a/tests/run-custom-args/Xmacro-settings/compileTimeEnv.check b/tests/run-custom-args/Xmacro-settings/compileTimeEnv.check new file mode 100644 index 000000000000..61075d3bb6aa --- /dev/null +++ b/tests/run-custom-args/Xmacro-settings/compileTimeEnv.check @@ -0,0 +1,6 @@ +I'm a info msg +I'm a warn msg +a = [] +b = [1] +c.b.a = [x.y.z=1] +wat is not defined diff --git a/tests/run-custom-args/Xmacro-settings/compileTimeEnv/Logging.scala b/tests/run-custom-args/Xmacro-settings/compileTimeEnv/Logging.scala new file mode 100644 index 000000000000..702097671924 --- /dev/null +++ b/tests/run-custom-args/Xmacro-settings/compileTimeEnv/Logging.scala @@ -0,0 +1,35 @@ +import scala.compiletime.* +import scala.quoted.* + + +object Logging { + + // Just use your imagination for now :) + private inline val Trace = 0 + private inline val Debug = 1 + private inline val Info = 2 + private inline val Warn = 3 + + private transparent inline def chosenThreshold: Int = ${ + choosenTresholdImpl + } + + + private def choosenTresholdImpl(using Quotes):Expr[Int] = + import quotes.reflect.* + MacroEnv.getInMacro("myLogger.level") match + case Some("TRACE") => Expr(Trace) + case Some("DEBUG") => Expr(Debug) + case Some("INFO") => Expr(Info) + case Some("WARN") => Expr(Warn) + case Some(x) => report.errorAndAbort("Unsupported logging level: " + x) + case None => Expr(Trace) + + private inline def log(inline lvl: Int, inline msg: String): Unit = + inline if lvl >= chosenThreshold then println(msg) + + inline def trace(inline msg: String): Unit = log(Trace, msg) + inline def debug(inline msg: String): Unit = log(Debug, msg) + inline def info (inline msg: String): Unit = log(Info , msg) + inline def warn (inline msg: String): Unit = log(Warn , msg) +} diff --git a/tests/run-custom-args/Xmacro-settings/compileTimeEnv/MacroEnv.scala b/tests/run-custom-args/Xmacro-settings/compileTimeEnv/MacroEnv.scala new file mode 100644 index 000000000000..bde3719396aa --- /dev/null +++ b/tests/run-custom-args/Xmacro-settings/compileTimeEnv/MacroEnv.scala @@ -0,0 +1,25 @@ +import scala.quoted.* + +object MacroEnv { + + transparent inline def get(inline key:String):Option[String] = ${ + getImpl('key) + } + + def getImpl(key:Expr[String])(using Quotes):Expr[Option[String]] = { + import quotes.reflect.* + val retval = getInMacro(key.valueOrAbort) + Expr(retval) + } + + def getInMacro(key:String)(using Quotes):Option[String] = { + import quotes.reflect.* + val keyEq = key + "=" + CompilationInfo.XmacroSettings.collectFirst{ + case v if v == key => "" + case v if v.startsWith(keyEq) => + v.substring(keyEq.length) + } + } + +} diff --git a/tests/run-custom-args/Xmacro-settings/compileTimeEnv/Test.scala b/tests/run-custom-args/Xmacro-settings/compileTimeEnv/Test.scala new file mode 100644 index 000000000000..7cd9b03b7bb6 --- /dev/null +++ b/tests/run-custom-args/Xmacro-settings/compileTimeEnv/Test.scala @@ -0,0 +1,30 @@ +import scala.compiletime.* + +object Test { + import Logging.* + + def main(args: Array[String]): Unit = { + runLog() + runBasic() + } + + def runLog(): Unit = { + trace("I'm a trace msg") + debug("I'm a debug msg") + info("I'm a info msg") + warn("I'm a warn msg") + } + + def runBasic(): Unit = { + printEnv("a") + printEnv("b") + printEnv("c.b.a") + printEnv("wat") + } + + inline def printEnv(inline k: String): Unit = + inline MacroEnv.get(k) match + case Some(v) => println(s"$k = [$v]") + case None => println(k + " is not defined") + +} diff --git a/tests/run-custom-args/Xmacro-settings/simple/M1.scala b/tests/run-custom-args/Xmacro-settings/simple/M1.scala new file mode 100644 index 000000000000..1210d1abe05e --- /dev/null +++ b/tests/run-custom-args/Xmacro-settings/simple/M1.scala @@ -0,0 +1,15 @@ +package x + +import scala.quoted.* + +object M: + + inline def settingsContains(inline x:String): Boolean = ${ + settingsContainsImpl('x) + } + + def settingsContainsImpl(x:Expr[String])(using Quotes): Expr[Boolean] = + import quotes.reflect.* + val v = x.valueOrAbort + val r = CompilationInfo.XmacroSettings.contains(v) + Expr(r) diff --git a/tests/run-custom-args/Xmacro-settings/simple/Test.scala b/tests/run-custom-args/Xmacro-settings/simple/Test.scala new file mode 100644 index 000000000000..97587362c835 --- /dev/null +++ b/tests/run-custom-args/Xmacro-settings/simple/Test.scala @@ -0,0 +1,10 @@ +import x.* + +object Test { + + def main(args: Array[String]):Unit = + assert(M.settingsContains("one")) + assert(!M.settingsContains("notwo")) + assert(M.settingsContains("two")) + +}