Skip to content

Refactor type splice logic #9381

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 type 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.
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -891,19 +891,19 @@ 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))` */
def appliedToArgs(args: List[Tree])(using Context): Apply =
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:
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
21 changes: 12 additions & 9 deletions compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -267,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)
Expand Down
35 changes: 24 additions & 11 deletions compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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
* '{
Expand Down Expand Up @@ -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.spliceType))
else
val splice = ref(defn.InternalQuoted_exprSplice).appliedToType(tree.tpe).appliedTo(body)
transformSplice(body, splice)

case tree: DefDef if tree.symbol.is(Macro) && level == 0 =>
// Shrink size of the tree. The methods have already been inlined.
Expand Down
15 changes: 12 additions & 3 deletions compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 _ =>
Expand All @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
4 changes: 2 additions & 2 deletions library/src-bootstrapped/scala/quoted/Type.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down