From f4f33049f90c7a6de376de4cebc7dadaa6f36591 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Jul 2020 14:59:08 +0200 Subject: [PATCH 1/4] Refactor type splice logic Specialize the matching and handling of term/type splices. This avoids unnecessary operations on either. --- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 23 +++++++----- .../dotc/transform/PCPCheckAndHeal.scala | 17 +++++---- .../tools/dotc/transform/ReifyQuotes.scala | 35 +++++++++++++------ .../dotc/transform/TreeMapWithStages.scala | 15 ++++++-- .../src/dotty/tools/dotc/typer/Inliner.scala | 4 +++ 5 files changed, 65 insertions(+), 29 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 07a5b15f3f1a..5b64762bb54d 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -879,15 +879,22 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => /** Extractors for splices */ object Spliced { - /** Extracts the content of a spliced tree. - * The result can be the contents of a term or type splice, which - * will return a term or type tree respectively. + /** Extracts the content of a spliced expresion tree. + * The result can be the contents of a term splice, which + * will return a term tree. */ - def unapply(tree: tpd.Tree)(using Context): Option[tpd.Tree] = tree match { - case tree: tpd.Apply if tree.symbol.isSplice => Some(tree.args.head) - case tree: tpd.Select if tree.symbol.isSplice => Some(tree.qualifier) - case _ => None - } + def unapply(tree: tpd.Apply)(using Context): Option[tpd.Tree] = + if tree.symbol.isSplice then Some(tree.args.head) else None + } + + /** Extractors for splices */ + object SplicedType { + /** Extracts the content of a spliced type tree. + * The result can be the contents of a type splice, which + * will return a type tree. + */ + def unapply(tree: tpd.Select)(using Context): Option[tpd.Tree] = + if tree.symbol.isSplice then Some(tree.qualifier) else None } /** Extractor for not-null assertions. diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala index 191a5f286024..1b3fd44b02af 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -123,25 +123,28 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( * - If inside inlined code, expand the macro code. * - If inside of a macro definition, check the validity of the macro. */ - protected def transformSplice(body: Tree, splice: Tree)(using Context): Tree = { + protected def transformSplice(body: Tree, splice: Apply)(using Context): Tree = { val body1 = transform(body)(using spliceContext) - splice match { - case Apply(fun @ TypeApply(_, _ :: Nil), _) if splice.isTerm => + splice.fun match { + case fun @ TypeApply(_, _ :: Nil) => // Type of the splice itsel must also be healed // internal.Quoted.expr[F[T]](... T ...) --> internal.Quoted.expr[F[$t]](... T ...) val tp = healType(splice.sourcePos)(splice.tpe.widenTermRefExpr) cpy.Apply(splice)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), body1 :: Nil) - case Apply(f @ Apply(fun @ TypeApply(_, _), qctx :: Nil), _) if splice.isTerm => + case f @ Apply(fun @ TypeApply(_, _), qctx :: Nil) => // Type of the splice itsel must also be healed // internal.Quoted.expr[F[T]](... T ...) --> internal.Quoted.expr[F[$t]](... T ...) val tp = healType(splice.sourcePos)(splice.tpe.widenTermRefExpr) cpy.Apply(splice)(cpy.Apply(f)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), qctx :: Nil), body1 :: Nil) - case splice: Select => - val tagRef = getQuoteTypeTags.getTagRef(splice.qualifier.tpe.asInstanceOf[TermRef]) - ref(tagRef).withSpan(splice.span) } } + protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree = { + val body1 = transform(body)(using spliceContext) + val tagRef = getQuoteTypeTags.getTagRef(splice.qualifier.tpe.asInstanceOf[TermRef]) + ref(tagRef).withSpan(splice.span) + } + /** Check that annotations do not contain quotes and and that splices are valid */ private def checkAnnotations(tree: Tree)(using Context): Unit = tree match diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index e5efcee697ff..1213a6e73474 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -187,13 +187,10 @@ class ReifyQuotes extends MacroTransform { * and make a hole from these parts. Otherwise issue an error, unless we * are in the body of an inline method. */ - protected def transformSplice(body: Tree, splice: Tree)(using Context): Tree = + protected def transformSplice(body: Tree, splice: Apply)(using Context): Tree = if (level > 1) { val body1 = nested(isQuote = false).transform(body)(using spliceContext) - splice match { - case splice: Apply => cpy.Apply(splice)(splice.fun, body1 :: Nil) - case splice: Select => cpy.Select(splice)(body1, splice.name) - } + cpy.Apply(splice)(splice.fun, body1 :: Nil) } else { assert(level == 1, "unexpected top splice outside quote") @@ -204,10 +201,26 @@ class ReifyQuotes extends MacroTransform { // enclosing quote. Any intemediate splice will add it's own Inlined node and cancel it before splicig the lifted tree. // Note that lifted trees are not necessarily expressions and that Inlined nodes are expected to be expressions. // For example we can have a lifted tree containing the LHS of an assignment (see tests/run-with-compiler/quote-var.scala). - if (splice.isType || outer.embedded.isLiftedSymbol(body.symbol)) hole + if (outer.embedded.isLiftedSymbol(body.symbol)) hole else Inlined(EmptyTree, Nil, hole).withSpan(splice.span) } + /** If inside a quote, split the body of the splice into a core and a list of embedded quotes + * and make a hole from these parts. Otherwise issue an error, unless we + * are in the body of an inline method. + */ + protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree = + if (level > 1) { + val body1 = nested(isQuote = false).transform(body)(using spliceContext) + cpy.Select(splice)(body1, splice.name) + } + else { + assert(level == 1, "unexpected top splice outside quote") + val (body1, quotes) = nested(isQuote = false).splitSplice(body)(using spliceContext) + val tpe = outer.embedded.getHoleType(body, splice) + makeHole(splice.isTerm, body1, quotes, tpe).withSpan(splice.span) + } + /** Transforms the contents of a nested splice * Assuming * '{ @@ -356,11 +369,11 @@ class ReifyQuotes extends MacroTransform { capturers(body.symbol)(body) case tree: RefTree if isCaptured(tree.symbol, level) => val body = capturers(tree.symbol).apply(tree) - val splice: Tree = - if (tree.isType) body.select(tpnme.splice) - else ref(defn.InternalQuoted_exprSplice).appliedToType(tree.tpe).appliedTo(body) - - transformSplice(body, splice) + if (tree.isType) + transformSpliceType(body, body.select(tpnme.splice)) + else + val splice = ref(defn.InternalQuoted_exprSplice).appliedToType(tree.tpe).appliedTo(body).asInstanceOf[Apply] + transformSplice(body, splice) case tree: DefDef if tree.symbol.is(Macro) && level == 0 => // Shrink size of the tree. The methods have already been inlined. diff --git a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala index 823c6b290a16..6020ca757643 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala @@ -71,8 +71,11 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap case quote: TypeApply => cpy.TypeApply(quote)(quote.fun, body :: Nil) } - /** Transform the splice `splice` which contains the spliced `body`. */ - protected def transformSplice(body: Tree, splice: Tree)(using Context): Tree + /** Transform the expression splice `splice` which contains the spliced `body`. */ + protected def transformSplice(body: Tree, splice: Apply)(using Context): Tree + + /** Transform the typee splice `splice` which contains the spliced `body`. */ + protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree override def transform(tree: Tree)(using Context): Tree = if (tree.source != ctx.source && tree.source.exists) @@ -94,7 +97,7 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap tree match { case Apply(Select(Quoted(quotedTree), _), _) if quotedTree.isType => dropEmptyBlocks(quotedTree) match - case Spliced(t) => + case SplicedType(t) => // '[ x.$splice ] --> x transform(t) case _ => @@ -121,6 +124,12 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap } finally inQuoteOrSplice = old + case tree @ SplicedType(splicedTree) => + val old = inQuoteOrSplice + inQuoteOrSplice = true + try transformSpliceType(splicedTree, tree) + finally inQuoteOrSplice = old + case Block(stats, _) => val last = enteredSyms stats.foreach(markDef) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 77d652c7d663..137946f5d133 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1450,6 +1450,10 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { level -= 1 try apply(syms, body) finally level += 1 + case SplicedType(body) => + level -= 1 + try apply(syms, body) + finally level += 1 case _ => foldOver(syms, tree) } From 6e4e35324798ed7bf245ece8981dec3ebf229dad Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Jul 2020 16:30:27 +0200 Subject: [PATCH 2/4] Replace `scala.quoted.Type.$splice` with `T` --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 2 +- compiler/src/dotty/tools/dotc/core/StdNames.scala | 2 +- compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala | 4 ++-- compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala | 2 +- compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala | 4 ++-- library/src-bootstrapped/scala/quoted/Type.scala | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 3e8fc27e9bab..f125773aa16a 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -710,7 +710,7 @@ class Definitions { @tu lazy val InternalQuotedType_unapply: Symbol = InternalQuotedTypeModule.requiredMethod(nme.unapply) @tu lazy val QuotedTypeClass: ClassSymbol = ctx.requiredClass("scala.quoted.Type") - @tu lazy val QuotedType_splice: Symbol = QuotedTypeClass.requiredType(tpnme.splice) + @tu lazy val QuotedType_splice: Symbol = QuotedTypeClass.requiredType(tpnme.spliceType) @tu lazy val QuotedTypeModule: Symbol = QuotedTypeClass.companionModule diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index c732043ff3df..87a1b6e25161 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -578,7 +578,7 @@ object StdNames { val setSymbol: N = "setSymbol" val setType: N = "setType" val setTypeSignature: N = "setTypeSignature" - val splice: N = "$splice" + val spliceType: N = "T" val standardInterpolator: N = "standardInterpolator" val staticClass : N = "staticClass" val staticModule : N = "staticModule" diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala index 1b3fd44b02af..d371454acc8a 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -270,13 +270,13 @@ object PCPCheckAndHeal { private def mkTagSymbolAndAssignType(spliced: TermRef): TypeDef = { val splicedTree = tpd.ref(spliced).withSpan(span) - val rhs = splicedTree.select(tpnme.splice).withSpan(span) + val rhs = splicedTree.select(tpnme.spliceType).withSpan(span) val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs, EmptyTree) val local = ctx.newSymbol( owner = ctx.owner, name = UniqueName.fresh((splicedTree.symbol.name.toString + "$_").toTermName).toTypeName, flags = Synthetic, - info = TypeAlias(splicedTree.tpe.select(tpnme.splice)), + info = TypeAlias(splicedTree.tpe.select(tpnme.spliceType)), coord = span).asType local.addAnnotation(Annotation(defn.InternalQuoted_QuoteTypeTagAnnot)) ctx.typeAssigner.assignType(untpd.TypeDef(local.name, alias), local) diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index 1213a6e73474..5854c7b679d2 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -370,7 +370,7 @@ class ReifyQuotes extends MacroTransform { case tree: RefTree if isCaptured(tree.symbol, level) => val body = capturers(tree.symbol).apply(tree) if (tree.isType) - transformSpliceType(body, body.select(tpnme.splice)) + transformSpliceType(body, body.select(tpnme.spliceType)) else val splice = ref(defn.InternalQuoted_exprSplice).appliedToType(tree.tpe).appliedTo(body).asInstanceOf[Apply] transformSplice(body, splice) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 58ef38076324..b6ec34215410 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -170,9 +170,9 @@ trait QuotesAndSplices { typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuotedMatcher_patternTypeAnnot.typeRef)).withSpan(tree.expr.span))) val pat = typedPattern(tree.expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))( using spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx))) - pat.select(tpnme.splice) + pat.select(tpnme.spliceType) else - typedSelect(untpd.Select(tree.expr, tpnme.splice), pt)(using spliceContext).withSpan(tree.span) + typedSelect(untpd.Select(tree.expr, tpnme.spliceType), pt)(using spliceContext).withSpan(tree.span) } private def checkSpliceOutsideQuote(tree: untpd.Tree)(using Context): Unit = diff --git a/library/src-bootstrapped/scala/quoted/Type.scala b/library/src-bootstrapped/scala/quoted/Type.scala index 0e9656383937..4a25af61f543 100644 --- a/library/src-bootstrapped/scala/quoted/Type.scala +++ b/library/src-bootstrapped/scala/quoted/Type.scala @@ -3,8 +3,8 @@ package scala.quoted import scala.quoted.show.SyntaxHighlight /** Quoted type (or kind) `T` */ -abstract class Type[T <: AnyKind] private[scala] { - type `$splice` = T +abstract class Type[X <: AnyKind] private[scala] { + type T = X /** Show a source code like representation of this type without syntax highlight */ def show(using qctx: QuoteContext): String = From dd2690772d6aea3bfa04524e01b90a82aa91e4fe Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Jul 2020 16:52:37 +0200 Subject: [PATCH 3/4] Update compiler/src/dotty/tools/dotc/ast/TreeInfo.scala Co-authored-by: Fengyun Liu --- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 5b64762bb54d..7c678f8b0094 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -887,7 +887,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => if tree.symbol.isSplice then Some(tree.args.head) else None } - /** Extractors for splices */ + /** Extractors for type splices */ object SplicedType { /** Extracts the content of a spliced type tree. * The result can be the contents of a type splice, which From 88630aed951161e5fd7011568d23c076a8a11634 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 20 Jul 2020 09:51:36 +0200 Subject: [PATCH 4/4] Return Apply from appliedTo --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 6 +++--- compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 18a8d3f4e836..efcbc5c8900d 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -891,11 +891,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { selectWithSig(sym.name, sym.signature) /** A unary apply node with given argument: `tree(arg)` */ - def appliedTo(arg: Tree)(using Context): Tree = + def appliedTo(arg: Tree)(using Context): Apply = appliedToArgs(arg :: Nil) /** An apply node with given arguments: `tree(arg, args0, ..., argsN)` */ - def appliedTo(arg: Tree, args: Tree*)(using Context): Tree = + def appliedTo(arg: Tree, args: Tree*)(using Context): Apply = appliedToArgs(arg :: args.toList) /** An apply node with given argument list `tree(args(0), ..., args(args.length - 1))` */ @@ -903,7 +903,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Apply(tree, args) /** An applied node that accepts only varargs as arguments */ - def appliedToVarargs(args: List[Tree], tpt: Tree)(using Context): Tree = + def appliedToVarargs(args: List[Tree], tpt: Tree)(using Context): Apply = appliedTo(repeated(args, tpt)) /** The current tree applied to given argument lists: diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index 5854c7b679d2..ad8c5776bc60 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -372,7 +372,7 @@ class ReifyQuotes extends MacroTransform { if (tree.isType) transformSpliceType(body, body.select(tpnme.spliceType)) else - val splice = ref(defn.InternalQuoted_exprSplice).appliedToType(tree.tpe).appliedTo(body).asInstanceOf[Apply] + val splice = ref(defn.InternalQuoted_exprSplice).appliedToType(tree.tpe).appliedTo(body) transformSplice(body, splice) case tree: DefDef if tree.symbol.is(Macro) && level == 0 =>