diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala index d7691a549da2..ccc6ed98b419 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala @@ -272,8 +272,10 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend override def unapply(x: Any): Option[Term] = x match case _ if Unapply_TypeTest.unapply(x).isDefined => None case _: tpd.PatternTree @unchecked => None - case x: tpd.SeqLiteral @unchecked => Some(x) case x: tpd.Tree @unchecked if x.isTerm => Some(x) + case x: tpd.SeqLiteral @unchecked => Some(x) + case x: tpd.Inlined @unchecked => Some(x) + case x: tpd.NamedArg @unchecked => Some(x) case _ => None } diff --git a/library/src-bootstrapped/scala/quoted/Expr.scala b/library/src-bootstrapped/scala/quoted/Expr.scala index e5a3eeaccec8..881b39062f43 100644 --- a/library/src-bootstrapped/scala/quoted/Expr.scala +++ b/library/src-bootstrapped/scala/quoted/Expr.scala @@ -41,11 +41,14 @@ abstract class Expr[+T] private[scala] { !scala.internal.quoted.Expr.unapply[EmptyTuple, EmptyTuple](this)(using that, false, qctx).isEmpty /** Checked cast to a `quoted.Expr[U]` */ - def cast[U](using tp: scala.quoted.Type[U])(using qctx: QuoteContext): scala.quoted.Expr[U] = { + def cast[U](using tp: scala.quoted.Type[U])(using qctx: QuoteContext): scala.quoted.Expr[U] = asExprOf[U] + + /** Convert this to an `quoted.Expr[X]` if this expression is a valid expression of type `X` or throws */ + def asExprOf[X](using tp: scala.quoted.Type[X])(using qctx: QuoteContext): scala.quoted.Expr[X] = { val tree = this.unseal val expectedType = tp.unseal.tpe if (tree.tpe <:< expectedType) - this.asInstanceOf[scala.quoted.Expr[U]] + this.asInstanceOf[scala.quoted.Expr[X]] else throw new scala.tasty.reflect.ExprCastError( s"""Expr: ${tree.show} @@ -178,7 +181,7 @@ object Expr { /** 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)(using qctx: QuoteContext): Expr[Tuple.InverseMap[T, Expr]] = { val elems: Seq[Expr[Any]] = tup.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Expr[Any]]] - ofTupleFromSeq(elems).cast[Tuple.InverseMap[T, Expr]] + ofTupleFromSeq(elems).asExprOf[Tuple.InverseMap[T, Expr]] } /** Find an implicit of type `T` in the current scope given by `qctx`. diff --git a/library/src-bootstrapped/scala/quoted/util/ExprMap.scala b/library/src-bootstrapped/scala/quoted/util/ExprMap.scala index aba68634bd35..bc2237c344b2 100644 --- a/library/src-bootstrapped/scala/quoted/util/ExprMap.scala +++ b/library/src-bootstrapped/scala/quoted/util/ExprMap.scala @@ -102,22 +102,18 @@ trait ExprMap { } def transformTerm(tree: Term, tpe: Type)(using ctx: Context): Term = - tree match { - case _: Closure => - tree - case _: Inlined => - transformTermChildren(tree, tpe) - case _ => - tree.tpe.widen match { - case _: MethodType | _: PolyType => - transformTermChildren(tree, tpe) - case _ => - type X - val expr = tree.seal.asInstanceOf[Expr[X]] - val t = tpe.seal.asInstanceOf[quoted.Type[X]] - transform(expr)(using qctx, t).unseal - } - } + tree match + case _: Closure => + tree + case _: Inlined => + transformTermChildren(tree, tpe) + case _ if tree.isExpr => + type X + val expr = tree.seal.asInstanceOf[Expr[X]] + val t = tpe.seal.asInstanceOf[quoted.Type[X]] + transform(expr)(using qctx, t).unseal + case _ => + transformTermChildren(tree, tpe) def transformTypeTree(tree: TypeTree)(using ctx: Context): TypeTree = tree @@ -155,7 +151,7 @@ trait ExprMap { trees mapConserve (transformTypeCaseDef(_)) } - new MapChildren().transformTermChildren(e.unseal, tpe.unseal.tpe).seal.cast[T] // Cast will only fail if this implementation has a bug + new MapChildren().transformTermChildren(e.unseal, tpe.unseal.tpe).asExprOf[T] } } diff --git a/library/src-non-bootstrapped/scala/quoted/Expr.scala b/library/src-non-bootstrapped/scala/quoted/Expr.scala index 065ab4bca282..ab2287789a9d 100644 --- a/library/src-non-bootstrapped/scala/quoted/Expr.scala +++ b/library/src-non-bootstrapped/scala/quoted/Expr.scala @@ -2,3 +2,4 @@ package scala.quoted abstract class Expr[+T] private[scala]: def unseal(using qctx: QuoteContext): qctx.tasty.Term + def asExprOf[X](using tp: scala.quoted.Type[X])(using qctx: QuoteContext): scala.quoted.Expr[X] = ??? diff --git a/library/src/scala/tasty/Reflection.scala b/library/src/scala/tasty/Reflection.scala index 238a2f7b320b..8d70981ec7ad 100644 --- a/library/src/scala/tasty/Reflection.scala +++ b/library/src/scala/tasty/Reflection.scala @@ -488,7 +488,28 @@ class Reflection(private[scala] val internal: CompilerInterface) { self => /** Shows the tree as fully typed source code */ def showWith(syntaxHighlight: SyntaxHighlight)(using ctx: Context): String = new SourceCodePrinter[self.type](self)(syntaxHighlight).showTree(tree) + + /** Does this tree represent a valid expression? */ + def isExpr(using ctx: Context): Boolean = + tree match + case tree: Term => + tree.tpe.widen match + case _: MethodType | _: PolyType => false + case _ => true + case _ => false + end extension + + + /** Convert this tree to an `quoted.Expr[T]` if the tree is a valid expression or throws */ + extension [T](tree: Tree) + def asExprOf(using scala.quoted.Type[T])(using QuoteContext): scala.quoted.Expr[T] = + if tree.isExpr then + new scala.internal.quoted.Expr(tree, internal.compilerId).asExprOf[T] + else tree match + case tree: Term => throw new Exception("Expected an expression. This is a partially applied Term. Try eta-expanding the term first.") + case _ => throw new Exception("Expected a Term but was: " + tree) + end Tree given (using ctx: Context) as TypeTest[Tree, PackageClause] = internal.PackageClause_TypeTest @@ -655,15 +676,13 @@ class Reflection(private[scala] val internal: CompilerInterface) { self => /** Convert `Term` to an `quoted.Expr[Any]` if the term is a valid expression or throws */ def seal(using ctx: Context): scala.quoted.Expr[Any] = - sealOpt.getOrElse { - throw new Exception("Cannot seal a partially applied Term. Try eta-expanding the term first.") - } + if self.isExpr then new scala.internal.quoted.Expr(self, internal.compilerId) + else throw new Exception("Cannot seal a partially applied Term. Try eta-expanding the term first.") /** Convert `Term` to an `quoted.Expr[Any]` if the term is a valid expression */ def sealOpt(using ctx: Context): Option[scala.quoted.Expr[Any]] = - self.tpe.widen match - case _: MethodType | _: PolyType => None - case _ => Some(new scala.internal.quoted.Expr(self, internal.compilerId)) + if self.isExpr then Some(new scala.internal.quoted.Expr(self, internal.compilerId)) + else None /** Type of this term */ def tpe(using ctx: Context): Type = internal.Term_tpe(self)