diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/QuotedOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/QuotedOpsImpl.scala index 3407046672eb..dfcc8bf75a0f 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/QuotedOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/QuotedOpsImpl.scala @@ -6,7 +6,7 @@ import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.Symbols.defn import dotty.tools.dotc.core.StdNames.nme import dotty.tools.dotc.core.quoted.PickledQuotes -import dotty.tools.dotc.core.Types +import dotty.tools.dotc.core.{Contexts, Types} trait QuotedOpsImpl extends scala.tasty.reflect.QuotedOps with CoreImpl { @@ -20,9 +20,20 @@ trait QuotedOpsImpl extends scala.tasty.reflect.QuotedOps with CoreImpl { def TermToQuoteDeco(term: Term): TermToQuotedAPI = new TermToQuotedAPI { - def seal[T: scala.quoted.Type](implicit ctx: Context): scala.quoted.Expr[T] = { + def seal(implicit ctx: Contexts.Context): scala.quoted.Sealed = { + val expr = new scala.quoted.Exprs.TastyTreeExpr(term) + val typeTree = tpd.TypeTree(term.tpe).withPos(term.pos) + val tpe = new scala.quoted.Types.TreeType(typeTree) + scala.quoted.Sealed(expr)(tpe) + } + + } + def SealedDeco(seal: quoted.Sealed): SealedAPI = new SealedAPI { + + def asExprOf[T: quoted.Type](implicit ctx: Context): quoted.Expr[T] = { val expectedType = QuotedTypeDeco(implicitly[scala.quoted.Type[T]]).unseal.tpe + val term = QuotedExprDeco(seal.expr).unseal def etaExpand(term: Term): Term = term.tpe.widen match { case mtpe: Types.MethodType if !mtpe.isParamDependent => @@ -47,5 +58,7 @@ trait QuotedOpsImpl extends scala.tasty.reflect.QuotedOps with CoreImpl { ) } } + } + } diff --git a/library/src/scala/quoted/SealedExpr.scala b/library/src/scala/quoted/SealedExpr.scala new file mode 100644 index 000000000000..bd8a63b78fd8 --- /dev/null +++ b/library/src/scala/quoted/SealedExpr.scala @@ -0,0 +1,25 @@ +package scala.quoted + +/** A container for an `Expr[Tpe]` with its `Type[Tpe]` where `Tpe` might be unkown. + * + * Example usage: + * ``` + * def let[T](s: Sealed)(body: Expr[s.Tpe] => Expr[T]): Expr[T] = '{ + * val x: ~s.tpe = ~s.expr + * ~body('(x)) + * } + * ``` + */ +final class Sealed private (e: Expr[_], t: Type[_]) { + + type Tpe + + val expr: Expr[Tpe] = e.asInstanceOf[Expr[Tpe]] + + val tpe: Type[Tpe] = t.asInstanceOf[Type[Tpe]] + +} + +object Sealed { + def apply[T](expr: Expr[T])(implicit tpe: Type[T]): Sealed { type Tpe = T } = new Sealed(expr, tpe).asInstanceOf[Sealed { type Tpe = T }] +} diff --git a/library/src/scala/tasty/reflect/QuotedOps.scala b/library/src/scala/tasty/reflect/QuotedOps.scala index 4e1cbfd2b47a..ae6271ebbb2f 100644 --- a/library/src/scala/tasty/reflect/QuotedOps.scala +++ b/library/src/scala/tasty/reflect/QuotedOps.scala @@ -16,9 +16,14 @@ trait QuotedOps extends Core { implicit def QuotedTypeDeco[T](tpe: quoted.Type[T]): QuotedTypeAPI trait TermToQuotedAPI { - /** Convert `Term` to an `Expr[T]` and check that it conforms to `T` */ - def seal[T: scala.quoted.Type](implicit ctx: Context): scala.quoted.Expr[T] + /** Convert `Term` to an `Expr[Tpe]` with its `Type[Tpe]` for some unknown `Tpe` */ + def seal(implicit ctx: Context): quoted.Sealed } implicit def TermToQuoteDeco(term: Term): TermToQuotedAPI + trait SealedAPI { + /** Return the `expr` as an `Expr[T]` and check that it conforms to a known `T` */ + def asExprOf[T: quoted.Type](implicit ctx: Context): quoted.Expr[T] + } + implicit def SealedDeco(seal: quoted.Sealed): SealedAPI } diff --git a/tests/neg/quoted-sealed.scala b/tests/neg/quoted-sealed.scala new file mode 100644 index 000000000000..71cf01f19d43 --- /dev/null +++ b/tests/neg/quoted-sealed.scala @@ -0,0 +1,15 @@ + +import scala.quoted._ + +class Foo { + + def let[T](s1: Sealed, s2: Sealed)(body: Expr[s1.Tpe] => Expr[T]): Expr[T] = '{ + val x: ~s2.tpe = ~s1.expr // error + val y: ~s1.tpe = ~s2.expr // error + val z: Any = ~s1.expr + ~body('(x)) // error + ~body('(y)) + ~body('(z)) // error + } + +} \ No newline at end of file diff --git a/tests/neg/tasty-macro-assert/quoted_1.scala b/tests/neg/tasty-macro-assert/quoted_1.scala index aff13eaf145c..c0b8441f180e 100644 --- a/tests/neg/tasty-macro-assert/quoted_1.scala +++ b/tests/neg/tasty-macro-assert/quoted_1.scala @@ -34,7 +34,7 @@ object Asserts { tree match { case Term.Inlined(_, Nil, Term.Apply(Term.Select(OpsTree(left), op), right :: Nil)) => - '(assertTrue(~left.seal[Boolean])) // Buggy code. To generate the errors + '(assertTrue(~left.seal.asExprOf[Boolean])) // Buggy code. To generate the errors case _ => '(assertTrue(~cond)) } diff --git a/tests/run/f-interpolation-1/FQuote_1.scala b/tests/run/f-interpolation-1/FQuote_1.scala index c428ecfe0c0d..c6f5306b671d 100644 --- a/tests/run/f-interpolation-1/FQuote_1.scala +++ b/tests/run/f-interpolation-1/FQuote_1.scala @@ -14,7 +14,7 @@ object FQuote { def liftListOfAny(lst: List[Term]): Expr[List[Any]] = lst match { case x :: xs => - val head = x.seal[Any] + val head = x.seal.asExprOf[Any] val tail = liftListOfAny(xs) '{ ~head :: ~tail } case Nil => '(Nil) diff --git a/tests/run/i5533/Macro_1.scala b/tests/run/i5533/Macro_1.scala index 05552fcc7513..7162e7c67f4d 100644 --- a/tests/run/i5533/Macro_1.scala +++ b/tests/run/i5533/Macro_1.scala @@ -13,7 +13,7 @@ object scalatest { val tree = condition.unseal - val expr = tree.seal[Boolean] + val expr = tree.seal.asExprOf[Boolean] '(println(~expr)) } diff --git a/tests/run/i5533b/Macro_1.scala b/tests/run/i5533b/Macro_1.scala index ad63176aa2cd..6003837ffe8f 100644 --- a/tests/run/i5533b/Macro_1.scala +++ b/tests/run/i5533b/Macro_1.scala @@ -16,8 +16,8 @@ object scalatest { tree.underlyingArgument match { case Term.Apply(Term.Select(lhs, op), rhs :: Nil) => - val left = lhs.seal[Any] - val right = rhs.seal[Any] + val left = lhs.seal.asExprOf[Any] + val right = rhs.seal.asExprOf[Any] op match { case "==" => '{ diff --git a/tests/run/i5536/Macro_1.scala b/tests/run/i5536/Macro_1.scala index 58e986cad5e7..4ba2299eb027 100644 --- a/tests/run/i5536/Macro_1.scala +++ b/tests/run/i5536/Macro_1.scala @@ -13,8 +13,8 @@ object scalatest { tree.underlyingArgument match { case Term.Apply(Term.Select(lhs, op), rhs :: Nil) => - val left = lhs.seal[Any] - val right = rhs.seal[Any] + val left = lhs.seal.asExprOf[Any] + val right = rhs.seal.asExprOf[Any] op match { case "===" => '{ diff --git a/tests/run/quote-extractors-1.check b/tests/run/quote-extractors-1.check new file mode 100644 index 000000000000..1077c8a445bc --- /dev/null +++ b/tests/run/quote-extractors-1.check @@ -0,0 +1,68 @@ +a.>(5) +{ + val pre: a = a + val arg: 5 = 5 + pre.>(arg) +} + +a.>(5.0) +{ + val pre: a = a + val arg: 5.0 = 5.0 + pre.>(arg) +} + +a.>('a') +{ + val pre: a = a + val arg: 'a' = 'a' + pre.>(arg) +} + +b1.>(4) +{ + val pre: b1 = b1 + val arg: 4 = 4 + pre.>(arg) +} + +b1.>(b2) +{ + val pre: b1 = b1 + val arg: b2 = b2 + pre.>(arg) +} + +b.unary_! +{ + val pre: b = b + pre.unary_! +} + +true +true + +false +false + +if (b.unary_!) a.>(5) else b1.>(4) +if ({ + val pre: b = b + pre.unary_! +}) { + val pre$2: a = a + val arg: 5 = 5 + pre$2.>(arg) +} else { + val pre$3: b1 = b1 + val arg$2: 4 = 4 + pre$3.>(arg$2) +} + +while (b3) { + scala.Predef.println(b3) + b3 = false +} +while (b3) scala.Predef.??? + +end diff --git a/tests/run/quote-extractors-1/assert_1.scala b/tests/run/quote-extractors-1/assert_1.scala new file mode 100644 index 000000000000..b0cdde779617 --- /dev/null +++ b/tests/run/quote-extractors-1/assert_1.scala @@ -0,0 +1,200 @@ +import scala.quoted._ +import scala.tasty._ + + +object Macro { + + inline def transformAndPrint(code: => Any): Unit = ~transformImpl('(code)) + + private def transformImpl(code: Expr[Any])(implicit refl: Reflection): Expr[Unit] = { + import refl._ + + val transformer = new Transformer { + override def postTransform[T](c: Expr[T])(implicit t: quoted.Type[T]): Expr[T] = c match { + // case app0 @@ '{ (~prefix).apply() } + case Inspect(app0: SealedApplication0[T]) => + '{ + val pre: ~app0.prefix.tpe = ~app0.prefix.expr + ~app0('(pre)) + } + // case app1 @@ '{ (~prefix).apply(~arg) } + case Inspect(app1: SealedApplication1[T]) => + '{ + val pre: ~app1.prefix.tpe = ~app1.prefix.expr + val arg: ~app1.arg.tpe = ~app1.arg.expr + ~app1('(pre))('(arg)) + } + // case whileDo @@ '{ while (~cond) ~body } + case Inspect(whileDo @ SealedWhile(cond, body)) => + whileDo (cond.expr) { + '(???) + } + case _ => + c + } + } + + val code2: Expr[Any] = transformer.transform(code.unseal.underlyingArgument.seal.asExprOf[Any]) + + implicit val toolbox: Toolbox = Toolbox.make + '{ + println(~code.show.toExpr) + println(~code2.show.toExpr) + println() + } + } + + /// LIBRARY CODE + + class Transformer(implicit refl: Reflection) { + + def postTransform[T](c: Expr[T])(implicit t: quoted.Type[T]): Expr[T] = c + + def transform[T](c: Expr[T])(implicit t: quoted.Type[T]): Expr[T] = { + import refl._ + c match { + // case '{ if (~cond) ~thenp else ~elsep } + case Inspect(iF @ SealedIf(cond, thenp, elsep)) => + postTransform(iF(transformSealed(cond).expr)(transformSealed(thenp).expr, transformSealed(elsep).expr)) + // case '{ (~prefix).apply() } + case Inspect(app0: SealedApplication0[T]) => + postTransform(app0(transformSealed(app0.prefix).expr)) + // case '{ (~prefix).apply(~arg) } + case Inspect(app1: SealedApplication1[T]) => + postTransform(app1(transformSealed(app1.prefix).expr)(transformSealed(app1.arg).expr)) + // case '{ while (~cond) ~body } + case Inspect(whileDo @ SealedWhile(cond, body)) => + postTransform(whileDo(transformSealed(cond).expr)(transformSealed(body).expr)) + // case '{ ~(stats: _*); ~expr } + case Inspect(block: SealedBlock[T]) => + postTransform(block(block.stats.map(x => transformSealed(x).expr), transformSealed(block.last).expr)) + case _ => + c + } + } + + def transformSealed[T](c: Sealed): Sealed { type Tpe = c.Tpe } = Sealed(transform(c.expr)(c.tpe))(c.tpe) + + } + + + object Inspect { + def unapply[T: Type](arg: Expr[T])(implicit refl: Reflection): Option[SplittedExpr[T]] = { + import refl._ + arg.unseal match { + case Term.IsIf(ifExpr) => + Some(new SealedIf { + val _1: SealedBy[Boolean] = ifExpr.cond.seal.asInstanceOf[SealedBy[Boolean]] + val _2: SealedBy[T] = ifExpr.thenp.seal.asInstanceOf[SealedBy[T]] + val _3: SealedBy[T] = ifExpr.elsep.seal.asInstanceOf[SealedBy[T]] + def apply(cond: Expr[Boolean])(thenp: Expr[T], elsep: Expr[T]): Expr[T] = + Term.If.copy(ifExpr)(cond.unseal, thenp.unseal, elsep.unseal).seal.asExprOf[T] + }) + case Term.IsWhile(whileDo) => + Some(new SealedWhile { + val _1: SealedBy[Boolean] = whileDo.cond.seal.asInstanceOf[SealedBy[Boolean]] + val _2: Sealed = whileDo.body.seal + def apply(cond: Expr[Boolean])(body: Expr[Any]): Expr[T & Unit] = + Term.While.copy(whileDo)(cond.unseal, body.unseal).seal.asExprOf[T & Unit] + }) + case Term.IsBlock(block) => + // TODO Use let expression instead? + val statements = block.statements.collect { case IsTerm(x) => x.seal } + if (statements.size != block.statements.size) { + None + } else { + Some(new SealedBlock { + val stats: List[Sealed] = statements + val last: SealedBy[T] = block.expr.seal.asInstanceOf[SealedBy[T]] + def apply(stats: List[Expr[_]], last: Expr[T]): Expr[T] = + Term.Block.copy(block)(stats.map(_.unseal), last.unseal).seal.asExprOf[T] + }) + } + case Term.IsSelect(sel @ Term.Select(pre, op)) => + Some(new SealedApplication0 { + val prefix: Sealed = pre.seal + def apply(prefix: Expr[this.prefix.Tpe]): Expr[T] = + Term.Select.copy(sel)(prefix.unseal, op).seal.asExprOf[T] + }) + case Term.Apply(Term.IsSelect(sel @ Term.Select(pre, op)), args) => + args match { + case a1 :: Nil => + Some(new SealedApplication1 { + val prefix: Sealed = pre.seal + val arg: Sealed = a1.seal + def apply(prefix: Expr[this.prefix.Tpe])(arg: Expr[this.arg.Tpe]): Expr[T] = + Term.Apply(Term.Select.copy(sel)(prefix.unseal, op), arg.unseal :: Nil).seal.asExprOf[T] + }) + case a1 :: a2 :: Nil => + Some(new SealedApplication2 { + val prefix: Sealed = pre.seal + val arg1: Sealed = a1.seal + val arg2: Sealed = a2.seal + def apply(prefix: Expr[this.prefix.Tpe])(arg1: Expr[this.arg1.Tpe], arg2: Expr[this.arg2.Tpe]): Expr[T] = + Term.Apply(Term.Select.copy(sel)(prefix.unseal, op), arg1.unseal :: arg2.unseal :: Nil).seal.asExprOf[T] + }) + } + case tree => + None + } + } + } + + type SealedBy[X] = Sealed { type Tpe <: X } + + sealed trait SplittedExpr[T] { self => + def get: self.type = self + def isEmpty: Boolean = false + } + + trait SealedModule[U[T] <: SplittedExpr[T]] { + def unapply[T](arg: U[T]): arg.type = arg + } + + object SealedIf extends SealedModule[SealedIf] + sealed trait SealedIf[T] extends SplittedExpr[T] { + /** Condition */ + def _1: SealedBy[Boolean] + /** `then` part */ + def _2: SealedBy[T] + /** `else` part */ + def _3: SealedBy[T] + /** `if` reconstructor */ + def apply(cond: Expr[Boolean])(thenp: Expr[T], elsep: Expr[T]): Expr[T] + } + + object SealedWhile extends SealedModule[SealedWhile] + trait SealedWhile[T] extends SplittedExpr[T] { + /** Condition of the loop */ + def _1: SealedBy[Boolean] + /** Body of the loop */ + def _2: Sealed + /** Loop reconstructor */ + def apply(cond: Expr[Boolean])(body: Expr[Any]): Expr[T & Unit] + } + + // TODO use let expression instead? + trait SealedBlock[T] extends SplittedExpr[T] { + val stats: List[Sealed] + val last: SealedBy[T] + def apply(stats: List[Expr[_]], last: Expr[T]): Expr[T] + } + + trait SealedApplication0[T] extends SplittedExpr[T] { + val prefix: Sealed + def apply(prefix: Expr[this.prefix.Tpe]): Expr[T] + } + + trait SealedApplication1[T] extends SplittedExpr[T] { + val prefix: Sealed + val arg: Sealed + def apply(prefix: Expr[this.prefix.Tpe])(arg: Expr[this.arg.Tpe]): Expr[T] + } + + trait SealedApplication2[T] extends SplittedExpr[T] { + val prefix: Sealed + val arg1: Sealed + val arg2: Sealed + def apply(prefix: Expr[this.prefix.Tpe])(arg1: Expr[this.arg1.Tpe], arg2: Expr[this.arg2.Tpe]): Expr[T] + } +} \ No newline at end of file diff --git a/tests/run/quote-extractors-1/test_2.scala b/tests/run/quote-extractors-1/test_2.scala new file mode 100644 index 000000000000..7d39b2d98bcf --- /dev/null +++ b/tests/run/quote-extractors-1/test_2.scala @@ -0,0 +1,32 @@ +object Test { + import Macro.transformAndPrint + + class Box(val x: Int) { + def >(y: Int): Boolean = x > y + def >(b: Box): Boolean = x > b.x + } + + def main(args: Array[String]): Unit = { + val a: Int = 100 + transformAndPrint(a > 5) + transformAndPrint(a > 5.0) + transformAndPrint(a > 'a') + + val b1 = new Box(10) + val b2 = new Box(3) + transformAndPrint(b1 > 4) + transformAndPrint(b1 > b2) + + val b: Boolean = false + transformAndPrint(!b) + + transformAndPrint(true) + transformAndPrint(false) + transformAndPrint(if (!b) a > 5 else b1 > 4) + + var b3: Boolean = false + transformAndPrint(while (b3) { println(b3); b3 = false }) + + println("end") + } +} diff --git a/tests/run/reflect-select-copy-2/assert_1.scala b/tests/run/reflect-select-copy-2/assert_1.scala new file mode 100644 index 000000000000..483a29e6d939 --- /dev/null +++ b/tests/run/reflect-select-copy-2/assert_1.scala @@ -0,0 +1,39 @@ +import scala.quoted._ +import scala.tasty._ + +object scalatest { + + inline def assert(condition: => Boolean): Unit = ~assertImpl('(condition), '("")) + + def assertImpl(cond: Expr[Boolean], clue: Expr[Any])(implicit refl: Reflection): Expr[Unit] = { + import refl._ + + cond match { + case BinaryApplication(lhs, rhs, f) => + '{ + val left: ~lhs.tpe = ~lhs.expr + val right: ~rhs.tpe = ~rhs.expr + scala.Predef.assert(~f('(left), '(right))) + } + case _ => + '{ scala.Predef.assert(~cond) } + } + } + + object BinaryApplication { + // TODO add dependent types to function parameters (not currently possible with this API) + def unapply[T: Type](arg: Expr[T])(implicit refl: Reflection): Option[(Sealed, Sealed, (Expr[_], Expr[_]) => Expr[T])] = { + import refl._ + arg.unseal.underlyingArgument match { + case Term.Apply(Term.IsSelect(sel @ Term.Select(lhs, op)), rhs :: Nil) => + val left = lhs.seal + val right = rhs.seal + val application = (lhs: Expr[_], rhs: Expr[_]) => Term.Apply(Term.Select.copy(sel)(lhs.unseal, op), rhs.unseal :: Nil).seal.asExprOf[T] + Some((left, right, application)) + case _ => + None + } + } + + } +} \ No newline at end of file diff --git a/tests/run/reflect-select-copy-2/test_2.scala b/tests/run/reflect-select-copy-2/test_2.scala new file mode 100644 index 000000000000..435c487885a2 --- /dev/null +++ b/tests/run/reflect-select-copy-2/test_2.scala @@ -0,0 +1,20 @@ +object Test { + import scalatest._ + + class Box(val x: Int) { + def >(y: Int): Boolean = x > y + def >(b: Box): Boolean = x > b.x + } + + def main(args: Array[String]): Unit = { + val a: Int = 100 + assert(a > 5) + assert(a > 5.0) + assert(a > 'a') + + val b1 = new Box(10) + val b2 = new Box(3) + assert(b1 > 4) + assert(b1 > b2) + } +} diff --git a/tests/run/reflect-select-copy-3/assert_1.scala b/tests/run/reflect-select-copy-3/assert_1.scala new file mode 100644 index 000000000000..a7e711371b6d --- /dev/null +++ b/tests/run/reflect-select-copy-3/assert_1.scala @@ -0,0 +1,44 @@ +import scala.quoted._ +import scala.tasty._ + +object scalatest { + + inline def assert(condition: => Boolean): Unit = ~assertImpl('(condition), '("")) + + def assertImpl(cond: Expr[Boolean], clue: Expr[Any])(implicit refl: Reflection): Expr[Unit] = { + import refl._ + + // Generic let binding + def let[T](s: Sealed)(body: Expr[s.Tpe] => Expr[T]): Expr[T] = '{ + val x: ~s.tpe = ~s.expr + ~body('(x)) + } + + cond match { + case BinaryApplication(lhs, rhs, f) => + let (lhs) { lhsExpr => + let (rhs) { rhsExpr => + '{ scala.Predef.assert(~f(lhsExpr, rhsExpr)) } + }} + case _ => + '{ scala.Predef.assert(~cond) } + } + } + + object BinaryApplication { + // TODO add dependent types to function parameters (not currently possible with this API) + def unapply[T: Type](arg: Expr[T])(implicit refl: Reflection): Option[(Sealed, Sealed, (Expr[_], Expr[_]) => Expr[T])] = { + import refl._ + arg.unseal.underlyingArgument match { + case Term.Apply(Term.IsSelect(sel @ Term.Select(lhs, op)), rhs :: Nil) => + val left = lhs.seal + val right = rhs.seal + val application = (lhs: Expr[_], rhs: Expr[_]) => Term.Apply(Term.Select.copy(sel)(lhs.unseal, op), rhs.unseal :: Nil).seal.asExprOf[T] + Some((left, right, application)) + case _ => + None + } + } + + } +} \ No newline at end of file diff --git a/tests/run/reflect-select-copy-3/test_2.scala b/tests/run/reflect-select-copy-3/test_2.scala new file mode 100644 index 000000000000..435c487885a2 --- /dev/null +++ b/tests/run/reflect-select-copy-3/test_2.scala @@ -0,0 +1,20 @@ +object Test { + import scalatest._ + + class Box(val x: Int) { + def >(y: Int): Boolean = x > y + def >(b: Box): Boolean = x > b.x + } + + def main(args: Array[String]): Unit = { + val a: Int = 100 + assert(a > 5) + assert(a > 5.0) + assert(a > 'a') + + val b1 = new Box(10) + val b2 = new Box(3) + assert(b1 > 4) + assert(b1 > b2) + } +} diff --git a/tests/run/reflect-select-copy-4/assert_1.scala b/tests/run/reflect-select-copy-4/assert_1.scala new file mode 100644 index 000000000000..0d55068e718a --- /dev/null +++ b/tests/run/reflect-select-copy-4/assert_1.scala @@ -0,0 +1,33 @@ +import scala.quoted._ +import scala.tasty._ + +object scalatest { + + inline def assert(condition: => Boolean): Unit = ~assertImpl('(condition), '("")) + + def assertImpl(cond: Expr[Boolean], clue: Expr[Any])(implicit refl: Reflection): Expr[Unit] = { + import refl._ + + def unsafeLet(expr: Term)(body: Term.Ident => Term): Term = { // as proposed in #5567 + // fundamentally dangerous, do not do this at home + val s = expr.seal + ('{ + val x: ~s.tpe = ~s.expr + ~body(('(x)).unseal.asInstanceOf[Term.Ident]).seal.asExprOf[Any] + }).unseal + } + + cond.unseal.underlyingArgument match { + case Term.Apply(Term.IsSelect(sel @ Term.Select(lhs, op)), rhs :: Nil) => + val cond = unsafeLet(lhs) { lhs2 => + unsafeLet(rhs) { rhs2 => + Term.Apply(Term.Select.copy(sel)(lhs2, op), rhs2 :: Nil) + } + }.seal.asExprOf[Boolean] + '{ scala.Predef.assert(~cond) } + case _ => + '{ scala.Predef.assert(~cond) } + } + } + +} \ No newline at end of file diff --git a/tests/run/reflect-select-copy-4/test_2.scala b/tests/run/reflect-select-copy-4/test_2.scala new file mode 100644 index 000000000000..435c487885a2 --- /dev/null +++ b/tests/run/reflect-select-copy-4/test_2.scala @@ -0,0 +1,20 @@ +object Test { + import scalatest._ + + class Box(val x: Int) { + def >(y: Int): Boolean = x > y + def >(b: Box): Boolean = x > b.x + } + + def main(args: Array[String]): Unit = { + val a: Int = 100 + assert(a > 5) + assert(a > 5.0) + assert(a > 'a') + + val b1 = new Box(10) + val b2 = new Box(3) + assert(b1 > 4) + assert(b1 > b2) + } +} diff --git a/tests/run/reflect-select-copy-5/assert_1.scala b/tests/run/reflect-select-copy-5/assert_1.scala new file mode 100644 index 000000000000..301b4b89c0d6 --- /dev/null +++ b/tests/run/reflect-select-copy-5/assert_1.scala @@ -0,0 +1,52 @@ +import scala.quoted._ +import scala.tasty._ + +object scalatest { + + inline def assert(condition: => Boolean): Unit = ~assertImpl('(condition), '("")) + + def assertImpl(cond: Expr[Boolean], clue: Expr[Any])(implicit refl: Reflection): Expr[Unit] = { + import refl._ + + split(cond) match { + case app0: SealedApplication0[Boolean] => + '{ + val pre: ~app0.prefix.tpe = ~app0.prefix.expr + scala.Predef.assert(~app0.fun('(pre))) + } + case app1: SealedApplication1[Boolean] => + '{ + val pre: ~app1.prefix.tpe = ~app1.prefix.expr + val arg: ~app1.arg.tpe = ~app1.arg.expr + scala.Predef.assert(~app1.fun('(pre))('(arg))) + } + case _: NotApplication[Boolean] => + '{ scala.Predef.assert(~cond) } + } + } + + def split[T: Type](arg: Expr[T])(implicit refl: Reflection): SealedApplication[T] = { + import refl._ + arg.unseal.underlyingArgument match { + case Term.IsSelect(sel @ Term.Select(prefix, op)) => + val sealedPrefix = prefix.seal + val application = (prefix: Expr[sealedPrefix.Tpe]) => Term.Select.copy(sel)(prefix.unseal, op).seal.asExprOf[T] + new SealedApplication0(sealedPrefix, application) + case Term.Apply(Term.IsSelect(sel @ Term.Select(prefix, op)), arg1 :: Nil) => + val sealedPrefix = prefix.seal + val sealedArg1 = arg1.seal + val application = (prefix: Expr[sealedPrefix.Tpe]) => (arg: Expr[sealedArg1.Tpe]) => Term.Apply(Term.Select.copy(sel)(prefix.unseal, op), arg.unseal :: Nil).seal.asExprOf[T] + new SealedApplication1(sealedPrefix, sealedArg1, application) + case _ => + new NotApplication[T] + } + } + + + trait SealedApplication[T] + class NotApplication[T] extends SealedApplication[T] + class SealedApplication0[T](val prefix: Sealed, val fun: Expr[prefix.Tpe] => Expr[T]) extends SealedApplication[T] + class SealedApplication1[T](val prefix: Sealed, val arg: Sealed, val fun: Expr[prefix.Tpe] => Expr[arg.Tpe] => Expr[T]) extends SealedApplication[T] + + +} \ No newline at end of file diff --git a/tests/run/reflect-select-copy-5/test_2.scala b/tests/run/reflect-select-copy-5/test_2.scala new file mode 100644 index 000000000000..6554f31275ee --- /dev/null +++ b/tests/run/reflect-select-copy-5/test_2.scala @@ -0,0 +1,23 @@ +object Test { + import scalatest._ + + class Box(val x: Int) { + def >(y: Int): Boolean = x > y + def >(b: Box): Boolean = x > b.x + } + + def main(args: Array[String]): Unit = { + val a: Int = 100 + assert(a > 5) + assert(a > 5.0) + assert(a > 'a') + + val b1 = new Box(10) + val b2 = new Box(3) + assert(b1 > 4) + assert(b1 > b2) + + val b: Boolean = false + assert(!b) + } +} diff --git a/tests/run/reflect-select-copy/assert_1.scala b/tests/run/reflect-select-copy/assert_1.scala index 498bd48c4c2d..f75fd2956be4 100644 --- a/tests/run/reflect-select-copy/assert_1.scala +++ b/tests/run/reflect-select-copy/assert_1.scala @@ -12,7 +12,7 @@ object scalatest { cond.unseal.underlyingArgument match { case Term.Apply(sel @ Term.Select(lhs, op), rhs :: Nil) => val Term.IsSelect(select) = sel - val cond = Term.Apply(Term.Select.copy(select)(lhs, ">"), rhs :: Nil).seal[Boolean] + val cond = Term.Apply(Term.Select.copy(select)(lhs, ">"), rhs :: Nil).seal.asExprOf[Boolean] '{ scala.Predef.assert(~cond) } case _ => '{ scala.Predef.assert(~cond) } diff --git a/tests/run/tasty-interpolation-1/Macro.scala b/tests/run/tasty-interpolation-1/Macro.scala index 2846a1030642..4735b1bd4904 100644 --- a/tests/run/tasty-interpolation-1/Macro.scala +++ b/tests/run/tasty-interpolation-1/Macro.scala @@ -59,19 +59,19 @@ abstract class MacroStringInterpolator[T] { case Term.Select(Term.Typed(Term.Apply(_, List(Term.Apply(_, List(Term.Typed(Term.Repeated(strCtxArgTrees, _), TypeTree.Inferred()))))), _), _) => val strCtxArgs = strCtxArgTrees.map { case Term.Literal(Constant.String(str)) => str - case tree => throw new NotStaticlyKnownError("Expected statically known StringContext", tree.seal[Any]) + case tree => throw new NotStaticlyKnownError("Expected statically known StringContext", tree.seal.asExprOf[Any]) } StringContext(strCtxArgs: _*) case tree => - throw new NotStaticlyKnownError("Expected statically known StringContext", tree.seal[Any]) + throw new NotStaticlyKnownError("Expected statically known StringContext", tree.seal.asExprOf[Any]) } } protected def getArgsList(argsExpr: Expr[Seq[Any]])(implicit reflect: Reflection): List[Expr[Any]] = { import reflect._ argsExpr.unseal.underlyingArgument match { - case Term.Typed(Term.Repeated(args, _), _) => args.map(_.seal[Any]) - case tree => throw new NotStaticlyKnownError("Expected statically known argument list", tree.seal[Any]) + case Term.Typed(Term.Repeated(args, _), _) => args.map(_.seal.asExprOf[Any]) + case tree => throw new NotStaticlyKnownError("Expected statically known argument list", tree.seal.asExprOf[Any]) } } diff --git a/tests/run/tasty-macro-assert/quoted_1.scala b/tests/run/tasty-macro-assert/quoted_1.scala index ae804284fa9e..2a92fcfcbc50 100644 --- a/tests/run/tasty-macro-assert/quoted_1.scala +++ b/tests/run/tasty-macro-assert/quoted_1.scala @@ -35,8 +35,8 @@ object Asserts { tree match { case Term.Inlined(_, Nil, Term.Apply(Term.Select(OpsTree(left), op), right :: Nil)) => op match { - case "===" => '(assertEquals(~left.seal[Any], ~right.seal[Any])) - case "!==" => '(assertNotEquals(~left.seal[Any], ~right.seal[Any])) + case "===" => '(assertEquals(~left.seal.asExprOf[Any], ~right.seal.asExprOf[Any])) + case "!==" => '(assertNotEquals(~left.seal.asExprOf[Any], ~right.seal.asExprOf[Any])) } case _ => '(assertTrue(~cond)) diff --git a/tests/run/tasty-macro-const/quoted_1.scala b/tests/run/tasty-macro-const/quoted_1.scala index 9d4a5dc970c3..8c780dafa1c1 100644 --- a/tests/run/tasty-macro-const/quoted_1.scala +++ b/tests/run/tasty-macro-const/quoted_1.scala @@ -12,7 +12,7 @@ object Macros { case Term.Literal(Constant.Int(n)) => if (n <= 0) throw new QuoteError("Parameter must be natural number") - xTree.seal[Int] + xTree.seal.asExprOf[Int] case _ => throw new QuoteError("Parameter must be a known constant") } diff --git a/tests/run/tasty-seal-method/quoted_1.scala b/tests/run/tasty-seal-method/quoted_1.scala index 09ecf2b9e936..2b7dfe7282e4 100644 --- a/tests/run/tasty-seal-method/quoted_1.scala +++ b/tests/run/tasty-seal-method/quoted_1.scala @@ -16,10 +16,10 @@ object Asserts { fn.tpe.widen match { case Type.IsMethodType(_) => args.size match { - case 0 => fn.seal[() => Int].apply() - case 1 => fn.seal[Int => Int].apply('(0)) - case 2 => fn.seal[(Int, Int) => Int].apply('(0), '(0)) - case 3 => fn.seal[(Int, Int, Int) => Int].apply('(0), '(0), '(0)) + case 0 => fn.seal.asExprOf[() => Int].apply() + case 1 => fn.seal.asExprOf[Int => Int].apply('(0)) + case 2 => fn.seal.asExprOf[(Int, Int) => Int].apply('(0), '(0)) + case 3 => fn.seal.asExprOf[(Int, Int, Int) => Int].apply('(0), '(0), '(0)) } } case _ => x @@ -37,15 +37,15 @@ object Asserts { case Term.Apply(fn, args) => val pre = rec(fn) args.size match { - case 0 => pre.seal[() => Any].apply().unseal - case 1 => pre.seal[Int => Any].apply('(0)).unseal - case 2 => pre.seal[(Int, Int) => Any].apply('(0), '(0)).unseal - case 3 => pre.seal[(Int, Int, Int) => Any].apply('(0), '(0), '(0)).unseal + case 0 => pre.seal.asExprOf[() => Any].apply().unseal + case 1 => pre.seal.asExprOf[Int => Any].apply('(0)).unseal + case 2 => pre.seal.asExprOf[(Int, Int) => Any].apply('(0), '(0)).unseal + case 3 => pre.seal.asExprOf[(Int, Int, Int) => Any].apply('(0), '(0), '(0)).unseal } case _ => term } - rec(x.unseal.underlyingArgument).seal[Int] + rec(x.unseal.underlyingArgument).seal.asExprOf[Int] } } diff --git a/tests/run/tasty-tree-map/quoted_1.scala b/tests/run/tasty-tree-map/quoted_1.scala index ceb98beb1343..2dd555bda9a7 100644 --- a/tests/run/tasty-tree-map/quoted_1.scala +++ b/tests/run/tasty-tree-map/quoted_1.scala @@ -8,7 +8,7 @@ object Macros { def impl[T: Type](x: Expr[T])(implicit reflection: Reflection): Expr[T] = { import reflection._ val identityMap = new TreeMap { } - val transformed = identityMap.transformTerm(x.unseal).seal[T] + val transformed = identityMap.transformTerm(x.unseal).seal.asExprOf[T] transformed } diff --git a/tests/run/xml-interpolation-1/XmlQuote_1.scala b/tests/run/xml-interpolation-1/XmlQuote_1.scala index 56c9a624ae20..fe62ce46bc90 100644 --- a/tests/run/xml-interpolation-1/XmlQuote_1.scala +++ b/tests/run/xml-interpolation-1/XmlQuote_1.scala @@ -27,7 +27,7 @@ object XmlQuote { def liftListOfAny(lst: List[Term]): Expr[List[Any]] = lst match { case x :: xs => - val head = x.seal[Any] + val head = x.seal.asExprOf[Any] val tail = liftListOfAny(xs) '{ ~head :: ~tail } case Nil => '(Nil) diff --git a/tests/run/xml-interpolation-2/XmlQuote_1.scala b/tests/run/xml-interpolation-2/XmlQuote_1.scala index c51f145f9642..5b87c0d3b969 100644 --- a/tests/run/xml-interpolation-2/XmlQuote_1.scala +++ b/tests/run/xml-interpolation-2/XmlQuote_1.scala @@ -56,7 +56,7 @@ object XmlQuote { // [a0, ...]: Any* val args2: Expr[List[Any]] = args.unseal.underlyingArgument match { case Typed(Repeated(args0, _), _) => // statically known args, make list directly - args0.map(_.seal[Any]).toExprOfList + args0.map(_.seal.asExprOf[Any]).toExprOfList case _ => '((~args).toList)