Skip to content

Commit 7f4bd1c

Browse files
committed
Move Staging check into typer
1 parent b0c10f0 commit 7f4bd1c

File tree

7 files changed

+88
-63
lines changed

7 files changed

+88
-63
lines changed

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

+2-7
Original file line numberDiff line numberDiff line change
@@ -1313,14 +1313,9 @@ object desugar {
13131313
val desugared = tree match {
13141314
case SymbolLit(str) =>
13151315
Literal(Constant(scala.Symbol(str)))
1316-
case Quote(t) =>
1317-
if (t.isType)
1318-
TypeApply(ref(defn.QuotedType_applyR), List(t))
1319-
else
1320-
Apply(ref(defn.QuotedExpr_applyR), t)
1321-
case Splice(expr) =>
1316+
case Splice(expr) => // TODO move to typer and track level
13221317
Select(expr, nme.splice)
1323-
case TypSplice(expr) =>
1318+
case TypSplice(expr) => // TODO move to typer and track level
13241319
Select(expr, tpnme.splice)
13251320
case InterpolatedString(id, segments) =>
13261321
val strs = segments map {

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

+29-49
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,6 @@ class Staging extends MacroTransform {
3737
import tpd._
3838
import Staging._
3939

40-
/** Classloader used for loading macros */
41-
private[this] var myMacroClassLoader: java.lang.ClassLoader = _
42-
private def macroClassLoader(implicit ctx: Context): ClassLoader = {
43-
if (myMacroClassLoader == null) {
44-
val urls = ctx.settings.classpath.value.split(java.io.File.pathSeparatorChar).map(cp => java.nio.file.Paths.get(cp).toUri.toURL)
45-
myMacroClassLoader = new java.net.URLClassLoader(urls, getClass.getClassLoader)
46-
}
47-
myMacroClassLoader
48-
}
49-
5040
override def phaseName: String = Staging.name
5141

5242
override def allowsImplicitSearch: Boolean = true
@@ -88,39 +78,30 @@ class Staging extends MacroTransform {
8878
new PCPCheckAndHeal(ctx).transform(tree)
8979
}
9080

91-
private class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(ictx) {
9281

93-
override def transform(tree: Tree)(implicit ctx: Context): Tree = {
94-
reporting.trace(i"PCPTransformer.transform $tree at $level", show = true) {
95-
tree match {
96-
case tree: DefDef if tree.symbol.is(Macro) =>
97-
if (level > 0) {
98-
super.transform(tree) // Ignore output, only check PCP
99-
EmptyTree // Already inlined
100-
}
101-
else if (enclosingInlineds.nonEmpty) {
102-
EmptyTree // Already checked at definition site and already inlined
103-
}
104-
else tree.rhs match {
105-
case InlineSplice(_) =>
106-
super.transform(tree) // Ignore output, only check PCP
107-
tree
108-
case _ =>
109-
ctx.error(
110-
"""Malformed macro.
111-
|
112-
|Expected the ~ to be at the top of the RHS:
113-
| inline def foo(inline x: X, ..., y: Y): Int = ~impl(x, ... '(y))
114-
|
115-
| * The contents of the splice must call a static method
116-
| * All arguments must be quoted or inline
117-
""".stripMargin, tree.rhs.sourcePos)
118-
tree
119-
}
120-
case _ =>
121-
checkLevel(super.transform(tree))
122-
}
82+
}
83+
84+
object Staging {
85+
val name: String = "staging"
86+
}
87+
88+
class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(ictx) {
89+
import tpd._
90+
import PCPCheckAndHeal._
91+
92+
/** Classloader used for loading macros */
93+
private[this] var myMacroClassLoader: java.lang.ClassLoader = _
94+
private def macroClassLoader(implicit ctx: Context): ClassLoader = {
95+
if (myMacroClassLoader == null) {
96+
val urls = ctx.settings.classpath.value.split(java.io.File.pathSeparatorChar).map(cp => java.nio.file.Paths.get(cp).toUri.toURL)
97+
myMacroClassLoader = new java.net.URLClassLoader(urls, getClass.getClassLoader)
12398
}
99+
myMacroClassLoader
100+
}
101+
102+
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
103+
case tree: DefDef if tree.symbol.is(Inline) && level > 0 => EmptyTree
104+
case _ => checkLevel(super.transform(tree))
124105
}
125106

126107
/** Transform quoted trees while maintaining phase correctness */
@@ -307,10 +288,16 @@ class Staging extends MacroTransform {
307288
}
308289
}
309290

291+
292+
}
293+
294+
object PCPCheckAndHeal {
295+
import tpd._
296+
310297
/** InlineSplice is used to detect cases where the expansion
311298
* consists of a (possibly multiple & nested) block or a sole expression.
312299
*/
313-
private object InlineSplice {
300+
object InlineSplice {
314301
def unapply(tree: Tree)(implicit ctx: Context): Option[Tree] = tree match {
315302
case Spliced(code) if Splicer.canBeSpliced(code) => Some(code)
316303
case Block(List(stat), Literal(Constant(()))) => unapply(stat)
@@ -319,11 +306,4 @@ class Staging extends MacroTransform {
319306
case _ => None
320307
}
321308
}
322-
323309
}
324-
325-
}
326-
327-
object Staging {
328-
val name: String = "staging"
329-
}

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

-1
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,6 @@ class Namer { typer: Typer =>
815815
case original: untpd.DefDef if sym.isInlineMethod =>
816816
PrepareInlineable.registerInlineInfo(
817817
sym,
818-
original.rhs,
819818
implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs
820819
)(localContext(sym))
821820
case _ =>

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ object PrepareInlineable {
219219
* to have the inline method as owner.
220220
*/
221221
def registerInlineInfo(
222-
inlined: Symbol, originalBody: untpd.Tree, treeExpr: Context => Tree)(implicit ctx: Context): Unit = {
222+
inlined: Symbol, treeExpr: Context => Tree)(implicit ctx: Context): Unit = {
223223
inlined.unforcedAnnotation(defn.BodyAnnot) match {
224224
case Some(ann: ConcreteBodyAnnotation) =>
225225
case Some(ann: LazyBodyAnnotation) if ann.isEvaluated =>

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

+46-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import EtaExpansion.etaExpand
2727
import util.Spans._
2828
import util.common._
2929
import util.Property
30-
import Applications.{ExtMethodApply, wrapDefs, productSelectorTypes}
30+
import Applications.{ExtMethodApply, productSelectorTypes, wrapDefs}
3131

3232
import collection.mutable
3333
import annotation.tailrec
@@ -36,6 +36,7 @@ import util.Stats.{record, track}
3636
import config.Printers.{gadts, typr}
3737
import rewrites.Rewrites.patch
3838
import NavigateAST._
39+
import dotty.tools.dotc.transform.{PCPCheckAndHeal, Staging, TreeMapWithStages}
3940
import transform.SymUtils._
4041
import transform.TypeUtils._
4142
import reporting.trace
@@ -1552,7 +1553,39 @@ class Typer extends Namer
15521553
if (sym.isInlineMethod) rhsCtx = rhsCtx.addMode(Mode.InlineableBody)
15531554
val rhs1 = typedExpr(ddef.rhs, tpt1.tpe)(rhsCtx)
15541555

1555-
if (sym.isInlineMethod) PrepareInlineable.registerInlineInfo(sym, ddef.rhs, _ => rhs1)
1556+
if (sym.isInlineMethod) {
1557+
if (ctx.phase.isTyper) {
1558+
import PCPCheckAndHeal.InlineSplice
1559+
import TreeMapWithStages._
1560+
var isMacro = false
1561+
new TreeMapWithStages(freshStagingContext) {
1562+
override protected def transformSplice(splice: tpd.Select)(implicit ctx: Context): tpd.Tree = {
1563+
isMacro = true
1564+
splice
1565+
}
1566+
}.transform(rhs1)
1567+
1568+
if (isMacro) {
1569+
sym.setFlag(Macro)
1570+
if (TreeMapWithStages.level == 0)
1571+
rhs1 match {
1572+
case InlineSplice(_) =>
1573+
new PCPCheckAndHeal(freshStagingContext).transform(rhs1) // Ignore output, only check PCP
1574+
case _ =>
1575+
ctx.error(
1576+
"""Malformed macro.
1577+
|
1578+
|Expected the ~ to be at the top of the RHS:
1579+
| inline def foo(inline x: X, ..., y: Y): Int = ~impl(x, ... '(y))
1580+
|
1581+
| * The contents of the splice must call a static method
1582+
| * All arguments must be quoted or inline
1583+
""".stripMargin, ddef.sourcePos)
1584+
}
1585+
}
1586+
}
1587+
PrepareInlineable.registerInlineInfo(sym, _ => rhs1)
1588+
}
15561589

15571590
if (sym.isConstructor && !sym.isPrimaryConstructor)
15581591
for (param <- tparams1 ::: vparamss1.flatten)
@@ -1927,6 +1960,16 @@ class Typer extends Namer
19271960
}
19281961
}
19291962

1963+
/** Translate `'(expr)`/`'{ expr* }` into `scala.quoted.Expr.apply(expr)` and `'[T]` into `scala.quoted.Type.apply[T]`
1964+
* while tracking the quotation level in the context.
1965+
*/
1966+
def typedQuote(tree: untpd.Quote, pt: Type)(implicit ctx: Context): Tree = track("typedQuote") {
1967+
if (tree.t.isType)
1968+
typedTypeApply(untpd.TypeApply(untpd.ref(defn.QuotedType_applyR), List(tree.t)), pt)(TreeMapWithStages.quoteContext).withSpan(tree.span)
1969+
else
1970+
typedApply(untpd.Apply(untpd.ref(defn.QuotedExpr_applyR), tree.t), pt)(TreeMapWithStages.quoteContext).withSpan(tree.span)
1971+
}
1972+
19301973
/** Retrieve symbol attached to given tree */
19311974
protected def retrieveSym(tree: untpd.Tree)(implicit ctx: Context): Symbol = tree.removeAttachment(SymOfTree) match {
19321975
case Some(sym) =>
@@ -2017,6 +2060,7 @@ class Typer extends Namer
20172060
case tree: untpd.InfixOp if ctx.mode.isExpr => typedInfixOp(tree, pt)
20182061
case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt)
20192062
case untpd.EmptyTree => tpd.EmptyTree
2063+
case tree: untpd.Quote => typedQuote(tree, pt)
20202064
case _ => typedUnadapted(desugar(tree), pt, locked)
20212065
}
20222066

tests/neg/quote-this-b.scala

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import scala.quoted._
2+
3+
class Foo {
4+
inline def k(): Unit = ${ Foo.impl[Any](this) } // error
5+
inline def l(that: Foo): Unit = ${ Foo.impl[Any](that) } // error
6+
}
7+
8+
object Foo {
9+
def impl[T](x: Any): Expr[Unit] = '{}
10+
}

tests/neg/quote-this.scala

-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ class Foo {
1919
'that // error
2020
}) }
2121

22-
inline def k(): Unit = ${ Foo.impl[Any](this) } // error
23-
inline def l(that: Foo): Unit = ${ Foo.impl[Any](that) } // error
24-
2522
}
2623

2724
object Foo {

0 commit comments

Comments
 (0)