diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index c6982f1ad5f0..73dc082bfe00 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -15,7 +15,7 @@ import dotty.tools.dotc.core.tasty.TreePickler.Hole import dotty.tools.dotc.core.tasty.{ PositionPickler, TastyPickler, TastyPrinter } import dotty.tools.dotc.core.tasty.TreeUnpickler.UnpickleMode import dotty.tools.dotc.quoted.QuoteContext -import dotty.tools.dotc.tastyreflect.ReflectionImpl +import dotty.tools.dotc.tastyreflect.{ReflectionImpl, TastyTreeExpr, TreeType} import dotty.tools.tasty.TastyString @@ -38,14 +38,14 @@ object PickledQuotes { /** Transform the expression into its fully spliced Tree */ def quotedExprToTree[T](expr: quoted.Expr[T])(implicit ctx: Context): Tree = { - val expr1 = expr.asInstanceOf[TastyTreeExpr[Tree]] + val expr1 = expr.asInstanceOf[TastyTreeExpr] QuoteContext.checkScopeId(expr1.scopeId) healOwner(expr1.tree) } /** Transform the expression into its fully spliced TypeTree */ def quotedTypeToTree(tpe: quoted.Type[?])(implicit ctx: Context): Tree = { - val tpe1 = tpe.asInstanceOf[TreeType[Tree]] + val tpe1 = tpe.asInstanceOf[TreeType] QuoteContext.checkScopeId(tpe1.scopeId) healOwner(tpe1.typeTree) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 63a99d31676c..40447a4bbc81 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -38,7 +38,7 @@ import dotty.tools.dotc.quoted.QuoteContext import dotty.tools.tasty.TastyFormat._ import scala.quoted -import scala.internal.quoted.{TastyTreeExpr, TreeType} +import dotty.tools.dotc.tastyreflect.{TastyTreeExpr, TreeType} import scala.annotation.constructorOnly import scala.annotation.internal.sharable diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala index c930b9ec5801..c90334ab8543 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala @@ -35,10 +35,10 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend // def unpickleExpr(repr: Unpickler.PickledQuote, args: Unpickler.PickledExprArgs): scala.quoted.Expr[?] = - new scala.internal.quoted.TastyTreeExpr(PickledQuotes.unpickleExpr(repr, args), compilerId) + new TastyTreeExpr(PickledQuotes.unpickleExpr(repr, args), compilerId) def unpickleType(repr: Unpickler.PickledQuote, args: Unpickler.PickledTypeArgs): scala.quoted.Type[?] = - new scala.internal.quoted.TreeType(PickledQuotes.unpickleType(repr, args), compilerId) + new TreeType(PickledQuotes.unpickleType(repr, args), compilerId) // // CONTEXT @@ -1779,7 +1779,7 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend /** Convert `Term` to an `quoted.Expr[Any]` */ def QuotedExpr_seal(self: Term)(given ctx: Context): scala.quoted.Expr[Any] = self.tpe.widen match { case _: Types.MethodType | _: Types.PolyType => throw new Exception("Cannot seal a partially applied Term. Try eta-expanding the term first.") - case _ => new scala.internal.quoted.TastyTreeExpr(self, compilerId) + case _ => new TastyTreeExpr(self, compilerId) } /** Checked cast to a `quoted.Expr[U]` */ @@ -1799,7 +1799,7 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend /** Convert `Type` to an `quoted.Type[?]` */ def QuotedType_seal(self: Type)(given ctx: Context): scala.quoted.Type[?] = { val dummySpan = ctx.owner.span // FIXME - new scala.internal.quoted.TreeType(tpd.TypeTree(self).withSpan(dummySpan), compilerId) + new TreeType(tpd.TypeTree(self).withSpan(dummySpan), compilerId) } // @@ -1988,4 +1988,3 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend private def compilerId: Int = rootContext.outersIterator.toList.last.hashCode() } - diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TastyTreeExpr.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TastyTreeExpr.scala new file mode 100644 index 000000000000..8330b9d8782d --- /dev/null +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TastyTreeExpr.scala @@ -0,0 +1,22 @@ +package dotty.tools.dotc.tastyreflect + +import dotty.tools.dotc.ast.tpd.Tree + +/** An Expr backed by a tree. Only the current compiler trees are allowed. + * + * These expressions are used for arguments of macros. They contain and actual tree + * from the program that is being expanded by the macro. + * + * May contain references to code defined outside this TastyTreeExpr instance. + */ +final class TastyTreeExpr(val tree: Tree, val scopeId: Int) extends scala.quoted.Expr[Any] { + override def equals(that: Any): Boolean = that match { + case that: TastyTreeExpr => + // TastyTreeExpr are wrappers around trees, therfore they are equals if their trees are equal. + // All scopeId should be equal unless two different runs of the compiler created the trees. + tree == that.tree && scopeId == that.scopeId + case _ => false + } + override def hashCode: Int = tree.hashCode + override def toString: String = s"Expr()" +} diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TreeType.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TreeType.scala new file mode 100644 index 000000000000..4bdf2ca41b55 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TreeType.scala @@ -0,0 +1,16 @@ +package dotty.tools.dotc.tastyreflect + +import dotty.tools.dotc.ast.tpd.Tree + +/** An Type backed by a tree */ +final class TreeType(val typeTree: Tree, val scopeId: Int) extends scala.quoted.Type[Any] { + override def equals(that: Any): Boolean = that match { + case that: TreeType => typeTree == + // TastyTreeExpr are wrappers around trees, therfore they are equals if their trees are equal. + // All scopeId should be equal unless two different runs of the compiler created the trees. + that.typeTree && scopeId == that.scopeId + case _ => false + } + override def hashCode: Int = typeTree.hashCode + override def toString: String = s"Type()" +} diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index cc7f6690c10a..e6c7a600ea52 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -17,7 +17,7 @@ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.{NameKinds, TypeErasure} import dotty.tools.dotc.core.Constants.Constant -import dotty.tools.dotc.tastyreflect.ReflectionImpl +import dotty.tools.dotc.tastyreflect.{ReflectionImpl, TastyTreeExpr, TreeType} import scala.util.control.NonFatal import dotty.tools.dotc.util.SourcePosition @@ -246,10 +246,10 @@ object Splicer { } private def interpretQuote(tree: Tree)(implicit env: Env): Object = - new scala.internal.quoted.TastyTreeExpr(Inlined(EmptyTree, Nil, tree).withSpan(tree.span), QuoteContext.scopeId) + new TastyTreeExpr(Inlined(EmptyTree, Nil, tree).withSpan(tree.span), QuoteContext.scopeId) private def interpretTypeQuote(tree: Tree)(implicit env: Env): Object = - new scala.internal.quoted.TreeType(tree, QuoteContext.scopeId) + new TreeType(tree, QuoteContext.scopeId) private def interpretLiteral(value: Any)(implicit env: Env): Object = value.asInstanceOf[Object] diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index 45b57db95e8e..896506888b33 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -1,272 +1,246 @@ -package scala - -package quoted { - - import scala.quoted.show.SyntaxHighlight - - sealed trait Expr[+T] { - - /** Show a source code like representation of this expression without syntax highlight */ - def show(implicit qctx: QuoteContext): String = qctx.show(this, SyntaxHighlight.plain) - - /** Show a source code like representation of this expression */ - def show(syntaxHighlight: SyntaxHighlight)(implicit qctx: QuoteContext): String = qctx.show(this, syntaxHighlight) - - /** Return the value of this expression. - * - * Returns `None` if the expression does not contain a value or contains side effects. - * Otherwise returns the `Some` of the value. - */ - final def getValue[U >: T](given qctx: QuoteContext, valueOf: ValueOfExpr[U]): Option[U] = valueOf(this) - - /** Pattern matches `this` against `that`. Effectively performing a deep equality check. - * It does the equivalent of - * ``` - * this match - * case '{...} => true // where the contens of the pattern are the contents of `that` - * case _ => false - * ``` - */ - final def matches(that: Expr[Any])(given qctx: QuoteContext): Boolean = - !scala.internal.quoted.Expr.unapply[Unit, Unit](this)(given that, false, qctx).isEmpty - - /** 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 from by-value to by-name. - */ - def underlyingArgument(given qctx: QuoteContext): Expr[T] = { - import qctx.tasty.{given, _} - this.unseal.underlyingArgument.seal.asInstanceOf[Expr[T]] - } +package scala.quoted + +import scala.quoted.show.SyntaxHighlight + +/** Quoted expression of type `T` + * + * Restriction: only the QuoteContext.tasty.internal implementation is allowed to extend this trait. + * Any other implementation will result in an undefined behavior. + */ +trait Expr[+T] { + + /** Show a source code like representation of this expression without syntax highlight */ + def show(implicit qctx: QuoteContext): String = qctx.show(this, SyntaxHighlight.plain) + + /** Show a source code like representation of this expression */ + def show(syntaxHighlight: SyntaxHighlight)(implicit qctx: QuoteContext): String = qctx.show(this, syntaxHighlight) + + /** Return the value of this expression. + * + * Returns `None` if the expression does not contain a value or contains side effects. + * Otherwise returns the `Some` of the value. + */ + final def getValue[U >: T](given qctx: QuoteContext, valueOf: ValueOfExpr[U]): Option[U] = valueOf(this) + + /** Pattern matches `this` against `that`. Effectively performing a deep equality check. + * It does the equivalent of + * ``` + * this match + * case '{...} => true // where the contens of the pattern are the contents of `that` + * case _ => false + * ``` + */ + final def matches(that: Expr[Any])(given qctx: QuoteContext): Boolean = + !scala.internal.quoted.Expr.unapply[Unit, Unit](this)(given that, false, qctx).isEmpty + + /** 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 from by-value to by-name. + */ + def underlyingArgument(given qctx: QuoteContext): Expr[T] = { + import qctx.tasty.{given, _} + this.unseal.underlyingArgument.seal.asInstanceOf[Expr[T]] } +} - object Expr { - - import scala.internal.quoted._ - - /** Converts a tuple `(T1, ..., Tn)` to `(Expr[T1], ..., Expr[Tn])` */ - type TupleOfExpr[Tup <: Tuple] = Tuple.Map[Tup, [X] =>> (given QuoteContext) => Expr[X]] - - /** `Expr.betaReduce(f)(x1, ..., xn)` is functionally the same as `'{($f)($x1, ..., $xn)}`, however it optimizes this call - * by returning the result of beta-reducing `f(x1, ..., xn)` if `f` is a known lambda expression. - * - * `Expr.betaReduce` distributes applications of `Expr` over function arrows - * ```scala - * Expr.betaReduce(_): Expr[(T1, ..., Tn) => R] => ((Expr[T1], ..., Expr[Tn]) => Expr[R]) - * ``` - */ - def betaReduce[F, Args <: Tuple, R, G](f: Expr[F])(given tf: TupledFunction[F, Args => R], tg: TupledFunction[G, TupleOfExpr[Args] => Expr[R]], qctx: QuoteContext): G = { - import qctx.tasty.{_, given} - tg.untupled(args => qctx.tasty.internal.betaReduce(f.unseal, args.toArray.toList.map(_.asInstanceOf[QuoteContext => Expr[_]](qctx).unseal)).seal.asInstanceOf[Expr[R]]) - } - - /** `Expr.betaReduceGiven(f)(x1, ..., xn)` is functionally the same as `'{($f)(given $x1, ..., $xn)}`, however it optimizes this call - * by returning the result of beta-reducing `f(given x1, ..., xn)` if `f` is a known lambda expression. - * - * `Expr.betaReduceGiven` distributes applications of `Expr` over function arrows - * ```scala - * Expr.betaReduceGiven(_): Expr[(given T1, ..., Tn) => R] => ((Expr[T1], ..., Expr[Tn]) => Expr[R]) - * ``` - * Note: The - */ - def betaReduceGiven[F, Args <: Tuple, R, G](f: Expr[F])(given tf: TupledFunction[F, (given Args) => R], tg: TupledFunction[G, TupleOfExpr[Args] => Expr[R]], qctx: QuoteContext): G = { - import qctx.tasty.{_, given} - tg.untupled(args => qctx.tasty.internal.betaReduce(f.unseal, args.toArray.toList.map(_.asInstanceOf[QuoteContext => Expr[_]](qctx).unseal)).seal.asInstanceOf[Expr[R]]) - } - - /** Returns a null expresssion equivalent to `'{null}` */ - def nullExpr: (given QuoteContext) => Expr[Null] = (given qctx) => { - import qctx.tasty.{_, given} - Literal(Constant(null)).seal.asInstanceOf[Expr[Null]] - } - - /** Returns a unit expresssion equivalent to `'{}` or `'{()}` */ - def unitExpr: (given QuoteContext) => Expr[Unit] = (given qctx) => { - import qctx.tasty.{_, given} - Literal(Constant(())).seal.asInstanceOf[Expr[Unit]] - } +object Expr { - /** Returns an expression containing a block with the given statements and ending with the expresion - * Given list of statements `s1 :: s2 :: ... :: Nil` and an expression `e` the resulting expression - * will be equivalent to `'{ $s1; $s2; ...; $e }`. - */ - def block[T](statements: List[Expr[_]], expr: Expr[T])(given qctx: QuoteContext): Expr[T] = { - import qctx.tasty.{_, given} - Block(statements.map(_.unseal), expr.unseal).seal.asInstanceOf[Expr[T]] - } + import scala.internal.quoted._ - /** Lift a value into an expression containing the construction of that value */ - def apply[T: Liftable](x: T)(given QuoteContext): Expr[T] = summon[Liftable[T]].toExpr(x) + /** Converts a tuple `(T1, ..., Tn)` to `(Expr[T1], ..., Expr[Tn])` */ + type TupleOfExpr[Tup <: Tuple] = Tuple.Map[Tup, [X] =>> (given QuoteContext) => Expr[X]] - /** Lifts this sequence of expressions into an expression of a sequence - * - * Transforms a sequence of expression - * `Seq(e1, e2, ...)` where `ei: Expr[T]` - * to an expression equivalent to - * `'{ Seq($e1, $e2, ...) }` typed as an `Expr[Seq[T]]` - * - * Usage: - * ```scala - * '{ List(${Expr.ofSeq(List(1, 2, 3))}: _*) } // equvalent to '{ List(1, 2, 3) } - * ``` - */ - def ofSeq[T](xs: Seq[Expr[T]])(given tp: Type[T], qctx: QuoteContext): Expr[Seq[T]] = { - import qctx.tasty.{_, given} - Repeated(xs.map(_.unseal).toList, tp.unseal).seal.asInstanceOf[Expr[Seq[T]]] - } + /** `Expr.betaReduce(f)(x1, ..., xn)` is functionally the same as `'{($f)($x1, ..., $xn)}`, however it optimizes this call + * by returning the result of beta-reducing `f(x1, ..., xn)` if `f` is a known lambda expression. + * + * `Expr.betaReduce` distributes applications of `Expr` over function arrows + * ```scala + * Expr.betaReduce(_): Expr[(T1, ..., Tn) => R] => ((Expr[T1], ..., Expr[Tn]) => Expr[R]) + * ``` + */ + def betaReduce[F, Args <: Tuple, R, G](f: Expr[F])(given tf: TupledFunction[F, Args => R], tg: TupledFunction[G, TupleOfExpr[Args] => Expr[R]], qctx: QuoteContext): G = { + import qctx.tasty.{_, given} + tg.untupled(args => qctx.tasty.internal.betaReduce(f.unseal, args.toArray.toList.map(_.asInstanceOf[QuoteContext => Expr[_]](qctx).unseal)).seal.asInstanceOf[Expr[R]]) + } + /** `Expr.betaReduceGiven(f)(x1, ..., xn)` is functionally the same as `'{($f)(given $x1, ..., $xn)}`, however it optimizes this call + * by returning the result of beta-reducing `f(given x1, ..., xn)` if `f` is a known lambda expression. + * + * `Expr.betaReduceGiven` distributes applications of `Expr` over function arrows + * ```scala + * Expr.betaReduceGiven(_): Expr[(given T1, ..., Tn) => R] => ((Expr[T1], ..., Expr[Tn]) => Expr[R]) + * ``` + */ + def betaReduceGiven[F, Args <: Tuple, R, G](f: Expr[F])(given tf: TupledFunction[F, (given Args) => R], tg: TupledFunction[G, TupleOfExpr[Args] => Expr[R]], qctx: QuoteContext): G = { + import qctx.tasty.{_, given} + tg.untupled(args => qctx.tasty.internal.betaReduce(f.unseal, args.toArray.toList.map(_.asInstanceOf[QuoteContext => Expr[_]](qctx).unseal)).seal.asInstanceOf[Expr[R]]) + } - /** Lifts this list of expressions into an expression of a list - * - * Transforms a list of expression - * `List(e1, e2, ...)` where `ei: Expr[T]` - * to an expression equivalent to - * `'{ List($e1, $e2, ...) }` typed as an `Expr[List[T]]` - */ - def ofList[T](xs: Seq[Expr[T]])(given Type[T], QuoteContext): Expr[List[T]] = - if (xs.isEmpty) '{ Nil } else '{ List(${ofSeq(xs)}: _*) } + /** Returns a null expresssion equivalent to `'{null}` */ + def nullExpr: (given QuoteContext) => Expr[Null] = (given qctx) => { + import qctx.tasty.{_, given} + Literal(Constant(null)).seal.asInstanceOf[Expr[Null]] + } - /** Lifts this sequence of expressions into an expression of a tuple - * - * Transforms a sequence of expression - * `Seq(e1, e2, ...)` where `ei: Expr[_]` - * to an expression equivalent to - * `'{ ($e1, $e2, ...) }` typed as an `Expr[Tuple]` - */ - def ofTuple(seq: Seq[Expr[_]])(given QuoteContext): Expr[Tuple] = { - seq match { - case Seq() => - unitExpr - case Seq('{ $x1: $t1 }) => - '{ Tuple1($x1) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }) => - '{ Tuple2($x1, $x2) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }) => - '{ Tuple3($x1, $x2, $x3) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }) => - '{ Tuple4($x1, $x2, $x3, $x4) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }) => - '{ Tuple5($x1, $x2, $x3, $x4, $x5) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }) => - '{ Tuple6($x1, $x2, $x3, $x4, $x5, $x6) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }) => - '{ Tuple7($x1, $x2, $x3, $x4, $x5, $x6, $x7) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }) => - '{ Tuple8($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }) => - '{ Tuple9($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }) => - '{ Tuple10($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }) => - '{ Tuple11($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }) => - '{ Tuple12($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }) => - '{ Tuple13($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }) => - '{ Tuple14($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }) => - '{ Tuple15($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }, '{ $x16: $t16 }) => - '{ Tuple16($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }, '{ $x16: $t16 }, '{ $x17: $t17 }) => - '{ Tuple17($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }, '{ $x16: $t16 }, '{ $x17: $t17 }, '{ $x18: $t18 }) => - '{ Tuple18($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17, $x18) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }, '{ $x16: $t16 }, '{ $x17: $t17 }, '{ $x18: $t18 }, '{ $x19: $t19 }) => - '{ Tuple19($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17, $x18, $x19) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }, '{ $x16: $t16 }, '{ $x17: $t17 }, '{ $x18: $t18 }, '{ $x19: $t19 }, '{ $x20: $t20 }) => - '{ Tuple20($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17, $x18, $x19, $x20) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }, '{ $x16: $t16 }, '{ $x17: $t17 }, '{ $x18: $t18 }, '{ $x19: $t19 }, '{ $x20: $t20 }, '{ $x21: $t21 }) => - '{ Tuple21($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17, $x18, $x19, $x20, $x21) } - case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }, '{ $x16: $t16 }, '{ $x17: $t17 }, '{ $x18: $t18 }, '{ $x19: $t19 }, '{ $x20: $t20 }, '{ $x21: $t21 }, '{ $x22: $t22 }) => - '{ Tuple22($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17, $x18, $x19, $x20, $x21, $x22) } - case _ => - '{ Tuple.fromIArray(IArray(${ofSeq(seq)}: _*)) } - } - } + /** Returns a unit expresssion equivalent to `'{}` or `'{()}` */ + def unitExpr: (given QuoteContext) => Expr[Unit] = (given qctx) => { + import qctx.tasty.{_, given} + Literal(Constant(())).seal.asInstanceOf[Expr[Unit]] + } - /** Given a tuple of the form `(Expr[A1], ..., Expr[An])`, outputs a tuple `Expr[(A1, ..., An)]`. */ - def ofTuple[T <: Tuple: Tuple.IsMappedBy[Expr]: Type](tup: T) (given qctx: QuoteContext): Expr[Tuple.InverseMap[T, Expr]] = { - import qctx.tasty.{_, given} - val elems: Seq[Expr[_]] = tup.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Expr[_]]] - ofTuple(elems).cast[Tuple.InverseMap[T, Expr]] - } + /** Returns an expression containing a block with the given statements and ending with the expresion + * Given list of statements `s1 :: s2 :: ... :: Nil` and an expression `e` the resulting expression + * will be equivalent to `'{ $s1; $s2; ...; $e }`. + */ + def block[T](statements: List[Expr[_]], expr: Expr[T])(given qctx: QuoteContext): Expr[T] = { + import qctx.tasty.{_, given} + Block(statements.map(_.unseal), expr.unseal).seal.asInstanceOf[Expr[T]] + } - // TODO generalize for any function arity (see Expr.betaReduce) - def open[T1, R, X](f: Expr[T1 => R])(content: (Expr[R], [t] => Expr[t] => Expr[T1] => Expr[t]) => X)(given qctx: QuoteContext): X = { - import qctx.tasty.{given, _} - val (params, bodyExpr) = paramsAndBody(f) - content(bodyExpr, [t] => (e: Expr[t]) => (v: Expr[T1]) => bodyFn[t](e.unseal, params, List(v.unseal)).seal.asInstanceOf[Expr[t]]) - } + /** Lift a value into an expression containing the construction of that value */ + def apply[T: Liftable](x: T)(given QuoteContext): Expr[T] = summon[Liftable[T]].toExpr(x) + + /** Lifts this sequence of expressions into an expression of a sequence + * + * Transforms a sequence of expression + * `Seq(e1, e2, ...)` where `ei: Expr[T]` + * to an expression equivalent to + * `'{ Seq($e1, $e2, ...) }` typed as an `Expr[Seq[T]]` + * + * Usage: + * ```scala + * '{ List(${Expr.ofSeq(List(1, 2, 3))}: _*) } // equvalent to '{ List(1, 2, 3) } + * ``` + */ + def ofSeq[T](xs: Seq[Expr[T]])(given tp: Type[T], qctx: QuoteContext): Expr[Seq[T]] = { + import qctx.tasty.{_, given} + Repeated(xs.map(_.unseal).toList, tp.unseal).seal.asInstanceOf[Expr[Seq[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)(given qctx: QuoteContext)(given DummyImplicit): X = { - import qctx.tasty.{given, _} - val (params, bodyExpr) = paramsAndBody(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)(given qctx: QuoteContext)(given DummyImplicit, DummyImplicit): X = { - import qctx.tasty.{given, _} - val (params, bodyExpr) = paramsAndBody(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](given qctx: QuoteContext)(f: Expr[Any]) = { - import qctx.tasty.{given, _} - val Block(List(DefDef("$anonfun", Nil, List(params), _, Some(body))), Closure(Ident("$anonfun"), None)) = f.unseal.etaExpand - (params, body.seal.asInstanceOf[Expr[R]]) + /** Lifts this list of expressions into an expression of a list + * + * Transforms a list of expression + * `List(e1, e2, ...)` where `ei: Expr[T]` + * to an expression equivalent to + * `'{ List($e1, $e2, ...) }` typed as an `Expr[List[T]]` + */ + def ofList[T](xs: Seq[Expr[T]])(given Type[T], QuoteContext): Expr[List[T]] = + if (xs.isEmpty) '{ Nil } else '{ List(${ofSeq(xs)}: _*) } + + /** Lifts this sequence of expressions into an expression of a tuple + * + * Transforms a sequence of expression + * `Seq(e1, e2, ...)` where `ei: Expr[_]` + * to an expression equivalent to + * `'{ ($e1, $e2, ...) }` typed as an `Expr[Tuple]` + */ + def ofTuple(seq: Seq[Expr[_]])(given QuoteContext): Expr[Tuple] = { + seq match { + case Seq() => + unitExpr + case Seq('{ $x1: $t1 }) => + '{ Tuple1($x1) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }) => + '{ Tuple2($x1, $x2) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }) => + '{ Tuple3($x1, $x2, $x3) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }) => + '{ Tuple4($x1, $x2, $x3, $x4) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }) => + '{ Tuple5($x1, $x2, $x3, $x4, $x5) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }) => + '{ Tuple6($x1, $x2, $x3, $x4, $x5, $x6) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }) => + '{ Tuple7($x1, $x2, $x3, $x4, $x5, $x6, $x7) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }) => + '{ Tuple8($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }) => + '{ Tuple9($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }) => + '{ Tuple10($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }) => + '{ Tuple11($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }) => + '{ Tuple12($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }) => + '{ Tuple13($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }) => + '{ Tuple14($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }) => + '{ Tuple15($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }, '{ $x16: $t16 }) => + '{ Tuple16($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }, '{ $x16: $t16 }, '{ $x17: $t17 }) => + '{ Tuple17($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }, '{ $x16: $t16 }, '{ $x17: $t17 }, '{ $x18: $t18 }) => + '{ Tuple18($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17, $x18) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }, '{ $x16: $t16 }, '{ $x17: $t17 }, '{ $x18: $t18 }, '{ $x19: $t19 }) => + '{ Tuple19($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17, $x18, $x19) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }, '{ $x16: $t16 }, '{ $x17: $t17 }, '{ $x18: $t18 }, '{ $x19: $t19 }, '{ $x20: $t20 }) => + '{ Tuple20($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17, $x18, $x19, $x20) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }, '{ $x16: $t16 }, '{ $x17: $t17 }, '{ $x18: $t18 }, '{ $x19: $t19 }, '{ $x20: $t20 }, '{ $x21: $t21 }) => + '{ Tuple21($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17, $x18, $x19, $x20, $x21) } + case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }, '{ $x16: $t16 }, '{ $x17: $t17 }, '{ $x18: $t18 }, '{ $x19: $t19 }, '{ $x20: $t20 }, '{ $x21: $t21 }, '{ $x22: $t22 }) => + '{ Tuple22($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17, $x18, $x19, $x20, $x21, $x22) } + case _ => + '{ Tuple.fromIArray(IArray(${ofSeq(seq)}: _*)) } } + } - private def bodyFn[t](given qctx: QuoteContext)(e: qctx.tasty.Term, params: List[qctx.tasty.ValDef], args: List[qctx.tasty.Term]): qctx.tasty.Term = { - import qctx.tasty.{given, _} - val map = params.map(_.symbol).zip(args).toMap - new TreeMap { - override def transformTerm(tree: Term)(given ctx: Context): Term = - super.transformTerm(tree) match - case tree: Ident => map.getOrElse(tree.symbol, tree) - case tree => tree - }.transformTerm(e) - } + /** Given a tuple of the form `(Expr[A1], ..., Expr[An])`, outputs a tuple `Expr[(A1, ..., An)]`. */ + def ofTuple[T <: Tuple: Tuple.IsMappedBy[Expr]: Type](tup: T) (given qctx: QuoteContext): Expr[Tuple.InverseMap[T, Expr]] = { + import qctx.tasty.{_, given} + val elems: Seq[Expr[_]] = tup.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Expr[_]]] + ofTuple(elems).cast[Tuple.InverseMap[T, Expr]] + } + // TODO generalize for any function arity (see Expr.betaReduce) + def open[T1, R, X](f: Expr[T1 => R])(content: (Expr[R], [t] => Expr[t] => Expr[T1] => Expr[t]) => X)(given qctx: QuoteContext): X = { + import qctx.tasty.{given, _} + val (params, bodyExpr) = paramsAndBody(f) + content(bodyExpr, [t] => (e: Expr[t]) => (v: Expr[T1]) => bodyFn[t](e.unseal, params, List(v.unseal)).seal.asInstanceOf[Expr[t]]) } -} -package internal { - package quoted { + def open[T1, T2, R, X](f: Expr[(T1, T2) => R])(content: (Expr[R], [t] => Expr[t] => (Expr[T1], Expr[T2]) => Expr[t]) => X)(given qctx: QuoteContext)(given DummyImplicit): X = { + import qctx.tasty.{given, _} + val (params, bodyExpr) = paramsAndBody(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]]) + } - import scala.quoted.Expr + 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)(given qctx: QuoteContext)(given DummyImplicit, DummyImplicit): X = { + import qctx.tasty.{given, _} + val (params, bodyExpr) = paramsAndBody(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]]) + } - /** An Expr backed by a tree. Only the current compiler trees are allowed. - * - * These expressions are used for arguments of macros. They contain and actual tree - * from the program that is being expanded by the macro. - * - * May contain references to code defined outside this TastyTreeExpr instance. - */ - final class TastyTreeExpr[Tree](val tree: Tree, val scopeId: Int) extends Expr[Any] { - override def equals(that: Any): Boolean = that match { - case that: TastyTreeExpr[_] => - // TastyTreeExpr are wrappers around trees, therfore they are equals if their trees are equal. - // All scopeId should be equal unless two different runs of the compiler created the trees. - tree == that.tree && scopeId == that.scopeId - case _ => false - } - override def hashCode: Int = tree.hashCode - override def toString: String = s"Expr()" - } + private def paramsAndBody[R](given qctx: QuoteContext)(f: Expr[Any]) = { + import qctx.tasty.{given, _} + 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](given qctx: QuoteContext)(e: qctx.tasty.Term, params: List[qctx.tasty.ValDef], args: List[qctx.tasty.Term]): qctx.tasty.Term = { + import qctx.tasty.{given, _} + val map = params.map(_.symbol).zip(args).toMap + new TreeMap { + override def transformTerm(tree: Term)(given ctx: Context): Term = + super.transformTerm(tree) match + case tree: Ident => map.getOrElse(tree.symbol, tree) + case tree => tree + }.transformTerm(e) } + } diff --git a/library/src/scala/quoted/Type.scala b/library/src/scala/quoted/Type.scala index 79e71c7ad279..ae59b84f8c23 100644 --- a/library/src/scala/quoted/Type.scala +++ b/library/src/scala/quoted/Type.scala @@ -1,86 +1,69 @@ -package scala +package scala.quoted -package quoted { - import scala.quoted.show.SyntaxHighlight +import scala.quoted.show.SyntaxHighlight - sealed trait Type[T <: AnyKind] { - type `$splice` = T +/** Quoted type (or kind) `T` + * + * Restriction: only the QuoteContext.tasty.internal implementation is allowed to extend this trait. + * Any other implementation will result in an undefined behavior. + */ +trait Type[T <: AnyKind] { + type `$splice` = T - /** Show a source code like representation of this type without syntax highlight */ - def show(given qctx: QuoteContext): String = qctx.show(this, SyntaxHighlight.plain) + /** Show a source code like representation of this type without syntax highlight */ + def show(given qctx: QuoteContext): String = qctx.show(this, SyntaxHighlight.plain) - /** Show a source code like representation of this type */ - def show(syntaxHighlight: SyntaxHighlight)(given qctx: QuoteContext): String = qctx.show(this, syntaxHighlight) + /** Show a source code like representation of this type */ + def show(syntaxHighlight: SyntaxHighlight)(given qctx: QuoteContext): String = qctx.show(this, syntaxHighlight) +} + +/** Some basic type tags, currently incomplete */ +object Type { + + given UnitTag(given qctx: QuoteContext): Type[Unit] = { + import qctx.tasty.{_, given} + defn.UnitType.seal.asInstanceOf[quoted.Type[Unit]] } - /** Some basic type tags, currently incomplete */ - object Type { - - given UnitTag(given qctx: QuoteContext): Type[Unit] = { - import qctx.tasty.{_, given} - defn.UnitType.seal.asInstanceOf[quoted.Type[Unit]] - } - - given BooleanTag(given qctx: QuoteContext): Type[Boolean] = { - import qctx.tasty.{_, given} - defn.BooleanType.seal.asInstanceOf[quoted.Type[Boolean]] - } - - given ByteTag(given qctx: QuoteContext): Type[Byte] = { - import qctx.tasty.{_, given} - defn.ByteType.seal.asInstanceOf[quoted.Type[Byte]] - } - - given CharTag(given qctx: QuoteContext): Type[Char] = { - import qctx.tasty.{_, given} - defn.CharType.seal.asInstanceOf[quoted.Type[Char]] - } - - given ShortTag(given qctx: QuoteContext): Type[Short] = { - import qctx.tasty.{_, given} - defn.ShortType.seal.asInstanceOf[quoted.Type[Short]] - } - - given IntTag(given qctx: QuoteContext): Type[Int] = { - import qctx.tasty.{_, given} - defn.IntType.seal.asInstanceOf[quoted.Type[Int]] - } - - given LongTag(given qctx: QuoteContext): Type[Long] = { - import qctx.tasty.{_, given} - defn.LongType.seal.asInstanceOf[quoted.Type[Long]] - } - - given FloatTag(given qctx: QuoteContext): Type[Float] = { - import qctx.tasty.{_, given} - defn.FloatType.seal.asInstanceOf[quoted.Type[Float]] - } - - given DoubleTag(given qctx: QuoteContext): Type[Double] = { - import qctx.tasty.{_, given} - defn.DoubleType.seal.asInstanceOf[quoted.Type[Double]] - } + given BooleanTag(given qctx: QuoteContext): Type[Boolean] = { + import qctx.tasty.{_, given} + defn.BooleanType.seal.asInstanceOf[quoted.Type[Boolean]] + } + given ByteTag(given qctx: QuoteContext): Type[Byte] = { + import qctx.tasty.{_, given} + defn.ByteType.seal.asInstanceOf[quoted.Type[Byte]] } -} + given CharTag(given qctx: QuoteContext): Type[Char] = { + import qctx.tasty.{_, given} + defn.CharType.seal.asInstanceOf[quoted.Type[Char]] + } + + given ShortTag(given qctx: QuoteContext): Type[Short] = { + import qctx.tasty.{_, given} + defn.ShortType.seal.asInstanceOf[quoted.Type[Short]] + } + + given IntTag(given qctx: QuoteContext): Type[Int] = { + import qctx.tasty.{_, given} + defn.IntType.seal.asInstanceOf[quoted.Type[Int]] + } -package internal { - package quoted { - - /** An Type backed by a tree */ - final class TreeType[Tree](val typeTree: Tree, val scopeId: Int) extends scala.quoted.Type[Any] { - override def equals(that: Any): Boolean = that match { - case that: TreeType[_] => typeTree == - // TastyTreeExpr are wrappers around trees, therfore they are equals if their trees are equal. - // All scopeId should be equal unless two different runs of the compiler created the trees. - that.typeTree && scopeId == that.scopeId - case _ => false - } - override def hashCode: Int = typeTree.hashCode - override def toString: String = s"Type()" - } + given LongTag(given qctx: QuoteContext): Type[Long] = { + import qctx.tasty.{_, given} + defn.LongType.seal.asInstanceOf[quoted.Type[Long]] + } + given FloatTag(given qctx: QuoteContext): Type[Float] = { + import qctx.tasty.{_, given} + defn.FloatType.seal.asInstanceOf[quoted.Type[Float]] } + + given DoubleTag(given qctx: QuoteContext): Type[Double] = { + import qctx.tasty.{_, given} + defn.DoubleType.seal.asInstanceOf[quoted.Type[Double]] + } + } diff --git a/tests/patmat/i6255b.check b/tests/patmat/i6255b.check index e69de29bb2d1..582d2e65bc51 100644 --- a/tests/patmat/i6255b.check +++ b/tests/patmat/i6255b.check @@ -0,0 +1 @@ +2: Pattern Match Exhaustivity: _: Expr[Int]