From 8ac5b72f2da2756252458f7dab38ce02d85e82b5 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 5 Nov 2020 17:26:12 +0100 Subject: [PATCH] Remove scala.quoted.unsafe.UnsafeExpr The library should not provide those operattion on Expr. These can still be done using reflection and we keep testing that they can be encoded. --- .../scala/quoted/unsafe/UnsafeExpr.scala | 70 ------------------- tests/run-macros/i7898/Macro_1.scala | 9 ++- .../quote-matcher-symantics-2/quoted_1.scala | 25 ++++++- .../quote-matcher-symantics-3/quoted_1.scala | 25 ++++++- .../quote-matching-open/Macro_1.scala | 35 +++++++++- .../quoted-matching-docs/Macro_1.scala | 7 +- 6 files changed, 91 insertions(+), 80 deletions(-) delete mode 100644 library/src-bootstrapped/scala/quoted/unsafe/UnsafeExpr.scala diff --git a/library/src-bootstrapped/scala/quoted/unsafe/UnsafeExpr.scala b/library/src-bootstrapped/scala/quoted/unsafe/UnsafeExpr.scala deleted file mode 100644 index 011c611ae632..000000000000 --- a/library/src-bootstrapped/scala/quoted/unsafe/UnsafeExpr.scala +++ /dev/null @@ -1,70 +0,0 @@ -package scala.quoted -package unsafe - -object UnsafeExpr { - - /** Returns the undelying argument that was in the call before inlining. - * - * ``` - * inline foo(x: Int): Int = baz(x, x) - * foo(bar()) - * ``` - * is inlined as - * ``` - * val x = bar() - * baz(x, x) - * ``` - * in this case the undelying argument of `x` will be `bar()`. - * - * Warning: Using the undelying argument directly in the expansion of a macro may - * change the parameter semantics as by-value parameter could be re-evaluated. - */ - def underlyingArgument[T](expr: Expr[T])(using qctx: QuoteContext): Expr[T] = - expr.unseal.underlyingArgument.seal.asInstanceOf[Expr[T]] - - // TODO generalize for any function arity - /** Allows inspection or transformation of the body of the expression of function. - * This body may have references to the arguments of the function which should be closed - * over if the expression will be spliced. - * - * ``` - * val f: Expr[T => R] = ... - * UnsafeExpr.open(f) { (body, close) => - * val newParam: Expr[T] = ... - * ... - * close(body)(newParam) // body or part of the body - * } - * ``` - */ - def open[T1, R, X](f: Expr[T1 => R])(content: (Expr[R], [t] => Expr[t] => Expr[T1] => Expr[t]) => X)(using qctx: QuoteContext): X = { - val (params, bodyExpr) = paramsAndBody[R](f) - content(bodyExpr, [t] => (e: Expr[t]) => (v: Expr[T1]) => bodyFn[t](e.unseal, params, List(v.unseal)).seal.asInstanceOf[Expr[t]]) - } - - def open[T1, T2, R, X](f: Expr[(T1, T2) => R])(content: (Expr[R], [t] => Expr[t] => (Expr[T1], Expr[T2]) => Expr[t]) => X)(using qctx: QuoteContext)(using DummyImplicit): X = { - val (params, bodyExpr) = paramsAndBody[R](f) - content(bodyExpr, [t] => (e: Expr[t]) => (v1: Expr[T1], v2: Expr[T2]) => bodyFn[t](e.unseal, params, List(v1.unseal, v2.unseal)).seal.asInstanceOf[Expr[t]]) - } - - def open[T1, T2, T3, R, X](f: Expr[(T1, T2, T3) => R])(content: (Expr[R], [t] => Expr[t] => (Expr[T1], Expr[T2], Expr[T3]) => Expr[t]) => X)(using qctx: QuoteContext)(using DummyImplicit, DummyImplicit): X = { - val (params, bodyExpr) = paramsAndBody[R](f) - content(bodyExpr, [t] => (e: Expr[t]) => (v1: Expr[T1], v2: Expr[T2], v3: Expr[T3]) => bodyFn[t](e.unseal, params, List(v1.unseal, v2.unseal, v3.unseal)).seal.asInstanceOf[Expr[t]]) - } - - private def paramsAndBody[R](using qctx: QuoteContext)(f: Expr[Any]): (List[qctx.reflect.ValDef], Expr[R]) = { - import qctx.reflect._ - val Block(List(DefDef("$anonfun", Nil, List(params), _, Some(body))), Closure(Ident("$anonfun"), None)) = f.unseal.etaExpand - (params, body.seal.asInstanceOf[Expr[R]]) - } - - private def bodyFn[t](using qctx: QuoteContext)(e: qctx.reflect.Term, params: List[qctx.reflect.ValDef], args: List[qctx.reflect.Term]): qctx.reflect.Term = { - import qctx.reflect._ - val map = params.map(_.symbol).zip(args).toMap - new TreeMap { - override def transformTerm(tree: Term)(using ctx: Context): Term = - super.transformTerm(tree) match - case tree: Ident => map.getOrElse(tree.symbol, tree) - case tree => tree - }.transformTerm(e) - } -} diff --git a/tests/run-macros/i7898/Macro_1.scala b/tests/run-macros/i7898/Macro_1.scala index 37512f21e53e..422628268018 100644 --- a/tests/run-macros/i7898/Macro_1.scala +++ b/tests/run-macros/i7898/Macro_1.scala @@ -1,10 +1,10 @@ -import quoted._ -import quoted.unsafe._ +import scala.quoted._ + object Main { def myMacroImpl(body: Expr[_])(using qctx: QuoteContext) : Expr[_] = { import qctx.reflect._ - val bodyTerm = UnsafeExpr.underlyingArgument(body).unseal + val bodyTerm = underlyingArgument(body).unseal val showed = bodyTerm.show '{ println(${Expr(showed)}) @@ -15,4 +15,7 @@ object Main { transparent inline def myMacro(body: => Any): Any = ${ myMacroImpl('body) } + + def underlyingArgument[T](expr: Expr[T])(using qctx: QuoteContext): Expr[T] = + expr.unseal.underlyingArgument.seal.asInstanceOf[Expr[T]] } diff --git a/tests/run-macros/quote-matcher-symantics-2/quoted_1.scala b/tests/run-macros/quote-matcher-symantics-2/quoted_1.scala index 0d8cd78287d4..0a90645da43c 100644 --- a/tests/run-macros/quote-matcher-symantics-2/quoted_1.scala +++ b/tests/run-macros/quote-matcher-symantics-2/quoted_1.scala @@ -1,7 +1,5 @@ import scala.quoted._ -import scala.quoted.unsafe._ - object Macros { inline def liftString(inline a: DSL): String = ${implStringNum('a)} @@ -64,6 +62,29 @@ object Macros { } +object UnsafeExpr { + def open[T1, R, X](f: Expr[T1 => R])(content: (Expr[R], [t] => Expr[t] => Expr[T1] => Expr[t]) => X)(using qctx: QuoteContext): X = { + val (params, bodyExpr) = paramsAndBody[R](f) + content(bodyExpr, [t] => (e: Expr[t]) => (v: Expr[T1]) => bodyFn[t](e.unseal, params, List(v.unseal)).seal.asInstanceOf[Expr[t]]) + } + private def paramsAndBody[R](using qctx: QuoteContext)(f: Expr[Any]): (List[qctx.reflect.ValDef], Expr[R]) = { + import qctx.reflect._ + val Block(List(DefDef("$anonfun", Nil, List(params), _, Some(body))), Closure(Ident("$anonfun"), None)) = f.unseal.etaExpand + (params, body.seal.asInstanceOf[Expr[R]]) + } + + private def bodyFn[t](using qctx: QuoteContext)(e: qctx.reflect.Term, params: List[qctx.reflect.ValDef], args: List[qctx.reflect.Term]): qctx.reflect.Term = { + import qctx.reflect._ + val map = params.map(_.symbol).zip(args).toMap + new TreeMap { + override def transformTerm(tree: Term)(using ctx: Context): Term = + super.transformTerm(tree) match + case tree: Ident => map.getOrElse(tree.symbol, tree) + case tree => tree + }.transformTerm(e) + } +} + def freshEnvVar()(using QuoteContext): (Int, Expr[DSL]) = { v += 1 (v, '{envVar(${Expr(v)})}) diff --git a/tests/run-macros/quote-matcher-symantics-3/quoted_1.scala b/tests/run-macros/quote-matcher-symantics-3/quoted_1.scala index a7dd6a183bbb..bf1591ab6c2f 100644 --- a/tests/run-macros/quote-matcher-symantics-3/quoted_1.scala +++ b/tests/run-macros/quote-matcher-symantics-3/quoted_1.scala @@ -1,7 +1,5 @@ import scala.quoted._ -import scala.quoted.unsafe._ - object Macros { @@ -76,6 +74,29 @@ object Macros { } +object UnsafeExpr { + def open[T1, R, X](f: Expr[T1 => R])(content: (Expr[R], [t] => Expr[t] => Expr[T1] => Expr[t]) => X)(using qctx: QuoteContext): X = { + val (params, bodyExpr) = paramsAndBody[R](f) + content(bodyExpr, [t] => (e: Expr[t]) => (v: Expr[T1]) => bodyFn[t](e.unseal, params, List(v.unseal)).seal.asInstanceOf[Expr[t]]) + } + private def paramsAndBody[R](using qctx: QuoteContext)(f: Expr[Any]): (List[qctx.reflect.ValDef], Expr[R]) = { + import qctx.reflect._ + val Block(List(DefDef("$anonfun", Nil, List(params), _, Some(body))), Closure(Ident("$anonfun"), None)) = f.unseal.etaExpand + (params, body.seal.asInstanceOf[Expr[R]]) + } + + private def bodyFn[t](using qctx: QuoteContext)(e: qctx.reflect.Term, params: List[qctx.reflect.ValDef], args: List[qctx.reflect.Term]): qctx.reflect.Term = { + import qctx.reflect._ + val map = params.map(_.symbol).zip(args).toMap + new TreeMap { + override def transformTerm(tree: Term)(using ctx: Context): Term = + super.transformTerm(tree) match + case tree: Ident => map.getOrElse(tree.symbol, tree) + case tree => tree + }.transformTerm(e) + } +} + def freshEnvVar[T: Type]()(using QuoteContext): (Int, Expr[T]) = { v += 1 (v, '{envVar[T](${Expr(v)})}) diff --git a/tests/run-macros/quote-matching-open/Macro_1.scala b/tests/run-macros/quote-matching-open/Macro_1.scala index c06bfaaaa5b6..5489054b3097 100644 --- a/tests/run-macros/quote-matching-open/Macro_1.scala +++ b/tests/run-macros/quote-matching-open/Macro_1.scala @@ -1,5 +1,5 @@ import scala.quoted._ -import scala.quoted.unsafe._ + object Macro { inline def openTest(inline x: Any): Any = ${ Macro.impl('x) } @@ -13,3 +13,36 @@ object Macro { } } + + +object UnsafeExpr { + def open[T1, R, X](f: Expr[T1 => R])(content: (Expr[R], [t] => Expr[t] => Expr[T1] => Expr[t]) => X)(using qctx: QuoteContext): X = { + val (params, bodyExpr) = paramsAndBody[R](f) + content(bodyExpr, [t] => (e: Expr[t]) => (v: Expr[T1]) => bodyFn[t](e.unseal, params, List(v.unseal)).seal.asInstanceOf[Expr[t]]) + } + def open[T1, T2, R, X](f: Expr[(T1, T2) => R])(content: (Expr[R], [t] => Expr[t] => (Expr[T1], Expr[T2]) => Expr[t]) => X)(using qctx: QuoteContext)(using DummyImplicit): X = { + val (params, bodyExpr) = paramsAndBody[R](f) + content(bodyExpr, [t] => (e: Expr[t]) => (v1: Expr[T1], v2: Expr[T2]) => bodyFn[t](e.unseal, params, List(v1.unseal, v2.unseal)).seal.asInstanceOf[Expr[t]]) + } + + def open[T1, T2, T3, R, X](f: Expr[(T1, T2, T3) => R])(content: (Expr[R], [t] => Expr[t] => (Expr[T1], Expr[T2], Expr[T3]) => Expr[t]) => X)(using qctx: QuoteContext)(using DummyImplicit, DummyImplicit): X = { + val (params, bodyExpr) = paramsAndBody[R](f) + content(bodyExpr, [t] => (e: Expr[t]) => (v1: Expr[T1], v2: Expr[T2], v3: Expr[T3]) => bodyFn[t](e.unseal, params, List(v1.unseal, v2.unseal, v3.unseal)).seal.asInstanceOf[Expr[t]]) + } + private def paramsAndBody[R](using qctx: QuoteContext)(f: Expr[Any]): (List[qctx.reflect.ValDef], Expr[R]) = { + import qctx.reflect._ + val Block(List(DefDef("$anonfun", Nil, List(params), _, Some(body))), Closure(Ident("$anonfun"), None)) = f.unseal.etaExpand + (params, body.seal.asInstanceOf[Expr[R]]) + } + + private def bodyFn[t](using qctx: QuoteContext)(e: qctx.reflect.Term, params: List[qctx.reflect.ValDef], args: List[qctx.reflect.Term]): qctx.reflect.Term = { + import qctx.reflect._ + val map = params.map(_.symbol).zip(args).toMap + new TreeMap { + override def transformTerm(tree: Term)(using ctx: Context): Term = + super.transformTerm(tree) match + case tree: Ident => map.getOrElse(tree.symbol, tree) + case tree => tree + }.transformTerm(e) + } +} \ No newline at end of file diff --git a/tests/run-macros/quoted-matching-docs/Macro_1.scala b/tests/run-macros/quoted-matching-docs/Macro_1.scala index 473fed86d6f5..3c714c03b6b2 100644 --- a/tests/run-macros/quoted-matching-docs/Macro_1.scala +++ b/tests/run-macros/quoted-matching-docs/Macro_1.scala @@ -1,7 +1,5 @@ import scala.quoted._ -import scala.quoted.unsafe._ - inline def sum(args: Int*): Int = ${ sumExpr('args) } inline def sumShow(args: Int*): String = ${ sumExprShow('args) } @@ -27,4 +25,9 @@ private def sumExpr(argsExpr: Expr[Seq[Int]])(using qctx: QuoteContext) : Expr[I case _ => '{ $argsExpr.sum } } +} + +object UnsafeExpr { + def underlyingArgument[T](expr: Expr[T])(using qctx: QuoteContext): Expr[T] = + expr.unseal.underlyingArgument.seal.asInstanceOf[Expr[T]] } \ No newline at end of file