Skip to content

Commit 5d21dd9

Browse files
committed
Move Staging check into typer
1 parent 28c382b commit 5d21dd9

File tree

7 files changed

+88
-63
lines changed

7 files changed

+88
-63
lines changed

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,14 +1325,9 @@ object desugar {
13251325
val desugared = tree match {
13261326
case SymbolLit(str) =>
13271327
Literal(Constant(scala.Symbol(str)))
1328-
case Quote(t) =>
1329-
if (t.isType)
1330-
TypeApply(ref(defn.QuotedType_applyR), List(t))
1331-
else
1332-
Apply(ref(defn.QuotedExpr_applyR), t)
1333-
case Splice(expr) =>
1328+
case Splice(expr) => // TODO move to typer and track level
13341329
Select(expr, nme.splice)
1335-
case TypSplice(expr) =>
1330+
case TypSplice(expr) => // TODO move to typer and track level
13361331
Select(expr, tpnme.splice)
13371332
case InterpolatedString(id, segments) =>
13381333
val strs = segments map {

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

Lines changed: 29 additions & 49 deletions
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

Lines changed: 0 additions & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 46 additions & 2 deletions
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
@@ -1553,7 +1554,39 @@ class Typer extends Namer
15531554
if (sym.isInlineMethod) rhsCtx = rhsCtx.addMode(Mode.InlineableBody)
15541555
val rhs1 = typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(rhsCtx)
15551556

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

15581591
if (sym.isConstructor && !sym.isPrimaryConstructor)
15591592
for (param <- tparams1 ::: vparamss1.flatten)
@@ -1928,6 +1961,16 @@ class Typer extends Namer
19281961
}
19291962
}
19301963

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

tests/neg/quote-this-b.scala

Lines changed: 10 additions & 0 deletions
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

Lines changed: 0 additions & 3 deletions
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)