Skip to content

Commit 2ecdf91

Browse files
committed
Disallow phase inconsitent inline parameters
1 parent 7282a9b commit 2ecdf91

File tree

63 files changed

+561
-169
lines changed

Some content is hidden

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

63 files changed

+561
-169
lines changed

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,6 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
8888
tree match {
8989
case Quoted(_) | Spliced(_) =>
9090
tree
91-
case tree: RefTree if tree.symbol.isAllOf(InlineParam) =>
92-
tree
9391
case _: This =>
9492
assert(checkSymLevel(tree.symbol, tree.tpe, tree.sourcePos).isEmpty)
9593
tree
@@ -197,10 +195,8 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
197195
case Some(l) =>
198196
l == level ||
199197
level == -1 && (
200-
// here we assume that Splicer.canBeSpliced was true before going to level -1,
201-
// this implies that all non-inline arguments are quoted and that the following two cases are checked
202-
// on inline parameters or type parameters.
203-
sym.is(Param) ||
198+
// here we assume that Splicer.checkValidMacroBody was true before going to level -1,
199+
// this implies that all arguments are quoted.
204200
sym.isClass // reference to this in inline methods
205201
)
206202
case None =>

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ object Splicer {
4949
catch {
5050
case ex: CompilationUnit.SuspendException =>
5151
throw ex
52+
case ex: scala.quoted.StopQuotedContext if ctx.reporter.hasErrors =>
53+
// errors have been emitted
54+
EmptyTree
5255
case ex: StopInterpretation =>
5356
ctx.error(ex.msg, ex.pos)
5457
EmptyTree
@@ -349,6 +352,8 @@ object Splicer {
349352
throw new StopInterpretation(sw.toString, pos)
350353
case ex: InvocationTargetException =>
351354
ex.getTargetException match {
355+
case ex: scala.quoted.StopQuotedContext =>
356+
throw ex
352357
case MissingClassDefinedInCurrentRun(sym) =>
353358
if (ctx.settings.XprintSuspension.value)
354359
ctx.echo(i"suspension triggered by a dependency on $sym", pos)

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import dotty.tools.dotc.util.Spans._
1919
import dotty.tools.dotc.util.{Property, SourcePosition}
2020
import dotty.tools.dotc.transform.SymUtils._
2121
import dotty.tools.dotc.typer.Implicits.SearchFailureType
22-
import dotty.tools.dotc.typer.Inliner
2322

2423
import scala.collection.mutable
2524
import scala.annotation.constructorOnly

library/src/scala/internal/quoted/Matcher.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,8 @@ private[quoted] object Matcher {
229229
case (While(cond1, body1), While(cond2, body2)) =>
230230
cond1 =?= cond2 && body1 =?= body2
231231

232-
case (New(tpt1), New(tpt2)) =>
233-
tpt1 =?= tpt2
232+
case (New(tpt1), New(tpt2)) if tpt1.tpe.typeSymbol == tpt2.tpe.typeSymbol =>
233+
matched
234234

235235
case (This(_), This(_)) if scrutinee.symbol == pattern.symbol =>
236236
matched

library/src/scala/quoted/Expr.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ class Expr[+T] private[scala] {
1818
*/
1919
final def getValue[U >: T](given qctx: QuoteContext, valueOf: ValueOfExpr[U]): Option[U] = valueOf(this)
2020

21+
/** Return the value of this expression.
22+
*
23+
* Emits an error error and throws if the expression does not contain a value or contains side effects.
24+
* Otherwise returns the value.
25+
*/
26+
final def value[U >: T](given qctx: QuoteContext, valueOf: ValueOfExpr[U]): U =
27+
valueOf(this).getOrElse(qctx.throwError(s"Expected a known value. \n\nThe value of: $show\ncould not be recovered using $valueOf", this))
28+
2129
/** Pattern matches `this` against `that`. Effectively performing a deep equality check.
2230
* It does the equivalent of
2331
* ```

library/src/scala/quoted/QuoteContext.scala

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class QuoteContext(val tasty: scala.tasty.Reflection) {
2222
tpe.unseal.show(syntaxHighlight)
2323
}
2424

25-
/** Report an error */
25+
/** Report an error at the position of the macro expansion */
2626
def error(msg: => String): Unit = {
2727
import tasty.{_, given}
2828
tasty.error(msg, rootPosition)(given rootContext)
@@ -34,6 +34,17 @@ class QuoteContext(val tasty: scala.tasty.Reflection) {
3434
tasty.error(msg, expr.unseal.pos)(given rootContext)
3535
}
3636

37+
/** Report an error at the position of the macro expansion and throws a StopQuotedContext */
38+
def throwError(msg: => String): Nothing = {
39+
error(msg)
40+
throw new StopQuotedContext
41+
}
42+
/** Report an error at the on the position of `expr` and throws a StopQuotedContext */
43+
def throwError(msg: => String, expr: Expr[_]): Nothing = {
44+
error(msg, expr)
45+
throw new StopQuotedContext
46+
}
47+
3748
/** Report a warning */
3849
def warning(msg: => String): Unit = {
3950
import tasty.{_, given}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package scala.quoted
2+
3+
/** Stop code generation after an error has been reported */
4+
class StopQuotedContext extends Throwable

library/src/scala/quoted/ValueOfExpr.scala

Lines changed: 252 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package scala.quoted
2+
package matching
3+
4+
/** Literal sequence of literal constant value expressions */
5+
object ValueSeq {
6+
7+
/** Matches literal sequence of literal constant value expressions and return a sequence of values.
8+
*
9+
* Usage:
10+
* ```scala
11+
* inline def sum(args: Int*): Int = ${ sumExpr('args) }
12+
* def sumExpr(argsExpr: Expr[Seq[Int]])(given QuoteContext): Expr[Int] = argsExpr match
13+
* case ValueSeq(args) =>
14+
* // args: Seq[Int]
15+
* ...
16+
* }
17+
* ```
18+
*/
19+
def unapply[T](expr: Expr[Seq[T]])(given valueOf: ValueOfExpr[T], qctx: QuoteContext): Option[Seq[T]] = expr match {
20+
case ExprSeq(elems) =>
21+
elems.foldRight(Option(List.empty[T])) { (elem, acc) =>
22+
(elem, acc) match {
23+
case (ValueOfExpr(value), Some(lst)) => Some(value :: lst)
24+
case (_, _) => None
25+
}
26+
}
27+
case _ => None
28+
}
29+
30+
}

tests/neg-macros/inline-macro-staged-interpreter/Macro_1.scala

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11

22
import scala.quoted._
33
import scala.quoted.autolift.given
4+
import scala.quoted.matching._
45

56
object E {
67

7-
inline def eval[T](inline x: E[T]): T = ${ impl(x) }
8+
inline def eval[T](inline x: E[T]): T = ${ impl('x) }
89

9-
def impl[T](x: E[T])(given QuoteContext): Expr[T] = x.lift
10+
def impl[T: Type](x: Expr[E[T]])(given QuoteContext): Expr[T] = x.value.lift
1011

12+
implicit def ev1[T: Type]: ValueOfExpr[E[T]] = new ValueOfExpr {
13+
def apply(x: Expr[E[T]])(given QuoteContext): Option[E[T]] = x match {
14+
case '{ I(${Const(n)}) } => Some(I(n).asInstanceOf[E[T]])
15+
case '{ Plus[T](${Value(x)}, ${Value(y)})(given $op) } => Some(Plus(x, y)(given Plus2.IPlus.asInstanceOf[Plus2[T]]).asInstanceOf[E[T]])
16+
}
17+
}
18+
19+
object Value {
20+
def unapply[T, U >: T](expr: Expr[T])(given ValueOfExpr[U], QuoteContext): Option[U] = expr.getValue
21+
}
1122
}
1223

1324
trait E[T] {

tests/neg-macros/quote-error-2/Macro_1.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import quoted._
22

33
object Macro_1 {
4-
inline def foo(inline b: Boolean): Unit = ${ fooImpl(b) }
5-
def fooImpl(b: Boolean)(given QuoteContext): Expr[Unit] =
6-
'{println(${msg(b)})}
4+
inline def foo(inline b: Boolean): Unit = ${ fooImpl('b) }
5+
def fooImpl(b: Expr[Boolean])(given QuoteContext): Expr[Unit] =
6+
'{println(${msg(b.value)})}
77

88
def msg(b: Boolean)(given qctx: QuoteContext): Expr[String] =
99
if (b) '{"foo(true)"}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import quoted._
22

33
object Macro_1 {
4-
inline def foo(inline b: Boolean): Unit = ${fooImpl(b)}
5-
def fooImpl(b: Boolean)(given qctx: QuoteContext): Expr[Unit] =
6-
if (b) '{println("foo(true)")}
4+
inline def foo(inline b: Boolean): Unit = ${fooImpl('b)}
5+
def fooImpl(b: Expr[Boolean])(given qctx: QuoteContext): Expr[Unit] =
6+
if (b.value) '{println("foo(true)")}
77
else { qctx.error("foo cannot be called with false"); '{ ??? } }
88
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import quoted._
22

33
object Macro_1 {
4-
inline def foo(inline b: Boolean): Unit = ${fooImpl(b)}
5-
def fooImpl(b: Boolean)(given QuoteContext): Expr[Unit] =
6-
if (b) '{println("foo(true)")}
4+
inline def foo(inline b: Boolean): Unit = ${fooImpl('b)}
5+
def fooImpl(b: Expr[Boolean])(given QuoteContext): Expr[Unit] =
6+
if (b.value) '{println("foo(true)")}
77
else ???
88
}

tests/neg-macros/quote-whitebox/Macro_1.scala

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

33
object Macros {
4-
inline def defaultOf(inline str: String) <: Any = ${ defaultOfImpl(str) }
5-
def defaultOfImpl(str: String)(given QuoteContext): Expr[Any] = str match {
4+
inline def defaultOf(inline str: String) <: Any = ${ defaultOfImpl('str) }
5+
def defaultOfImpl(str: Expr[String])(given QuoteContext): Expr[Any] = str.value match {
66
case "int" => '{1}
77
case "string" => '{"a"}
88
}

tests/neg-macros/reflect-inline/assert_1.scala

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

33
object api {
44
inline def (inline x: String).stripMargin2: String =
5-
${ stripImpl(x) }
5+
${ stripImpl('x) }
66

7-
private def stripImpl(x: String)(given qctx: QuoteContext): Expr[String] =
8-
Expr(x.stripMargin)
7+
private def stripImpl(x: Expr[String])(given qctx: QuoteContext): Expr[String] =
8+
Expr(x.value.stripMargin)
99

1010
}
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import scala.quoted._
22

33
object Test {
4-
inline def foo(inline x: Int): Int = ${fooImpl(x, 'x, '{ 'x }, '{ '{ 'x } })}
4+
inline def foo(inline x: Int): Int = ${
5+
fooImpl(
6+
x, // error
7+
'x,
8+
'{ 'x }, // error
9+
'{ '{ 'x } } // error
10+
)
11+
}
512
def fooImpl(a: Int, b: Expr[Int], c: Expr[(given QuoteContext) => Expr[Int]], d: Expr[(given QuoteContext) => Expr[(given QuoteContext) => Expr[Int]]]): Expr[Int] = ???
613
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import scala.quoted._
2+
3+
inline def old(inline x: Int): Int =
4+
${ oldImpl(x) } // error
5+
6+
private def oldImpl(x: Int): Expr[Int] = ???
7+
8+
inline def `new`(inline x: Int): Int =
9+
${ newImpl('x) }
10+
11+
private def newImpl(x: Expr[Int]): Expr[Int] = ???

tests/pos-macros/power-macro/Macro_1.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import scala.quoted._
33

44
object PowerMacro {
55

6-
inline def power(inline n: Long, x: Double) = ${powerCode(n, 'x)}
6+
inline def power(inline n: Long, x: Double) = ${powerCode('n, 'x)}
7+
8+
def powerCode(n: Expr[Long], x: Expr[Double])(given QuoteContext): Expr[Double] =
9+
powerCode(n.value, x)
710

811
def powerCode(n: Long, x: Expr[Double])(given QuoteContext): Expr[Double] =
912
if (n == 0) '{1.0}

tests/pos-macros/quote-nested-object/Macro_1.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ object Macro {
77

88
object Implementation {
99

10-
inline def plus(inline n: Int, m: Int): Int = ${ plus(n, 'm) }
10+
inline def plus(inline n: Int, m: Int): Int = ${ plus('n, 'm) }
1111

12-
def plus(n: Int, m: Expr[Int])(given QuoteContext): Expr[Int] =
13-
if (n == 0) m
12+
def plus(n: Expr[Int], m: Expr[Int])(given QuoteContext): Expr[Int] =
13+
if (n.value == 0) m
1414
else '{ ${n} + $m }
1515

1616
object Implementation2 {
1717

18-
inline def plus(inline n: Int, m: Int): Int = ${ plus(n, 'm) }
18+
inline def plus(inline n: Int, m: Int): Int = ${ plus('n, 'm) }
1919

20-
def plus(n: Int, m: Expr[Int])(given QuoteContext): Expr[Int] =
21-
if (n == 0) m
20+
def plus(n: Expr[Int], m: Expr[Int])(given QuoteContext): Expr[Int] =
21+
if (n.value == 0) m
2222
else '{ ${n} + $m }
2323
}
2424
}

tests/pos-macros/quote-whitebox-2/Macro_1.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import scala.quoted._
33

44
object Macro {
55

6-
inline def charOrString(inline str: String) <: Any = ${ impl(str) }
6+
inline def charOrString(inline str: String) <: Any = ${ impl('str) }
77

8-
def impl(str: String)(given QuoteContext) = if (str.length == 1) Expr(str.charAt(0)) else Expr(str)
8+
def impl(strExpr: Expr[String])(given QuoteContext) =
9+
val str = strExpr.value
10+
if (str.length == 1) Expr(str.charAt(0)) else Expr(str)
911

1012
}

tests/pos-staging/quote-0.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ object Macros {
1414

1515
def showExpr[T](expr: Expr[T])(given QuoteContext): Expr[String] = expr.toString
1616

17-
inline def power(inline n: Int, x: Double) = ${ powerCode(n, 'x) }
17+
inline def power(inline n: Int, x: Double) = ${ powerCode('n, 'x) }
18+
19+
def powerCode(n: Expr[Int], x: Expr[Double])(given QuoteContext): Expr[Double] =
20+
powerCode(n.value, x)
1821

1922
def powerCode(n: Int, x: Expr[Double])(given QuoteContext): Expr[Double] =
2023
if (n == 0) '{1.0}

tests/pos/quote-this.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ class Foo {
1616

1717
inline def g(): Unit = ${ Foo.impl[this.type](1) }
1818
inline def h(): Unit = ${ Foo.impl[Any]('this) }
19-
inline def i(that: Foo): Unit = ${ Foo.impl[that.type](1) }
19+
// FIXME
20+
// inline def i(that: Foo): Unit = ${ Foo.impl[that.type](1) }
2021

2122
}
2223

tests/run-macros/gestalt-type-toolbox-reflect/Macro_1.scala

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ object TypeToolbox {
4141
Expr(fields)
4242
}
4343

44-
inline def fieldIn[T](inline mem: String): String = ${fieldInImpl('[T], mem)}
45-
private def fieldInImpl(t: Type[_], mem: String)(given qctx: QuoteContext): Expr[String] = {
44+
inline def fieldIn[T](inline mem: String): String = ${fieldInImpl('[T], 'mem)}
45+
private def fieldInImpl(t: Type[_], mem: Expr[String])(given qctx: QuoteContext): Expr[String] = {
4646
import qctx.tasty.{_, given}
47-
val field = t.unseal.symbol.field(mem)
47+
val field = t.unseal.symbol.field(mem.value)
4848
Expr(if field.isNoSymbol then "" else field.name)
4949
}
5050

@@ -55,10 +55,10 @@ object TypeToolbox {
5555
Expr(fields.map(_.name).toList)
5656
}
5757

58-
inline def methodIn[T](inline mem: String): Seq[String] = ${methodInImpl('[T], mem)}
59-
private def methodInImpl(t: Type[_], mem: String)(given qctx: QuoteContext): Expr[Seq[String]] = {
58+
inline def methodIn[T](inline mem: String): Seq[String] = ${methodInImpl('[T], 'mem)}
59+
private def methodInImpl(t: Type[_], mem: Expr[String])(given qctx: QuoteContext): Expr[Seq[String]] = {
6060
import qctx.tasty.{_, given}
61-
Expr(t.unseal.symbol.classMethod(mem).map(_.name))
61+
Expr(t.unseal.symbol.classMethod(mem.value).map(_.name))
6262
}
6363

6464
inline def methodsIn[T]: Seq[String] = ${methodsInImpl('[T])}
@@ -67,10 +67,10 @@ object TypeToolbox {
6767
Expr(t.unseal.symbol.classMethods.map(_.name))
6868
}
6969

70-
inline def method[T](inline mem: String): Seq[String] = ${methodImpl('[T], mem)}
71-
private def methodImpl(t: Type[_], mem: String)(given qctx: QuoteContext): Expr[Seq[String]] = {
70+
inline def method[T](inline mem: String): Seq[String] = ${methodImpl('[T], 'mem)}
71+
private def methodImpl(t: Type[_], mem: Expr[String])(given qctx: QuoteContext): Expr[Seq[String]] = {
7272
import qctx.tasty.{_, given}
73-
Expr(t.unseal.symbol.method(mem).map(_.name))
73+
Expr(t.unseal.symbol.method(mem.value).map(_.name))
7474
}
7575

7676
inline def methods[T]: Seq[String] = ${methodsImpl('[T])}

tests/run-macros/i4734/Macro_1.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import scala.quoted.autolift.given
44

55
object Macros {
66
inline def unrolledForeach(seq: IndexedSeq[Int], f: => Int => Unit, inline unrollSize: Int): Unit = // or f: Int => Unit
7-
${ unrolledForeachImpl('seq, 'f, unrollSize) }
7+
${ unrolledForeachImpl('seq, 'f, 'unrollSize) }
8+
9+
def unrolledForeachImpl(seq: Expr[IndexedSeq[Int]], f: Expr[Int => Unit], unrollSizeExpr: Expr[Int])(given QuoteContext): Expr[Unit] =
10+
unrolledForeachImpl(seq, f, unrollSizeExpr.value)
811

912
def unrolledForeachImpl(seq: Expr[IndexedSeq[Int]], f: Expr[Int => Unit], unrollSize: Int)(given QuoteContext): Expr[Unit] = '{
1013
val size = ($seq).length

0 commit comments

Comments
 (0)