From 9c2992f4636ff79ad43db99793fcdb8407266f36 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 2 Mar 2022 10:50:28 +0100 Subject: [PATCH] Only make the staged parts of a quote inlineable Contents of splices do not need to be transformed as they will be evaluated before the code is inlined. Therefore we only want to make code at staging level 1 or grater inlineable. If the quote is in an inline method, we also need to make the contents of the splices inlineable (i.e. for any level). Fix #14603 --- .../tools/dotc/typer/PrepareInlineable.scala | 16 ++++++++++++++-- tests/pos-macros/i14603.scala | 9 +++++++++ tests/pos-macros/i14603b.scala | 17 +++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 tests/pos-macros/i14603.scala create mode 100644 tests/pos-macros/i14603b.scala diff --git a/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala index 48fc1c70ded0..5d45d1dce714 100644 --- a/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala @@ -17,6 +17,7 @@ import NameKinds.{InlineAccessorName, UniqueInlineName} import NameOps._ import Annotations._ import transform.{AccessProxies, PCPCheckAndHeal, Splicer} +import transform.SymUtils.* import config.Printers.inlining import util.Property import dotty.tools.dotc.transform.TreeMapWithStages._ @@ -61,13 +62,17 @@ object PrepareInlineable { * * Constant vals don't need accessors since they are inlined in FirstTransform. * Inline methods don't need accessors since they are inlined in Typer. + * + * When creating accessors for staged/quoted code we only need to create accessors + * for the code that is staged. This excludes code at level 0 (except if it is inlined). */ def needsAccessor(sym: Symbol)(using Context): Boolean = sym.isTerm && (sym.isOneOf(AccessFlags) || sym.privateWithin.exists) && !sym.isContainedIn(inlineSym) && !(sym.isStableMember && sym.info.widenTermRefExpr.isInstanceOf[ConstantType]) && - !sym.isInlineMethod + !sym.isInlineMethod && + (Inliner.inInlineMethod || StagingContext.level > 0) def preTransform(tree: Tree)(using Context): Tree @@ -79,7 +84,14 @@ object PrepareInlineable { } override def transform(tree: Tree)(using Context): Tree = - postTransform(super.transform(preTransform(tree))) + inContext(stagingContext(tree)) { + postTransform(super.transform(preTransform(tree))) + } + + private def stagingContext(tree: Tree)(using Context): Context = tree match + case tree: Apply if tree.symbol.isQuote => StagingContext.quoteContext + case tree: Apply if tree.symbol.isExprSplice => StagingContext.spliceContext + case _ => ctx } /** Direct approach: place the accessor with the accessed symbol. This has the diff --git a/tests/pos-macros/i14603.scala b/tests/pos-macros/i14603.scala new file mode 100644 index 000000000000..098c313e5104 --- /dev/null +++ b/tests/pos-macros/i14603.scala @@ -0,0 +1,9 @@ +import scala.quoted.* + +class Macro(using qctx: Quotes): // Anti-pattern: put Quotes in a field + import qctx.reflect._ + + def apply: Expr[Unit] = '{ + println("in quote") + ${ val a: Term = '{ println("in nested quote") }.asTerm; ??? } + } diff --git a/tests/pos-macros/i14603b.scala b/tests/pos-macros/i14603b.scala new file mode 100644 index 000000000000..92e254471d45 --- /dev/null +++ b/tests/pos-macros/i14603b.scala @@ -0,0 +1,17 @@ +import scala.language.experimental.macros +import scala.quoted.* + +class Runtime + +class Macro(using qctx: Quotes) { // Anti-pattern: put Quotes in a field + import qctx.reflect._ + + def apply[A: Type](x: Expr[A]): Expr[Unit] = { + '{ + val rt: Runtime = ??? + ${Block(doExprs('{ rt }.asTerm), '{ () }.asTerm).asExprOf[Unit]} + } + } + + private def doExprs(rt: Term): List[Term] = Nil +}