Skip to content

Commit

Permalink
Disallow phase inconsitent inline parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasstucki committed Jan 22, 2020
1 parent 7282a9b commit 2ecdf91
Show file tree
Hide file tree
Showing 63 changed files with 561 additions and 169 deletions.
8 changes: 2 additions & 6 deletions compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,6 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
tree match {
case Quoted(_) | Spliced(_) =>
tree
case tree: RefTree if tree.symbol.isAllOf(InlineParam) =>
tree
case _: This =>
assert(checkSymLevel(tree.symbol, tree.tpe, tree.sourcePos).isEmpty)
tree
Expand Down Expand Up @@ -197,10 +195,8 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
case Some(l) =>
l == level ||
level == -1 && (
// here we assume that Splicer.canBeSpliced was true before going to level -1,
// this implies that all non-inline arguments are quoted and that the following two cases are checked
// on inline parameters or type parameters.
sym.is(Param) ||
// here we assume that Splicer.checkValidMacroBody was true before going to level -1,
// this implies that all arguments are quoted.
sym.isClass // reference to this in inline methods
)
case None =>
Expand Down
5 changes: 5 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/Splicer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ object Splicer {
catch {
case ex: CompilationUnit.SuspendException =>
throw ex
case ex: scala.quoted.StopQuotedContext if ctx.reporter.hasErrors =>
// errors have been emitted
EmptyTree
case ex: StopInterpretation =>
ctx.error(ex.msg, ex.pos)
EmptyTree
Expand Down Expand Up @@ -349,6 +352,8 @@ object Splicer {
throw new StopInterpretation(sw.toString, pos)
case ex: InvocationTargetException =>
ex.getTargetException match {
case ex: scala.quoted.StopQuotedContext =>
throw ex
case MissingClassDefinedInCurrentRun(sym) =>
if (ctx.settings.XprintSuspension.value)
ctx.echo(i"suspension triggered by a dependency on $sym", pos)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import dotty.tools.dotc.util.Spans._
import dotty.tools.dotc.util.{Property, SourcePosition}
import dotty.tools.dotc.transform.SymUtils._
import dotty.tools.dotc.typer.Implicits.SearchFailureType
import dotty.tools.dotc.typer.Inliner

import scala.collection.mutable
import scala.annotation.constructorOnly
Expand Down
4 changes: 2 additions & 2 deletions library/src/scala/internal/quoted/Matcher.scala
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,8 @@ private[quoted] object Matcher {
case (While(cond1, body1), While(cond2, body2)) =>
cond1 =?= cond2 && body1 =?= body2

case (New(tpt1), New(tpt2)) =>
tpt1 =?= tpt2
case (New(tpt1), New(tpt2)) if tpt1.tpe.typeSymbol == tpt2.tpe.typeSymbol =>
matched

case (This(_), This(_)) if scrutinee.symbol == pattern.symbol =>
matched
Expand Down
8 changes: 8 additions & 0 deletions library/src/scala/quoted/Expr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ class Expr[+T] private[scala] {
*/
final def getValue[U >: T](given qctx: QuoteContext, valueOf: ValueOfExpr[U]): Option[U] = valueOf(this)

/** Return the value of this expression.
*
* Emits an error error and throws if the expression does not contain a value or contains side effects.
* Otherwise returns the value.
*/
final def value[U >: T](given qctx: QuoteContext, valueOf: ValueOfExpr[U]): U =
valueOf(this).getOrElse(qctx.throwError(s"Expected a known value. \n\nThe value of: $show\ncould not be recovered using $valueOf", this))

/** Pattern matches `this` against `that`. Effectively performing a deep equality check.
* It does the equivalent of
* ```
Expand Down
13 changes: 12 additions & 1 deletion library/src/scala/quoted/QuoteContext.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class QuoteContext(val tasty: scala.tasty.Reflection) {
tpe.unseal.show(syntaxHighlight)
}

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

/** Report an error at the position of the macro expansion and throws a StopQuotedContext */
def throwError(msg: => String): Nothing = {
error(msg)
throw new StopQuotedContext
}
/** Report an error at the on the position of `expr` and throws a StopQuotedContext */
def throwError(msg: => String, expr: Expr[_]): Nothing = {
error(msg, expr)
throw new StopQuotedContext
}

/** Report a warning */
def warning(msg: => String): Unit = {
import tasty.{_, given}
Expand Down
4 changes: 4 additions & 0 deletions library/src/scala/quoted/StopQuotedContext.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package scala.quoted

/** Stop code generation after an error has been reported */
class StopQuotedContext extends Throwable
252 changes: 252 additions & 0 deletions library/src/scala/quoted/ValueOfExpr.scala

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions library/src/scala/quoted/matching/ValueSeq.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package scala.quoted
package matching

/** Literal sequence of literal constant value expressions */
object ValueSeq {

/** Matches literal sequence of literal constant value expressions and return a sequence of values.
*
* Usage:
* ```scala
* inline def sum(args: Int*): Int = ${ sumExpr('args) }
* def sumExpr(argsExpr: Expr[Seq[Int]])(given QuoteContext): Expr[Int] = argsExpr match
* case ValueSeq(args) =>
* // args: Seq[Int]
* ...
* }
* ```
*/
def unapply[T](expr: Expr[Seq[T]])(given valueOf: ValueOfExpr[T], qctx: QuoteContext): Option[Seq[T]] = expr match {
case ExprSeq(elems) =>
elems.foldRight(Option(List.empty[T])) { (elem, acc) =>
(elem, acc) match {
case (ValueOfExpr(value), Some(lst)) => Some(value :: lst)
case (_, _) => None
}
}
case _ => None
}

}
File renamed without changes.
File renamed without changes.
15 changes: 13 additions & 2 deletions tests/neg-macros/inline-macro-staged-interpreter/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@

import scala.quoted._
import scala.quoted.autolift.given
import scala.quoted.matching._

object E {

inline def eval[T](inline x: E[T]): T = ${ impl(x) }
inline def eval[T](inline x: E[T]): T = ${ impl('x) }

def impl[T](x: E[T])(given QuoteContext): Expr[T] = x.lift
def impl[T: Type](x: Expr[E[T]])(given QuoteContext): Expr[T] = x.value.lift

implicit def ev1[T: Type]: ValueOfExpr[E[T]] = new ValueOfExpr {
def apply(x: Expr[E[T]])(given QuoteContext): Option[E[T]] = x match {
case '{ I(${Const(n)}) } => Some(I(n).asInstanceOf[E[T]])
case '{ Plus[T](${Value(x)}, ${Value(y)})(given $op) } => Some(Plus(x, y)(given Plus2.IPlus.asInstanceOf[Plus2[T]]).asInstanceOf[E[T]])
}
}

object Value {
def unapply[T, U >: T](expr: Expr[T])(given ValueOfExpr[U], QuoteContext): Option[U] = expr.getValue
}
}

trait E[T] {
Expand Down
6 changes: 3 additions & 3 deletions tests/neg-macros/quote-error-2/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import quoted._

object Macro_1 {
inline def foo(inline b: Boolean): Unit = ${ fooImpl(b) }
def fooImpl(b: Boolean)(given QuoteContext): Expr[Unit] =
'{println(${msg(b)})}
inline def foo(inline b: Boolean): Unit = ${ fooImpl('b) }
def fooImpl(b: Expr[Boolean])(given QuoteContext): Expr[Unit] =
'{println(${msg(b.value)})}

def msg(b: Boolean)(given qctx: QuoteContext): Expr[String] =
if (b) '{"foo(true)"}
Expand Down
6 changes: 3 additions & 3 deletions tests/neg-macros/quote-error/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import quoted._

object Macro_1 {
inline def foo(inline b: Boolean): Unit = ${fooImpl(b)}
def fooImpl(b: Boolean)(given qctx: QuoteContext): Expr[Unit] =
if (b) '{println("foo(true)")}
inline def foo(inline b: Boolean): Unit = ${fooImpl('b)}
def fooImpl(b: Expr[Boolean])(given qctx: QuoteContext): Expr[Unit] =
if (b.value) '{println("foo(true)")}
else { qctx.error("foo cannot be called with false"); '{ ??? } }
}
6 changes: 3 additions & 3 deletions tests/neg-macros/quote-exception/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import quoted._

object Macro_1 {
inline def foo(inline b: Boolean): Unit = ${fooImpl(b)}
def fooImpl(b: Boolean)(given QuoteContext): Expr[Unit] =
if (b) '{println("foo(true)")}
inline def foo(inline b: Boolean): Unit = ${fooImpl('b)}
def fooImpl(b: Expr[Boolean])(given QuoteContext): Expr[Unit] =
if (b.value) '{println("foo(true)")}
else ???
}
4 changes: 2 additions & 2 deletions tests/neg-macros/quote-whitebox/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import scala.quoted._

object Macros {
inline def defaultOf(inline str: String) <: Any = ${ defaultOfImpl(str) }
def defaultOfImpl(str: String)(given QuoteContext): Expr[Any] = str match {
inline def defaultOf(inline str: String) <: Any = ${ defaultOfImpl('str) }
def defaultOfImpl(str: Expr[String])(given QuoteContext): Expr[Any] = str.value match {
case "int" => '{1}
case "string" => '{"a"}
}
Expand Down
6 changes: 3 additions & 3 deletions tests/neg-macros/reflect-inline/assert_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import scala.quoted._

object api {
inline def (inline x: String).stripMargin2: String =
${ stripImpl(x) }
${ stripImpl('x) }

private def stripImpl(x: String)(given qctx: QuoteContext): Expr[String] =
Expr(x.stripMargin)
private def stripImpl(x: Expr[String])(given qctx: QuoteContext): Expr[String] =
Expr(x.value.stripMargin)

}
9 changes: 8 additions & 1 deletion tests/pos/i4846.scala → tests/neg/i4846.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import scala.quoted._

object Test {
inline def foo(inline x: Int): Int = ${fooImpl(x, 'x, '{ 'x }, '{ '{ 'x } })}
inline def foo(inline x: Int): Int = ${
fooImpl(
x, // error
'x,
'{ 'x }, // error
'{ '{ 'x } } // error
)
}
def fooImpl(a: Int, b: Expr[Int], c: Expr[(given QuoteContext) => Expr[Int]], d: Expr[(given QuoteContext) => Expr[(given QuoteContext) => Expr[Int]]]): Expr[Int] = ???
}
11 changes: 11 additions & 0 deletions tests/neg/old-inline-param-macro.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import scala.quoted._

inline def old(inline x: Int): Int =
${ oldImpl(x) } // error

private def oldImpl(x: Int): Expr[Int] = ???

inline def `new`(inline x: Int): Int =
${ newImpl('x) }

private def newImpl(x: Expr[Int]): Expr[Int] = ???
5 changes: 4 additions & 1 deletion tests/pos-macros/power-macro/Macro_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import scala.quoted._

object PowerMacro {

inline def power(inline n: Long, x: Double) = ${powerCode(n, 'x)}
inline def power(inline n: Long, x: Double) = ${powerCode('n, 'x)}

def powerCode(n: Expr[Long], x: Expr[Double])(given QuoteContext): Expr[Double] =
powerCode(n.value, x)

def powerCode(n: Long, x: Expr[Double])(given QuoteContext): Expr[Double] =
if (n == 0) '{1.0}
Expand Down
12 changes: 6 additions & 6 deletions tests/pos-macros/quote-nested-object/Macro_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ object Macro {

object Implementation {

inline def plus(inline n: Int, m: Int): Int = ${ plus(n, 'm) }
inline def plus(inline n: Int, m: Int): Int = ${ plus('n, 'm) }

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

object Implementation2 {

inline def plus(inline n: Int, m: Int): Int = ${ plus(n, 'm) }
inline def plus(inline n: Int, m: Int): Int = ${ plus('n, 'm) }

def plus(n: Int, m: Expr[Int])(given QuoteContext): Expr[Int] =
if (n == 0) m
def plus(n: Expr[Int], m: Expr[Int])(given QuoteContext): Expr[Int] =
if (n.value == 0) m
else '{ ${n} + $m }
}
}
Expand Down
6 changes: 4 additions & 2 deletions tests/pos-macros/quote-whitebox-2/Macro_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import scala.quoted._

object Macro {

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

def impl(str: String)(given QuoteContext) = if (str.length == 1) Expr(str.charAt(0)) else Expr(str)
def impl(strExpr: Expr[String])(given QuoteContext) =
val str = strExpr.value
if (str.length == 1) Expr(str.charAt(0)) else Expr(str)

}
5 changes: 4 additions & 1 deletion tests/pos-staging/quote-0.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ object Macros {

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

inline def power(inline n: Int, x: Double) = ${ powerCode(n, 'x) }
inline def power(inline n: Int, x: Double) = ${ powerCode('n, 'x) }

def powerCode(n: Expr[Int], x: Expr[Double])(given QuoteContext): Expr[Double] =
powerCode(n.value, x)

def powerCode(n: Int, x: Expr[Double])(given QuoteContext): Expr[Double] =
if (n == 0) '{1.0}
Expand Down
3 changes: 2 additions & 1 deletion tests/pos/quote-this.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class Foo {

inline def g(): Unit = ${ Foo.impl[this.type](1) }
inline def h(): Unit = ${ Foo.impl[Any]('this) }
inline def i(that: Foo): Unit = ${ Foo.impl[that.type](1) }
// FIXME
// inline def i(that: Foo): Unit = ${ Foo.impl[that.type](1) }

}

Expand Down
18 changes: 9 additions & 9 deletions tests/run-macros/gestalt-type-toolbox-reflect/Macro_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ object TypeToolbox {
Expr(fields)
}

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

Expand All @@ -55,10 +55,10 @@ object TypeToolbox {
Expr(fields.map(_.name).toList)
}

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

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

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

inline def methods[T]: Seq[String] = ${methodsImpl('[T])}
Expand Down
5 changes: 4 additions & 1 deletion tests/run-macros/i4734/Macro_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import scala.quoted.autolift.given

object Macros {
inline def unrolledForeach(seq: IndexedSeq[Int], f: => Int => Unit, inline unrollSize: Int): Unit = // or f: Int => Unit
${ unrolledForeachImpl('seq, 'f, unrollSize) }
${ unrolledForeachImpl('seq, 'f, 'unrollSize) }

def unrolledForeachImpl(seq: Expr[IndexedSeq[Int]], f: Expr[Int => Unit], unrollSizeExpr: Expr[Int])(given QuoteContext): Expr[Unit] =
unrolledForeachImpl(seq, f, unrollSizeExpr.value)

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

0 comments on commit 2ecdf91

Please sign in to comment.