Skip to content

Commit ea51ad7

Browse files
Merge pull request #9514 from dotty-staging/add-asExprOf
Add quoted.Expr.asExprOf and Reflection.Tree.{asExprOf, isExpr}
2 parents dfcc0b1 + e3e6831 commit ea51ad7

File tree

5 files changed

+48
-27
lines changed

5 files changed

+48
-27
lines changed

compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,10 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend
272272
override def unapply(x: Any): Option[Term] = x match
273273
case _ if Unapply_TypeTest.unapply(x).isDefined => None
274274
case _: tpd.PatternTree @unchecked => None
275-
case x: tpd.SeqLiteral @unchecked => Some(x)
276275
case x: tpd.Tree @unchecked if x.isTerm => Some(x)
276+
case x: tpd.SeqLiteral @unchecked => Some(x)
277+
case x: tpd.Inlined @unchecked => Some(x)
278+
case x: tpd.NamedArg @unchecked => Some(x)
277279
case _ => None
278280
}
279281

library/src-bootstrapped/scala/quoted/Expr.scala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,14 @@ abstract class Expr[+T] private[scala] {
4141
!scala.internal.quoted.Expr.unapply[EmptyTuple, EmptyTuple](this)(using that, false, qctx).isEmpty
4242

4343
/** Checked cast to a `quoted.Expr[U]` */
44-
def cast[U](using tp: scala.quoted.Type[U])(using qctx: QuoteContext): scala.quoted.Expr[U] = {
44+
def cast[U](using tp: scala.quoted.Type[U])(using qctx: QuoteContext): scala.quoted.Expr[U] = asExprOf[U]
45+
46+
/** Convert this to an `quoted.Expr[X]` if this expression is a valid expression of type `X` or throws */
47+
def asExprOf[X](using tp: scala.quoted.Type[X])(using qctx: QuoteContext): scala.quoted.Expr[X] = {
4548
val tree = this.unseal
4649
val expectedType = tp.unseal.tpe
4750
if (tree.tpe <:< expectedType)
48-
this.asInstanceOf[scala.quoted.Expr[U]]
51+
this.asInstanceOf[scala.quoted.Expr[X]]
4952
else
5053
throw new scala.tasty.reflect.ExprCastError(
5154
s"""Expr: ${tree.show}
@@ -178,7 +181,7 @@ object Expr {
178181
/** Given a tuple of the form `(Expr[A1], ..., Expr[An])`, outputs a tuple `Expr[(A1, ..., An)]`. */
179182
def ofTuple[T <: Tuple: Tuple.IsMappedBy[Expr]: Type](tup: T)(using qctx: QuoteContext): Expr[Tuple.InverseMap[T, Expr]] = {
180183
val elems: Seq[Expr[Any]] = tup.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Expr[Any]]]
181-
ofTupleFromSeq(elems).cast[Tuple.InverseMap[T, Expr]]
184+
ofTupleFromSeq(elems).asExprOf[Tuple.InverseMap[T, Expr]]
182185
}
183186

184187
/** Find an implicit of type `T` in the current scope given by `qctx`.

library/src-bootstrapped/scala/quoted/util/ExprMap.scala

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -102,22 +102,18 @@ trait ExprMap {
102102
}
103103

104104
def transformTerm(tree: Term, tpe: Type)(using ctx: Context): Term =
105-
tree match {
106-
case _: Closure =>
107-
tree
108-
case _: Inlined =>
109-
transformTermChildren(tree, tpe)
110-
case _ =>
111-
tree.tpe.widen match {
112-
case _: MethodType | _: PolyType =>
113-
transformTermChildren(tree, tpe)
114-
case _ =>
115-
type X
116-
val expr = tree.seal.asInstanceOf[Expr[X]]
117-
val t = tpe.seal.asInstanceOf[quoted.Type[X]]
118-
transform(expr)(using qctx, t).unseal
119-
}
120-
}
105+
tree match
106+
case _: Closure =>
107+
tree
108+
case _: Inlined =>
109+
transformTermChildren(tree, tpe)
110+
case _ if tree.isExpr =>
111+
type X
112+
val expr = tree.seal.asInstanceOf[Expr[X]]
113+
val t = tpe.seal.asInstanceOf[quoted.Type[X]]
114+
transform(expr)(using qctx, t).unseal
115+
case _ =>
116+
transformTermChildren(tree, tpe)
121117

122118
def transformTypeTree(tree: TypeTree)(using ctx: Context): TypeTree = tree
123119

@@ -155,7 +151,7 @@ trait ExprMap {
155151
trees mapConserve (transformTypeCaseDef(_))
156152

157153
}
158-
new MapChildren().transformTermChildren(e.unseal, tpe.unseal.tpe).seal.cast[T] // Cast will only fail if this implementation has a bug
154+
new MapChildren().transformTermChildren(e.unseal, tpe.unseal.tpe).asExprOf[T]
159155
}
160156

161157
}

library/src-non-bootstrapped/scala/quoted/Expr.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ package scala.quoted
22

33
abstract class Expr[+T] private[scala]:
44
def unseal(using qctx: QuoteContext): qctx.tasty.Term
5+
def asExprOf[X](using tp: scala.quoted.Type[X])(using qctx: QuoteContext): scala.quoted.Expr[X] = ???

library/src/scala/tasty/Reflection.scala

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,28 @@ class Reflection(private[scala] val internal: CompilerInterface) { self =>
488488
/** Shows the tree as fully typed source code */
489489
def showWith(syntaxHighlight: SyntaxHighlight)(using ctx: Context): String =
490490
new SourceCodePrinter[self.type](self)(syntaxHighlight).showTree(tree)
491+
492+
/** Does this tree represent a valid expression? */
493+
def isExpr(using ctx: Context): Boolean =
494+
tree match
495+
case tree: Term =>
496+
tree.tpe.widen match
497+
case _: MethodType | _: PolyType => false
498+
case _ => true
499+
case _ => false
500+
491501
end extension
502+
503+
504+
/** Convert this tree to an `quoted.Expr[T]` if the tree is a valid expression or throws */
505+
extension [T](tree: Tree)
506+
def asExprOf(using scala.quoted.Type[T])(using QuoteContext): scala.quoted.Expr[T] =
507+
if tree.isExpr then
508+
new scala.internal.quoted.Expr(tree, internal.compilerId).asExprOf[T]
509+
else tree match
510+
case tree: Term => throw new Exception("Expected an expression. This is a partially applied Term. Try eta-expanding the term first.")
511+
case _ => throw new Exception("Expected a Term but was: " + tree)
512+
492513
end Tree
493514

494515
given (using ctx: Context) as TypeTest[Tree, PackageClause] = internal.PackageClause_TypeTest
@@ -655,15 +676,13 @@ class Reflection(private[scala] val internal: CompilerInterface) { self =>
655676

656677
/** Convert `Term` to an `quoted.Expr[Any]` if the term is a valid expression or throws */
657678
def seal(using ctx: Context): scala.quoted.Expr[Any] =
658-
sealOpt.getOrElse {
659-
throw new Exception("Cannot seal a partially applied Term. Try eta-expanding the term first.")
660-
}
679+
if self.isExpr then new scala.internal.quoted.Expr(self, internal.compilerId)
680+
else throw new Exception("Cannot seal a partially applied Term. Try eta-expanding the term first.")
661681

662682
/** Convert `Term` to an `quoted.Expr[Any]` if the term is a valid expression */
663683
def sealOpt(using ctx: Context): Option[scala.quoted.Expr[Any]] =
664-
self.tpe.widen match
665-
case _: MethodType | _: PolyType => None
666-
case _ => Some(new scala.internal.quoted.Expr(self, internal.compilerId))
684+
if self.isExpr then Some(new scala.internal.quoted.Expr(self, internal.compilerId))
685+
else None
667686

668687
/** Type of this term */
669688
def tpe(using ctx: Context): Type = internal.Term_tpe(self)

0 commit comments

Comments
 (0)