diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/QuotedOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/QuotedOpsImpl.scala index 3407046672eb..0aee5d39f569 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/QuotedOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/QuotedOpsImpl.scala @@ -48,4 +48,11 @@ trait QuotedOpsImpl extends scala.tasty.reflect.QuotedOps with CoreImpl { } } } + + def TypeToQuoteDeco(tpe: Types.Type): TypeToQuotedAPI = new TypeToQuotedAPI { + def seal(implicit ctx: Context): quoted.Type[_] = { + val dummyPos = ctx.owner.pos // FIXME + new scala.quoted.Types.TreeType(tpd.TypeTree(tpe).withPos(dummyPos)) + } + } } diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index 6a04d9bc01fa..510df2ef3f65 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -174,8 +174,6 @@ class Staging extends MacroTransformWithImplicits { val rhs = transform(tag.select(tpnme.UNARY_~)) val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs) - val original = typeRef.symbol.asType - val local = ctx.newSymbol( owner = ctx.owner, name = UniqueName.fresh("T".toTermName).toTypeName, diff --git a/library/src-bootstrapped/scala/tasty/reflect/utils/TreeUtils.scala b/library/src-bootstrapped/scala/tasty/reflect/utils/TreeUtils.scala new file mode 100644 index 000000000000..4144a767977c --- /dev/null +++ b/library/src-bootstrapped/scala/tasty/reflect/utils/TreeUtils.scala @@ -0,0 +1,22 @@ +package scala.tasty +package reflect.utils + +import scala.quoted._ + +trait TreeUtils { + + val reflect: Reflection + import reflect._ + + def let(rhs: Term)(in: Term.Ident => Term): Term = { + type T // TODO probably it is better to use the Sealed contruct rather than let the user create their own existential type + implicit val rhsTpe: quoted.Type[T] = rhs.tpe.seal.asInstanceOf[quoted.Type[T]] + val rhsExpr = rhs.seal[T] + val expr = '{ + val x = ~rhsExpr + ~in(('(x)).unseal.asInstanceOf[Term.Ident]).seal[Any] + } + expr.unseal + } + +} diff --git a/library/src-non-bootstrapped/scala/tasty/reflect/utils/TreeUtils.scala b/library/src-non-bootstrapped/scala/tasty/reflect/utils/TreeUtils.scala new file mode 100644 index 000000000000..d532aa2883f6 --- /dev/null +++ b/library/src-non-bootstrapped/scala/tasty/reflect/utils/TreeUtils.scala @@ -0,0 +1,11 @@ +package scala.tasty +package reflect.utils + +trait TreeUtils { + + val reflect: Reflection + import reflect._ + + def let(rhs: Term)(in: Term.Ident => Term): Term = throw new Exception("non bootstrpped lib") + +} diff --git a/library/src/scala/tasty/Reflection.scala b/library/src/scala/tasty/Reflection.scala index 7828444a830c..a512c990798d 100644 --- a/library/src/scala/tasty/Reflection.scala +++ b/library/src/scala/tasty/Reflection.scala @@ -21,10 +21,14 @@ abstract class Reflection with TreeOps with TreeUtils with TypeOrBoundsTreeOps - with TypeOrBoundsOps { + with TypeOrBoundsOps { self => def typeOf[T: scala.quoted.Type]: Type = implicitly[scala.quoted.Type[T]].unseal.tpe + + val util: reflect.utils.TreeUtils { val reflect: self.type } = new reflect.utils.TreeUtils { + val reflect: self.type = self + } } object Reflection { diff --git a/library/src/scala/tasty/reflect/QuotedOps.scala b/library/src/scala/tasty/reflect/QuotedOps.scala index 4e1cbfd2b47a..65958da3d01f 100644 --- a/library/src/scala/tasty/reflect/QuotedOps.scala +++ b/library/src/scala/tasty/reflect/QuotedOps.scala @@ -21,4 +21,9 @@ trait QuotedOps extends Core { } implicit def TermToQuoteDeco(term: Term): TermToQuotedAPI + trait TypeToQuotedAPI { + /** Convert `Type` to an `quoted.Type[T]` */ + def seal(implicit ctx: Context): scala.quoted.Type[_] + } + implicit def TypeToQuoteDeco(tpe: Type): TypeToQuotedAPI } diff --git a/tests/run-with-compiler/tasty-unsafe-let.check b/tests/run-with-compiler/tasty-unsafe-let.check new file mode 100644 index 000000000000..882e63b72834 --- /dev/null +++ b/tests/run-with-compiler/tasty-unsafe-let.check @@ -0,0 +1,7 @@ +1 +1 +null +null +foo +bar +bar diff --git a/tests/run-with-compiler/tasty-unsafe-let/quoted_1.scala b/tests/run-with-compiler/tasty-unsafe-let/quoted_1.scala new file mode 100644 index 000000000000..793cb85c9e47 --- /dev/null +++ b/tests/run-with-compiler/tasty-unsafe-let/quoted_1.scala @@ -0,0 +1,22 @@ +import scala.quoted._ + +import scala.tasty._ + +object Macros { + + inline def let[T](rhs: T)(body: => T => Unit): Unit = + ~impl('(rhs), '(body)) + + private def impl[T](rhs: Expr[T], body: Expr[T => Unit])(implicit reflect: Reflection): Expr[Unit] = { + import reflect._ + + val rhsTerm = rhs.unseal + + import reflect.util.{let => letTerm} + letTerm(rhsTerm) { rhsId => + body(rhsId.seal[Any].asInstanceOf[Expr[T]]).unseal // Dangerous uncheked cast! + }.seal[Unit] + } + + +} diff --git a/tests/run-with-compiler/tasty-unsafe-let/quoted_2.scala b/tests/run-with-compiler/tasty-unsafe-let/quoted_2.scala new file mode 100644 index 000000000000..10189f718ff9 --- /dev/null +++ b/tests/run-with-compiler/tasty-unsafe-let/quoted_2.scala @@ -0,0 +1,17 @@ + +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + let(1)(x => { println(x); println(x) }) + let(null)(x => { println(x); println(x) }) + let { + println("foo") + "bar" + } { x => + println(x) + println(x) + } + } + +}