Skip to content

Commit d4e3602

Browse files
committed
Replace quoted.Expr.splice to scala.internal.Quoted.exprSplice
1 parent 09a56e5 commit d4e3602

File tree

17 files changed

+104
-48
lines changed

17 files changed

+104
-48
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -824,8 +824,11 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
824824
* The result can be the contents of a term or type splice, which
825825
* will return a term or type tree respectively.
826826
*/
827-
def unapply(tree: tpd.Select)(implicit ctx: Context): Option[tpd.Tree] =
828-
if (tree.symbol.isSplice) Some(tree.qualifier) else None
827+
def unapply(tree: tpd.Tree)(implicit ctx: Context): Option[tpd.Tree] = tree match {
828+
case tree: tpd.Apply if tree.symbol.isSplice => Some(tree.args.head)
829+
case tree: tpd.Select if tree.symbol.isSplice => Some(tree.qualifier)
830+
case _ => None
831+
}
829832
}
830833
}
831834

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,12 +704,13 @@ class Definitions {
704704

705705
lazy val QuotedExprType: TypeRef = ctx.requiredClassRef("scala.quoted.Expr")
706706
def QuotedExprClass(implicit ctx: Context): ClassSymbol = QuotedExprType.symbol.asClass
707-
lazy val QuotedExpr_splice : TermSymbol = QuotedExprClass.requiredMethod(nme.splice)
708707

709708
lazy val InternalQuotedModule: TermRef = ctx.requiredModuleRef("scala.internal.Quoted")
710709
def InternalQuotedModuleClass: Symbol = InternalQuotedModule.symbol
711710
lazy val InternalQuoted_exprQuoteR: TermRef = InternalQuotedModuleClass.requiredMethodRef("exprQuote".toTermName)
712711
def InternalQuoted_exprQuote(implicit ctx: Context): Symbol = InternalQuoted_exprQuoteR.symbol
712+
lazy val InternalQuoted_exprSpliceR: TermRef = InternalQuotedModuleClass.requiredMethodRef("exprSplice".toTermName)
713+
def InternalQuoted_exprSplice(implicit ctx: Context): Symbol = InternalQuoted_exprSpliceR.symbol
713714
lazy val InternalQuoted_typeQuoteR: TermRef = InternalQuotedModuleClass.requiredMethodRef("typeQuote".toTermName)
714715
def InternalQuoted_typeQuote(implicit ctx: Context): Symbol = InternalQuoted_typeQuoteR.symbol
715716

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,8 +328,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
328328
if (name.isTypeName) typeText(txt)
329329
else txt
330330
case tree @ Select(qual, name) =>
331-
if (tree.hasType && tree.symbol == defn.QuotedExpr_splice) keywordStr("${") ~ toTextLocal(qual) ~ keywordStr("}")
332-
else if (tree.hasType && tree.symbol == defn.QuotedType_splice) typeText("${") ~ toTextLocal(qual) ~ typeText("}")
331+
if (tree.hasType && tree.symbol == defn.QuotedType_splice) typeText("${") ~ toTextLocal(qual) ~ typeText("}")
333332
else if (qual.isType) toTextLocal(qual) ~ "#" ~ typeText(toText(name))
334333
else toTextLocal(qual) ~ ("." ~ nameIdText(tree) provided name != nme.CONSTRUCTOR)
335334
case tree: This =>
@@ -343,6 +342,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
343342
}
344343
else if (fun.hasType && fun.symbol == defn.InternalQuoted_exprQuote)
345344
keywordStr("'{") ~ toTextGlobal(args, ", ") ~ keywordStr("}")
345+
else if (fun.hasType && fun.symbol == defn.InternalQuoted_exprSplice)
346+
keywordStr("${") ~ toTextGlobal(args, ", ") ~ keywordStr("}")
346347
else
347348
toTextLocal(fun) ~ "(" ~ toTextGlobal(args, ", ") ~ ")"
348349
case tree: TypeApply =>

compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,22 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
4949
* - If inside inlined code, expand the macro code.
5050
* - If inside of a macro definition, check the validity of the macro.
5151
*/
52-
protected def transformSplice(splice: Select)(implicit ctx: Context): Tree = {
52+
protected def transformSplice(body: Tree, splice: Tree)(implicit ctx: Context): Tree = {
5353
if (level >= 1) {
54-
val body1 = transform(splice.qualifier)(spliceContext)
55-
val splice1 = cpy.Select(splice)(body1, splice.name)
54+
val body1 = transform(body)(spliceContext)
55+
val splice1 = splice match {
56+
case splice: Apply => cpy.Apply(splice)(splice.fun, body1 :: Nil)
57+
case splice: Select => cpy.Select(splice)(body1, splice.name)
58+
}
59+
5660
if (splice1.isType) splice1
5761
else addSpliceCast(splice1)
5862
}
5963
else {
6064
assert(!enclosingInlineds.nonEmpty, "unexpanded macro")
6165
assert(ctx.owner.isInlineMethod)
62-
if (Splicer.canBeSpliced(splice.qualifier)) { // level 0 inside an inline definition
63-
transform(splice.qualifier)(spliceContext) // Just check PCP
66+
if (Splicer.canBeSpliced(body)) { // level 0 inside an inline definition
67+
transform(body)(spliceContext) // Just check PCP
6468
splice
6569
}
6670
else { // level 0 inside an inline definition

compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -222,21 +222,24 @@ class ReifyQuotes extends MacroTransform {
222222
* and make a hole from these parts. Otherwise issue an error, unless we
223223
* are in the body of an inline method.
224224
*/
225-
protected def transformSplice(splice: Select)(implicit ctx: Context): Tree = {
225+
protected def transformSplice(body: Tree, splice: Tree)(implicit ctx: Context): Tree = {
226226
if (level > 1) {
227-
val body1 = nested(isQuote = false).transform(splice.qualifier)(spliceContext)
228-
body1.select(splice.name)
227+
val body1 = nested(isQuote = false).transform(body)(spliceContext)
228+
splice match {
229+
case splice: Apply => cpy.Apply(splice)(splice.fun, body1 :: Nil)
230+
case splice: Select => cpy.Select(splice)(body1, splice.name)
231+
}
229232
}
230233
else {
231234
assert(level == 1, "unexpected top splice outside quote")
232-
val (body1, quotes) = nested(isQuote = false).splitSplice(splice.qualifier)(spliceContext)
233-
val tpe = outer.embedded.getHoleType(splice)
235+
val (body1, quotes) = nested(isQuote = false).splitSplice(body)(spliceContext)
236+
val tpe = outer.embedded.getHoleType(body, splice)
234237
val hole = makeHole(body1, quotes, tpe).withSpan(splice.span)
235238
// We do not place add the inline marker for trees that where lifted as they come from the same file as their
236239
// enclosing quote. Any intemediate splice will add it's own Inlined node and cancel it before splicig the lifted tree.
237240
// Note that lifted trees are not necessarily expressions and that Inlined nodes are expected to be expressions.
238241
// For example we can have a lifted tree containing the LHS of an assignment (see tests/run-with-compiler/quote-var.scala).
239-
if (splice.isType || outer.embedded.isLiftedSymbol(splice.qualifier.symbol)) hole
242+
if (splice.isType || outer.embedded.isLiftedSymbol(body.symbol)) hole
240243
else Inlined(EmptyTree, Nil, hole).withSpan(splice.span)
241244
}
242245
}
@@ -346,14 +349,19 @@ class ReifyQuotes extends MacroTransform {
346349
override def transform(tree: Tree)(implicit ctx: Context): Tree =
347350
reporting.trace(i"Reifier.transform $tree at $level", show = true) {
348351
tree match {
349-
case TypeApply(Select(spliceTree @ Spliced(_), _), tp) if tree.symbol.isTypeCast =>
352+
case TypeApply(Select(spliceTree @ Spliced(spliced), _), tp) if tree.symbol.isTypeCast =>
353+
// TODO this branch should be removable with the new splice
350354
// Splice term which should be in the form `${x}.asInstanceOf[T]` where T is an artifact of
351355
// typer to allow pickling/unpickling phase consistent types
352-
transformSplice(spliceTree)
356+
transformSplice(spliced, spliceTree)
353357

354358
case tree: RefTree if isCaptured(tree.symbol, level) =>
355-
val t = capturers(tree.symbol).apply(tree)
356-
transformSplice(t.select(if (tree.isTerm) nme.splice else tpnme.splice))
359+
val body = capturers(tree.symbol).apply(tree)
360+
val splice: Tree =
361+
if (tree.isType) body.select(tpnme.splice)
362+
else ref(defn.InternalQuoted_exprSplice).appliedToType(tree.tpe).appliedTo(body)
363+
364+
transformSplice(body, splice)
357365

358366
case tree: DefDef if tree.symbol.is(Macro) && level == 0 =>
359367
// Shrink size of the tree. The methods have already been inlined.
@@ -396,11 +404,11 @@ object ReifyQuotes {
396404
}
397405

398406
/** Type used for the hole that will replace this splice */
399-
def getHoleType(splice: tpd.Select)(implicit ctx: Context): Type = {
407+
def getHoleType(body: tpd.Tree, splice: tpd.Tree)(implicit ctx: Context): Type = {
400408
// For most expressions the splice.tpe but there are some types that are lost by lifting
401409
// that can be recoverd from the original tree. Currently the cases are:
402410
// * Method types: the splice represents a method reference
403-
map.get(splice.qualifier.symbol).map(_.tpe.widen).getOrElse(splice.tpe)
411+
map.get(body.symbol).map(_.tpe.widen).getOrElse(splice.tpe)
404412
}
405413

406414
def isLiftedSymbol(sym: Symbol)(implicit ctx: Context): Boolean = map.contains(sym)

compiler/src/dotty/tools/dotc/transform/SymUtils.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,5 +157,5 @@ class SymUtils(val self: Symbol) extends AnyVal {
157157

158158
/** Is symbol a splice operation? */
159159
def isSplice(implicit ctx: Context): Boolean =
160-
self == defn.QuotedExpr_splice || self == defn.QuotedType_splice
160+
self == defn.InternalQuoted_exprSplice || self == defn.QuotedType_splice
161161
}

compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
6464
}
6565
}
6666

67-
/** Transform the splice `splice`. */
68-
protected def transformSplice(splice: Select)(implicit ctx: Context): Tree
67+
/** Transform the splice `splice` which contains the spliced `body`. */
68+
protected def transformSplice(body: Tree, splice: Tree)(implicit ctx: Context): Tree
6969

7070
override def transform(tree: Tree)(implicit ctx: Context): Tree = {
7171
reporting.trace(i"StagingTransformer.transform $tree at $level", show = true) {
@@ -93,7 +93,7 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
9393
case tree @ Spliced(splicedTree) =>
9494
dropEmptyBlocks(splicedTree) match {
9595
case Quoted(t) => transform(t) // ${ 'x } --> x
96-
case _ => transformSplice(tree)
96+
case _ => transformSplice(splicedTree, tree)
9797
}
9898

9999
case Block(stats, _) =>

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -954,21 +954,11 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
954954
override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = {
955955
assert(tree.hasType, tree)
956956
val qual1 = typed(tree.qualifier, selectionProto(tree.name, pt, this))
957-
val res =
958-
if (tree.symbol == defn.QuotedExpr_splice && level == 0) expandMacro(qual1, tree.span)
959-
else untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt)
957+
val res = untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt)
960958
ensureAccessible(res.tpe, tree.qualifier.isInstanceOf[untpd.Super], tree.sourcePos)
961959
res
962960
}
963961

964-
private def expandMacro(body: Tree, span: Span)(implicit ctx: Context) = {
965-
assert(level == 0)
966-
val inlinedFrom = enclosingInlineds.last
967-
val evaluatedSplice = Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)(ctx.withSource(inlinedFrom.source))
968-
if (ctx.reporter.hasErrors) EmptyTree
969-
else evaluatedSplice.withSpan(span)
970-
}
971-
972962
override def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context): Tree =
973963
typed(tree.cond, defn.BooleanType) match {
974964
case cond1 @ ConstantValue(b: Boolean) =>
@@ -987,8 +977,13 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
987977
}
988978
}
989979

990-
override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree =
991-
constToLiteral(betaReduce(super.typedApply(tree, pt)))
980+
override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
981+
constToLiteral(betaReduce(super.typedApply(tree, pt))) match {
982+
case res: Apply if res.symbol == defn.InternalQuoted_exprSplice && level == 0 =>
983+
expandMacro(res.args.head, tree.span)
984+
case res => res
985+
}
986+
}
992987

993988
override def typedMatchFinish(tree: untpd.Match, sel: Tree, wideSelType: Type, cases: List[untpd.CaseDef], pt: Type)(implicit ctx: Context) =
994989
if (!tree.isInline || ctx.owner.isInlineMethod) // don't reduce match of nested inline method yet
@@ -1145,4 +1140,13 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
11451140
inlineTermBindings(termBindings1.asInstanceOf[List[ValOrDefDef]], tree1)
11461141
}
11471142
}
1143+
1144+
private def expandMacro(body: Tree, span: Span)(implicit ctx: Context) = {
1145+
assert(level == 0)
1146+
val inlinedFrom = enclosingInlineds.last
1147+
val evaluatedSplice = Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)(ctx.withSource(inlinedFrom.source))
1148+
if (ctx.reporter.hasErrors) EmptyTree
1149+
else evaluatedSplice.withSpan(span)
1150+
}
1151+
11481152
}

compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ object PrepareInlineable {
270270

271271
var isMacro = false
272272
new TreeMapWithStages(freshStagingContext) {
273-
override protected def transformSplice(splice: tpd.Select)(implicit ctx: Context): tpd.Tree = {
273+
override protected def transformSplice(body: tpd.Tree, splice: tpd.Tree)(implicit ctx: Context): tpd.Tree = {
274274
isMacro = true
275275
splice
276276
}

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1952,7 +1952,7 @@ class Typer extends Namer
19521952
ctx.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.sourcePos)
19531953
typed(innerExpr, pt)
19541954
case expr =>
1955-
typedSelect(untpd.Select(expr, nme.splice), pt)(spliceContext).withSpan(tree.span)
1955+
typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprSpliceR), tree.expr), pt)(spliceContext).withSpan(tree.span)
19561956
}
19571957
}
19581958

