Skip to content

Commit 3ac8c5e

Browse files
committed
Require QuoteContext warning on quotes
1 parent 3c99306 commit 3ac8c5e

File tree

101 files changed

+295
-296
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+295
-296
lines changed

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

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ object Splicer {
8080
/** Tree interpreter that evaluates the tree */
8181
private class Interpreter(pos: SourcePosition, classLoader: ClassLoader)(implicit ctx: Context) extends AbstractInterpreter {
8282

83+
def checking: Boolean = false
84+
8385
type Result = Object
8486

8587
/** Returns the interpreted result of interpreting the code a call to the symbol with default arguments.
@@ -277,6 +279,7 @@ object Splicer {
277279

278280
/** Tree interpreter that tests if tree can be interpreted */
279281
private class CheckValidMacroBody(implicit ctx: Context) extends AbstractInterpreter {
282+
def checking: Boolean = true
280283

281284
type Result = Unit
282285

@@ -312,6 +315,9 @@ object Splicer {
312315

313316
/** Abstract Tree interpreter that can interpret calls to static methods with quoted or inline arguments */
314317
private abstract class AbstractInterpreter(implicit ctx: Context) {
318+
319+
def checking: Boolean
320+
315321
type Env = Map[Name, Result]
316322
type Result
317323

@@ -370,20 +376,10 @@ object Splicer {
370376

371377
// Interpret `foo(j = x, i = y)` which it is expanded to
372378
// `val j$1 = x; val i$1 = y; foo(i = y, j = x)`
373-
case Block(stats, expr) =>
374-
var unexpected: Option[Result] = None
375-
val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match {
376-
case stat: ValDef if stat.symbol.is(Synthetic) =>
377-
accEnv.updated(stat.name, interpretTree(stat.rhs)(accEnv))
378-
case stat =>
379-
if (unexpected.isEmpty)
380-
unexpected = Some(unexpectedTree(stat))
381-
accEnv
382-
})
383-
unexpected.getOrElse(interpretTree(expr)(newEnv))
379+
case Block(stats, expr) => interpretBlock(stats, expr)
384380
case NamedArg(_, arg) => interpretTree(arg)
385381

386-
case Inlined(_, Nil, expansion) => interpretTree(expansion)
382+
case Inlined(_, bindings, expansion) => interpretBlock(bindings, expansion)
387383

388384
case Typed(expr, _) =>
389385
interpretTree(expr)
@@ -395,6 +391,19 @@ object Splicer {
395391
unexpectedTree(tree)
396392
}
397393

394+
private def interpretBlock(stats: List[Tree], expr: Tree)(implicit env: Env) = {
395+
var unexpected: Option[Result] = None
396+
val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match {
397+
case stat: ValDef if stat.symbol.is(Synthetic) || !checking =>
398+
accEnv.updated(stat.name, interpretTree(stat.rhs)(accEnv))
399+
case stat =>
400+
if (unexpected.isEmpty)
401+
unexpected = Some(unexpectedTree(stat))
402+
accEnv
403+
})
404+
unexpected.getOrElse(interpretTree(expr)(newEnv))
405+
}
406+
398407
object Call {
399408
def unapply(arg: Tree): Option[(RefTree, List[Tree])] = arg match {
400409
case Select(Call(fn, args), nme.apply) if defn.isImplicitFunctionType(fn.tpe.widenDealias.finalResultType) =>

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

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1962,15 +1962,18 @@ class Typer extends Namer
19621962
case untpd.TypSplice(innerType) if tree.isType =>
19631963
ctx.warning("Canceled splice directly inside a quote. '[ ${ XYZ } ] is equivalent to XYZ.", tree.sourcePos)
19641964
typed(innerType, pt)
1965-
case quoted if quoted.isType =>
1966-
ctx.compilationUnit.needsStaging = true
1967-
typedTypeApply(untpd.TypeApply(untpd.ref(defn.InternalQuoted_typeQuoteR), quoted :: Nil), pt)(quoteContext).withSpan(tree.span)
19681965
case quoted =>
19691966
ctx.compilationUnit.needsStaging = true
1970-
if (ctx.mode.is(Mode.Pattern) && level == 0)
1971-
typedQuotePattern(quoted, pt, tree.span)
1972-
else
1973-
typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprQuoteR), quoted), pt)(quoteContext).withSpan(tree.span)
1967+
1968+
val qctx = inferImplicitArg(defn.QuoteContextType, tree.span)
1969+
if (level == 0 && qctx.tpe.isInstanceOf[SearchFailureType])
1970+
ctx.error(missingArgMsg(qctx, defn.QuoteContextType, ""), ctx.source.atSpan(tree.span))
1971+
1972+
val tree1 =
1973+
if (quoted.isType) typedTypeApply(untpd.TypeApply(untpd.ref(defn.InternalQuoted_typeQuoteR), quoted :: Nil), pt)(quoteContext)
1974+
else if (ctx.mode.is(Mode.Pattern) && level == 0) typedQuotePattern(quoted, pt, qctx)
1975+
else typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprQuoteR), quoted), pt)(quoteContext)
1976+
tree1.withSpan(tree.span)
19741977
}
19751978
}
19761979

