Skip to content

Commit 67355f5

Browse files
committed
WIP Expand macros in typer
1 parent 6c0cce7 commit 67355f5

File tree

7 files changed

+82
-24
lines changed

7 files changed

+82
-24
lines changed

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

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -122,25 +122,22 @@ object Staging {
122122
if (splice1.isType) splice1
123123
else addSpliceCast(splice1)
124124
}
125-
else if (enclosingInlineds.nonEmpty) { // level 0 in an inlined call
126-
val spliceCtx = ctx.outer // drop the last `inlineContext`
127-
val pos: SourcePosition = spliceCtx.source.atSpan(enclosingInlineds.head.span)
128-
val evaluatedSplice = Splicer.splice(splice.qualifier, pos, macroClassLoader)(spliceCtx).withSpan(splice.span)
129-
if (ctx.reporter.hasErrors) splice else transform(evaluatedSplice)
130-
}
131-
else if (!ctx.owner.isInlineMethod) { // level 0 outside an inline method
132-
ctx.error(i"splice outside quotes or inline method", splice.sourcePos)
133-
splice
134-
}
135-
else if (Splicer.canBeSpliced(splice.qualifier)) { // level 0 inside an inline definition
136-
transform(splice.qualifier)(spliceContext) // Just check PCP
137-
splice
138-
}
139-
else { // level 0 inside an inline definition
140-
ctx.error(
141-
"Malformed macro call. The contents of the ~ must call a static method and arguments must be quoted or inline.",
142-
splice.sourcePos)
143-
splice
125+
else {
126+
assert(!enclosingInlineds.nonEmpty, "unexpanded macro")
127+
if (!ctx.owner.isInlineMethod) { // level 0 outside an inline method
128+
ctx.error(i"splice outside quotes or inline method", splice.sourcePos)
129+
splice
130+
}
131+
else if (Splicer.canBeSpliced(splice.qualifier)) { // level 0 inside an inline definition
132+
transform(splice.qualifier)(spliceContext) // Just check PCP
133+
splice
134+
}
135+
else { // level 0 inside an inline definition
136+
ctx.error(
137+
"Malformed macro call. The contents of the ~ must call a static method and arguments must be quoted or inline.",
138+
splice.sourcePos)
139+
splice
140+
}
144141
}
145142
}
146143

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

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package dotty.tools
22
package dotc
33
package typer
44

5-
import ast._
5+
import ast.{TreeInfo, tpd, _}
66
import Trees._
77
import core._
88
import Flags._
@@ -26,7 +26,8 @@ import collection.mutable
2626
import reporting.trace
2727
import util.Spans.Span
2828
import util.SourcePosition
29-
import ast.TreeInfo
29+
import dotty.tools.dotc.core.quoted.Spliced
30+
import dotty.tools.dotc.transform.{Splicer, TreeMapWithStages}
3031

3132
object Inliner {
3233
import tpd._
@@ -106,7 +107,7 @@ object Inliner {
106107
else if (enclosingInlineds.length < ctx.settings.XmaxInlines.value) {
107108
val body = bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors
108109
if (ctx.reporter.hasErrors) tree
109-
else new Inliner(tree, body).inlined(pt)
110+
else new Inliner(tree, body).inlined(pt, tree.sourcePos)
110111
}
111112
else
112113
errorTree(
@@ -359,7 +360,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
359360
}
360361

361362
/** The Inlined node representing the inlined call */
362-
def inlined(pt: Type): Tree = {
363+
def inlined(pt: Type, sourcePos: SourcePosition): Tree = {
363364

364365
if (callTypeArgs.length == 1)
365366
if (inlinedMethod == defn.Compiletime_constValue) {
@@ -467,8 +468,13 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
467468
// The normalized bindings collected in `bindingsBuf`
468469
bindingsBuf.transform(reducer.normalizeBinding(_)(inlineCtx))
469470

471+
// Evaluate the top level ~ if it is a macro
472+
val expansion0 =
473+
if (call.symbol.is(Macro) && TreeMapWithStages.level == 0) expandMacro(expansion, sourcePos)
474+
else expansion
475+
470476
// Run a typing pass over the inlined tree. See InlineTyper for details.
471-
val expansion1 = inlineTyper.typed(expansion, pt)(inlineCtx)
477+
val expansion1 = inlineTyper.typed(expansion0, pt)(inlineCtx)
472478

473479
if (ctx.settings.verbose.value) {
474480
inlining.println(i"to inline = $rhsToInline")
@@ -487,6 +493,23 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
487493
}
488494
}
489495

496+
private def expandMacro(expansion: Tree, pos: SourcePosition) = {
497+
// TODO cache macro classloader
498+
val urls = ctx.settings.classpath.value.split(java.io.File.pathSeparatorChar).map(cp => java.nio.file.Paths.get(cp).toUri.toURL)
499+
val macroClassLoader = new java.net.URLClassLoader(urls, getClass.getClassLoader)
500+
501+
new TreeMap() {
502+
override def transform(tree1: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree1 match {
503+
case Spliced(splicedBody) =>
504+
val evaluatedSplice = Splicer.splice(splicedBody, pos, macroClassLoader).withSpan(tree1.span)
505+
if (ctx.reporter.hasErrors) EmptyTree
506+
else evaluatedSplice
507+
case _ =>
508+
super.transform(tree1)
509+
}
510+
}.transform(expansion)
511+
}
512+
490513
/** A utility object offering methods for rewriting inlined code */
491514
object reducer {
492515

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import scala.quoted._
2+
3+
object Macros {
4+
inline def defaultOf(inline str: String) <: Any = ~defaultOfImpl(str)
5+
def defaultOfImpl(str: String): Expr[Any] = str match {
6+
case "int" => '(1)
7+
case "string" => '("a")
8+
}
9+
}

tests/neg/quote-whitebox/Test_2.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import Macros._
2+
3+
object Test {
4+
def main(args: Array[String]): Unit = {
5+
val a: String = defaultOf("int") // error
6+
val b: Int = defaultOf("string") // error
7+
}
8+
}

tests/run/quote-whitebox.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
1
2+
a
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import scala.quoted._
2+
3+
object Macros {
4+
inline def defaultOf(inline str: String) <: Any = ~defaultOfImpl(str)
5+
def defaultOfImpl(str: String): Expr[Any] = str match {
6+
case "int" => '(1)
7+
case "string" => '("a")
8+
}
9+
}

tests/run/quote-whitebox/Test_2.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Macros._
2+
3+
object Test {
4+
def main(args: Array[String]): Unit = {
5+
val a: Int = defaultOf("int")
6+
val b: String = defaultOf("string")
7+
println(a)
8+
println(b)
9+
}
10+
}

0 commit comments

Comments
 (0)