@@ -1964,7 +1964,14 @@ class Typer extends Namer
19641964

19651965
private def checkSpliceOutsideQuote(tree: untpd.Tree)(implicit ctx: Context): Unit = {
19661966
if (level == 0 && !ctx.owner.isInlineMethod)
1967-
ctx.error("splice outside quotes or inline method", tree.sourcePos)
1967+
ctx.error("Splice ${...} outside quotes '{...} or inline method", tree.sourcePos)
1968+
else if (level < 0)
1969+
ctx.error(
1970+
"""Splice ${...} at level -1.
1971+
|
1972+
|Inline method may contain a splice at level 0 but the contents of this splice cannot have a splice.
1973+
|""".stripMargin, tree.sourcePos
1974+
)
19681975
}
19691976

19701977
/** Retrieve symbol attached to given tree */

library/src-bootstrapped/scala/StagedTuple.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ object StagedTuple {
168168
case Some(s) if s > MaxSpecialized =>
169169
val t = tup.as[TupleXXL]
170170
nValue match {
171-
case Some(n) if n >= 0 && n < s => '{$t.elems(${ n.toExpr })}
171+
case Some(n) if n >= 0 && n < s => '{$t.elems(${ Liftable.IntIsLiftable.toExpr(n) })}
172172
case _ => fallbackApply()
173173
}
174174
case _ => fallbackApply()
@@ -203,7 +203,7 @@ object StagedTuple {
203203
if (!specialize) '{dynamic_++[Self, That]($self, $that)}
204204
else {
205205
def genericConcat(xs: Expr[Tuple], ys: Expr[Tuple]): Expr[Tuple] =
206-
fromArrayStaged[Tuple]('{${ toArrayStaged(xs, None) } ++ ${ toArrayStaged(ys, None) }}, None)
206+
fromArrayStaged[Tuple]('{${ toArrayStaged(xs, None) } ++ (${ toArrayStaged(ys, None) }: Array[Object])}, None)
207207

208208
val res = selfSize match {
209209
case Some(0) =>

library/src-bootstrapped/scala/internal/Quoted.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ object Quoted {
88
def exprQuote[T](x: T): Expr[T] =
99
throw new Error("Internal error: this method call should have been replaced by the compiler")
1010

11+
/** A term splice is desugared by the compiler into a call to this method */
12+
def exprSplice[T](x: Expr[T]): T =
13+
throw new Error("Internal error: this method call should have been replaced by the compiler")
14+
1115
/** A type quote is desugared by the compiler into a call to this method */
1216
def typeQuote[T <: AnyKind]: Type[T] =
1317
throw new Error("Internal error: this method call should have been replaced by the compiler")

library/src-non-bootstrapped/scala/internal/Quoted.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ object Quoted {
88
def exprQuote[T](x: T): Expr[T] =
99
throw new Error("Internal error: this method call should have been replaced by the compiler")
1010

11+
/** A term splice is desugared by the compiler into a call to this method */
12+
def exprSplice[T](x: Expr[T]): T =
13+
throw new Error("Internal error: this method call should have been replaced by the compiler")
14+
1115
/** A type quote is desugared by the compiler into a call to this method */
1216
def typeQuote[T/* <: AnyKind */]: Type[T] =
1317
throw new Error("Internal error: this method call should have been replaced by the compiler")

library/src/scala/quoted/Expr.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import scala.runtime.quoted.Unpickler.Pickled
44

55
sealed abstract class Expr[+T] {
66

7-
final def `$splice`: T = throw new Error("splice should have been compiled away")
8-
97
/** Evaluate the contents of this expression and return the result.
108
*
119
* May throw a FreeVariableError on expressions that came from a macro.

tests/pos/i4774f.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ object Test {
66
'{ def y: T = $x; ${ loop('y) } }
77

88
def loop2[T](x: Expr[T])(implicit t: Type[T]): Expr[T] =
9-
'{ def y(): T = $x; ${ loop('{y()}) } }
9+
'{ def y(): T = $x; ${ loop2('{y()}) } }
1010
}

tests/pos/quote-nested.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import scala.annotation.tailrec
2+
import scala.quoted._
3+
4+
object Macro {
5+
6+
inline def foo: Unit = ${ nested() }
7+
8+
private def nested(): Expr[Unit] = '{
9+
var i = 0
10+
${
11+
val x: Expr[Double] = '{
12+
val y: Boolean = ${
13+
val z: Expr[Int] = '{i + 3}
14+
'{true}
15+
}
16+
4.2
17+
}
18+
'{}
19+
}
20+
()
21+
}
22+
}

tests/run/quote-unrolled-foreach/quoted_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ object Macro {
1515
${
1616
@tailrec def loop(j: Int, acc: Expr[Unit]): Expr[Unit] =
1717
if (j >= 0) loop(j - 1, '{ ${f('{$seq(i + ${j.toExpr})})}; $acc })
18-
else acc
18+
else acc
1919
loop(unrollSize - 1, '{})
2020
}
2121
i += ${unrollSize.toExpr}

0 commit comments

Comments
 (0)