@@ -2019,7 +2022,7 @@ class Typer extends Namer
20192022
* ) => ...
20202023
* ```
20212024
*/
2022-
private def typedQuotePattern(quoted: untpd.Tree, pt: Type, quoteSpan: Span)(implicit ctx: Context): Tree = {
2025+
private def typedQuotePattern(quoted: untpd.Tree, pt: Type, qctx: tpd.Tree)(implicit ctx: Context): Tree = {
20232026
val exprPt = pt.baseType(defn.QuotedExprClass)
20242027
val quotedPt = if (exprPt.exists) exprPt.argTypesHi.head else defn.AnyType
20252028
val quoted1 = typedExpr(quoted, quotedPt)(quoteContext.addMode(Mode.QuotedPattern))
@@ -2068,7 +2071,7 @@ class Typer extends Namer
20682071
implicits =
20692072
ref(defn.InternalQuoted_exprQuoteR).appliedToType(shape.tpe).appliedTo(shape) ::
20702073
Literal(Constant(typeBindings.nonEmpty)) ::
2071-
implicitArgTree(defn.QuoteContextType, quoteSpan) :: Nil,
2074+
qctx :: Nil,
20722075
patterns = splicePat :: Nil,
20732076
proto = pt)
20742077
}

compiler/test-resources/repl-macros/i6007

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
scala> import scala.quoted._
12
scala> implicit def toolbox: scala.quoted.Toolbox = scala.quoted.Toolbox.make(getClass.getClassLoader)
23
def toolbox: quoted.Toolbox
3-
scala> val v = '{ (if true then Some(1) else None).map(v => v+1) }
4-
val v: quoted.Expr[Option[Int]] = Expr(<pickled tasty>)
4+
scala> def v given QuoteContext = '{ (if true then Some(1) else None).map(v => v+1) }
5+
def v given (x$1: quoted.QuoteContext): quoted.Expr[Option[Int]]
56
scala> scala.quoted.withQuoteContext(v.show)
67
val res0: String = (if (true) scala.Some.apply[scala.Int](1) else scala.None).map[scala.Int](((v: scala.Int) => v.+(1)))
78
scala> scala.quoted.run(v)

compiler/test-resources/repl/i5551

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
scala> import scala.quoted._
2-
3-
scala> def assertImpl(expr: Expr[Boolean]) = '{ if !($expr) then throw new AssertionError("failed assertion")}
4-
def assertImpl(expr: quoted.Expr[Boolean]): quoted.Expr[Unit]
5-
2+
scala> def assertImpl(expr: Expr[Boolean]) given (qctx: QuoteContext) = '{ if !($expr) then throw new AssertionError("failed assertion")}
3+
def assertImpl
4+
(expr: quoted.Expr[Boolean])
5+
given (qctx: quoted.QuoteContext): quoted.Expr[Unit]
66
scala> inline def assert(expr: => Boolean): Unit = ${ assertImpl('{expr}) }
77
def assert(expr: => Boolean): Unit
88

library/src-3.x/scala/tasty/reflect/utils/TreeUtils.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ trait TreeUtils {
1010

1111
/** Bind the `rhs` to a `val` and use it in `body` */
1212
def let(rhs: Term)(body: Ident => Term): Term = {
13+
delegate for QuoteContext = new QuoteContext(reflect)
1314
type T // TODO probably it is better to use the Sealed contruct rather than let the user create their own existential type
1415
implicit val rhsTpe: quoted.Type[T] = rhs.tpe.seal.asInstanceOf[quoted.Type[T]]
1516
val rhsExpr = rhs.seal.cast[T]

library/src-bootstrapped/scala/quoted/package.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ package object quoted {
5454
implicit object ExprOps {
5555
def (x: T) toExpr[T: Liftable] given QuoteContext: Expr[T] = the[Liftable[T]].toExpr(x)
5656

57-
def (list: List[Expr[T]]) toExprOfList[T] given Type[T]: Expr[List[T]] = list match {
57+
def (list: List[Expr[T]]) toExprOfList[T: Type] given QuoteContext: Expr[List[T]] = list match {
5858
case x :: xs => '{ $x :: ${xs.toExprOfList} }
5959
case Nil => '{ Nil }
6060
}

tests/neg-custom-args/fatal-warnings/quote-simple-hole.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
class Test {
1+
import scala.quoted.QuoteContext
2+
3+
def test given QuoteContext = {
24
val x = '{0}
35
val y = '{ // error: Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.
46
$x

tests/neg-macros/quote-complex-top-splice.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ object Test {
2424
impl(1)
2525
}
2626

27-
def impl(i: Int): Expr[Unit] = '{}
27+
def impl(i: Int) given QuoteContext: Expr[Unit] = '{}
2828

2929
}

0 commit comments

Comments
 (0)