Skip to content

Commit f4532e3

Browse files
committed
Fix #4801: WIP
1 parent c27ca82 commit f4532e3

13 files changed

+77
-67
lines changed

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

Lines changed: 24 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import core._
55
import Decorators._, Flags._, Types._, Contexts._, Symbols._, Constants._
66
import Flags._
77
import ast.Trees._
8-
import ast.{TreeTypeMap, untpd}
8+
import ast.{TreeTypeMap, tpd, untpd}
99
import util.Positions._
1010
import tasty.TreePickler.Hole
1111
import SymUtils._
@@ -16,6 +16,7 @@ import typer.Implicits.SearchFailureType
1616
import scala.collection.mutable
1717
import dotty.tools.dotc.core.StdNames._
1818
import dotty.tools.dotc.core.quoted._
19+
import dotty.tools.dotc.util.SourcePosition
1920

2021

2122
/** Translates quoted terms and types to `unpickle` method calls.
@@ -230,10 +231,6 @@ class ReifyQuotes extends MacroTransformWithImplicits {
230231
!sym.is(Param) || levelOK(sym.owner)
231232
}
232233

233-
/** Issue a "splice outside quote" error unless we are in the body of a transparent method */
234-
def spliceOutsideQuotes(pos: Position)(implicit ctx: Context): Unit =
235-
ctx.error(i"splice outside quotes", pos)
236-
237234
/** Try to heal phase-inconsistent reference to type `T` using a local type definition.
238235
* @return None if successful
239236
* @return Some(msg) if unsuccessful where `msg` is a potentially empty error message
@@ -292,7 +289,7 @@ class ReifyQuotes extends MacroTransformWithImplicits {
292289
outer.checkType(pos).foldOver(acc, tp)
293290
}
294291
else {
295-
if (tp.isTerm) spliceOutsideQuotes(pos)
292+
if (tp.isTerm) ctx.error(i"splice outside quotes", pos)
296293
tp
297294
}
298295
case tp: NamedType =>
@@ -418,14 +415,28 @@ class ReifyQuotes extends MacroTransformWithImplicits {
418415
val body1 = nested(isQuote = false).transform(splice.qualifier)
419416
body1.select(splice.name)
420417
}
421-
else if (!inQuote && level == 0 && !ctx.owner.is(Transparent)) {
422-
spliceOutsideQuotes(splice.pos)
423-
splice
424-
}
425-
else {
418+
else if (level == 1) {
426419
val (body1, quotes) = nested(isQuote = false).split(splice.qualifier)
427420
makeHole(body1, quotes, splice.tpe).withPos(splice.pos)
428421
}
422+
else if (enclosingInlineds.nonEmpty) { // level 0 in an inline call
423+
val spliceCtx = ctx.outer // drop the last `inlineContext`
424+
val pos: SourcePosition = Decorators.sourcePos(enclosingInlineds.head.pos)(spliceCtx)
425+
val evaluatedSplice = Splicer.splice(splice.qualifier, pos, macroClassLoader)(spliceCtx).withPos(splice.pos)
426+
if (ctx.reporter.hasErrors) splice else transform(evaluatedSplice)
427+
}
428+
else if (!ctx.owner.ownersIterator.exists(_.is(Transparent))) { // level 0 outside a transparent definition
429+
ctx.error(i"splice outside quotes or transparent method", splice.pos)
430+
splice
431+
}
432+
else if (Splicer.canBeSpliced(splice.qualifier)) { // level 0 inside a transparent definition
433+
nested(isQuote = false).split(splice.qualifier) // Just check PCP
434+
splice
435+
}
436+
else { // level 0 inside a transparent definition
437+
ctx.error("Malformed macro call. The contents of the ~ must call a static method and arguments must be quoted or transparent.".stripMargin, splice.pos)
438+
splice
439+
}
429440
}
430441

431442
/** Transforms the contents of a nested splice
@@ -550,39 +561,10 @@ class ReifyQuotes extends MacroTransformWithImplicits {
550561
val last = enteredSyms
551562
stats.foreach(markDef)
552563
mapOverTree(last)
553-
case Inlined(call, bindings, InlineSplice(spliced)) if !call.isEmpty =>
554-
val tree2 =
555-
if (level == 0) {
556-
val evaluatedSplice = Splicer.splice(spliced, tree.pos, macroClassLoader).withPos(tree.pos)
557-
if (ctx.reporter.hasErrors) EmptyTree
558-
else transform(cpy.Inlined(tree)(call, bindings, evaluatedSplice))
559-
}
560-
else super.transform(tree)
561-
562-
// due to value-discarding which converts an { e } into { e; () })
563-
if (tree.tpe =:= defn.UnitType) Block(tree2 :: Nil, Literal(Constant(())))
564-
else tree2
565564
case _: Import =>
566565
tree
567-
case tree: DefDef if tree.symbol.is(Macro) && level == 0 =>
568-
if (enclosingInlineds.nonEmpty)
569-
return EmptyTree // Already checked at definition site and already inlined
570-
markDef(tree)
571-
tree.rhs match {
572-
case InlineSplice(_) =>
573-
mapOverTree(enteredSyms) // Ignore output, only check PCP
574-
cpy.DefDef(tree)(rhs = defaultValue(tree.rhs.tpe))
575-
case _ =>
576-
ctx.error(
577-
"""Malformed transparent macro.
578-
|
579-
|Expected the ~ to be at the top of the RHS:
580-
| transparent def foo(x: X, ..., y: Y): Int = ~impl(x, ... '(y))
581-
|
582-
|The contents of the splice must call a static method. Arguments must be quoted or inlined.
583-
""".stripMargin, tree.rhs.pos)
584-
EmptyTree
585-
}
566+
case tree: DefDef if tree.symbol.is(Macro) && level == 0 && enclosingInlineds.nonEmpty =>
567+
EmptyTree // Already checked at definition site and already inlined
586568
case _ =>
587569
markDef(tree)
588570
checkLevel(mapOverTree(enteredSyms))
@@ -616,18 +598,6 @@ class ReifyQuotes extends MacroTransformWithImplicits {
616598
acc.select("::".toTermName).appliedToType(tpe).appliedTo(x)
617599
}
618600
}
619-
620-
/** InlineSplice is used to detect cases where the expansion
621-
* consists of a (possibly multiple & nested) block or a sole expression.
622-
*/
623-
object InlineSplice {
624-
def unapply(tree: Tree)(implicit ctx: Context): Option[Tree] = tree match {
625-
case Select(qual, _) if tree.symbol.isSplice && Splicer.canBeSpliced(qual) => Some(qual)
626-
case Block(List(stat), Literal(Constant(()))) => unapply(stat)
627-
case Block(Nil, expr) => unapply(expr)
628-
case _ => None
629-
}
630-
}
631601
}
632602
}
633603

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import dotty.tools.dotc.core.Constants.Constant
2020
import dotty.tools.dotc.tastyreflect.TastyImpl
2121

2222
import scala.util.control.NonFatal
23-
import dotty.tools.dotc.util.Positions.Position
23+
import dotty.tools.dotc.util.SourcePosition
2424

2525
import scala.reflect.ClassTag
2626

@@ -34,7 +34,7 @@ object Splicer {
3434
*
3535
* See: `ReifyQuotes`
3636
*/
37-
def splice(tree: Tree, pos: Position, classLoader: ClassLoader)(implicit ctx: Context): Tree = tree match {
37+
def splice(tree: Tree, pos: SourcePosition, classLoader: ClassLoader)(implicit ctx: Context): Tree = tree match {
3838
case Quoted(quotedTree) => quotedTree
3939
case _ =>
4040
val interpreter = new Interpreter(pos, classLoader)
@@ -70,7 +70,7 @@ object Splicer {
7070
}
7171

7272
/** Tree interpreter that evaluates the tree */
73-
private class Interpreter(pos: Position, classLoader: ClassLoader)(implicit ctx: Context) extends AbstractInterpreter {
73+
private class Interpreter(pos: SourcePosition, classLoader: ClassLoader)(implicit ctx: Context) extends AbstractInterpreter {
7474

7575
type Res = Object
7676

@@ -219,7 +219,7 @@ object Splicer {
219219
}
220220

221221
/** Exception that stops interpretation if some issue is found */
222-
private class StopInterpretation(val msg: String, val pos: Position) extends Exception
222+
private class StopInterpretation(val msg: String, val pos: SourcePosition) extends Exception
223223

224224
}
225225

tests/neg/quote-exception/Macro_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ object Macro_1 {
44
transparent def foo(transparent b: Boolean): Unit = ~fooImpl(b)
55
def fooImpl(b: Boolean): Expr[Unit] =
66
if (b) '(println("foo(true)"))
7-
else ???
7+
else ???
88
}

tests/neg/quote-macro-splice.scala

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@ import scala.quoted._
22

33
object Test {
44

5-
transparent def foo1: Int = { // error
5+
transparent def foo1: Int = {
66
println()
7-
~impl(1.toExpr)
7+
~impl(1.toExpr) // error
88
}
99

10-
transparent def foo2: Int = { // error
11-
~impl(1.toExpr)
12-
~impl(2.toExpr)
10+
transparent def foo2: Int = {
11+
~impl(1.toExpr) // error
12+
~impl(2.toExpr) // error
1313
}
1414

15-
transparent def foo3: Int = { // error
15+
transparent def foo3: Int = {
1616
val a = 1
1717
~impl('(a))
1818
}
1919

20-
transparent def foo4: Int = { // error
20+
transparent def foo4: Int = {
2121
~impl('(1))
2222
1
2323
}

tests/neg/quote-pcp-in-arg.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import scala.quoted._
2+
3+
object Foo {
4+
transparent def foo(x: Int): Int = ~bar('{ '(x); x }) // error
5+
def bar(i: Expr[Int]): Expr[Int] = i
6+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import scala.quoted._
2+
3+
object Foo {
4+
transparent def foo(): Int = ~bar(~x) // error
5+
def x: Expr[Int] = '(1)
6+
def bar(i: Int): Expr[Int] = i.toExpr
7+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import scala.quoted._
2+
3+
object Foo {
4+
transparent def foo(): Int = ~(~x) // error
5+
def x: Expr[Expr[Int]] = '( '(1) )
6+
}

tests/run/i4801.check

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
1
2+
0
3+
1

tests/run/i4801/Macros_1.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import scala.quoted._
2+
3+
object Macro {
4+
5+
transparent def foo(b: Boolean): Int = {
6+
if (b) ~bar(true)
7+
else ~bar(false)
8+
}
9+
10+
def bar(b: Boolean): Expr[Int] = if (b) '(1) else '(0)
11+
}

tests/run/i4801/Test_2.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Test {
2+
def main(args: Array[String]): Unit = {
3+
println(Macro.foo(true))
4+
println(Macro.foo(false))
5+
val x: Boolean = true
6+
println(Macro.foo(x))
7+
}
8+
}

tests/run-with-compiler/tasty-positioned/quoted_1.scala renamed to tests/run/tasty-positioned/quoted_1.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ object Positioned {
1414

1515
def impl[T](x: Expr[T])(implicit ev: Type[T], tasty: Tasty): Expr[Positioned[T]] = {
1616
import tasty.{Position => _, _}
17-
implicit val toolbox: scala.quoted.Toolbox = dotty.tools.dotc.quoted.Toolbox.make
1817
val pos = x.toTasty.pos
1918

2019
x.toTasty match {

0 commit comments

Comments
 (0)