Skip to content

Commit 2722c3d

Browse files
committed
Add isExpr, asExpr, asExprOf and asTerm
These replace the methods `seal`, `sealOpt` and `unseal` with equivalent method with more intuitive names. Insead of `sealOpt` we provide a more flexible `isExpr`. All these methods are defined directly on `Tree` insead of `Term` to avoid the need to perform type test to a `Term` first.
1 parent 8eea8d8 commit 2722c3d

File tree

86 files changed

+303
-272
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+303
-272
lines changed

library/src-bootstrapped/dotty/internal/StringContextMacro.scala

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ object StringContextMacro {
5959
*/
6060
private def interpolate(strCtxExpr: Expr[StringContext], argsExpr: Expr[Seq[Any]])(using qctx: QuoteContext): Expr[String] = {
6161
import qctx.tasty._
62-
val sourceFile = strCtxExpr.unseal.pos.sourceFile
62+
val sourceFile = strCtxExpr.asTerm.pos.sourceFile
6363

6464
val (partsExpr, parts) = strCtxExpr match {
6565
case Expr.StringContext(p1 @ Consts(p2)) => (p1.toList, p2.toList)
@@ -76,28 +76,28 @@ object StringContextMacro {
7676
private[this] var oldReported = false
7777
def partError(message : String, index : Int, offset : Int) : Unit = {
7878
reported = true
79-
val positionStart = partsExpr(index).unseal.pos.start + offset
79+
val positionStart = partsExpr(index).asTerm.pos.start + offset
8080
error(message, sourceFile, positionStart, positionStart)
8181
}
8282
def partWarning(message : String, index : Int, offset : Int) : Unit = {
8383
reported = true
84-
val positionStart = partsExpr(index).unseal.pos.start + offset
84+
val positionStart = partsExpr(index).asTerm.pos.start + offset
8585
warning(message, sourceFile, positionStart, positionStart)
8686
}
8787

8888
def argError(message : String, index : Int) : Unit = {
8989
reported = true
90-
error(message, args(index).unseal.pos)
90+
error(message, args(index).asTerm.pos)
9191
}
9292

9393
def strCtxError(message : String) : Unit = {
9494
reported = true
95-
val positionStart = strCtxExpr.unseal.pos.start
95+
val positionStart = strCtxExpr.asTerm.pos.start
9696
error(message, sourceFile, positionStart, positionStart)
9797
}
9898
def argsError(message : String) : Unit = {
9999
reported = true
100-
error(message, argsExpr.unseal.pos)
100+
error(message, argsExpr.asTerm.pos)
101101
}
102102

103103
def hasReported() : Boolean = {
@@ -658,7 +658,7 @@ object StringContextMacro {
658658
case Some(argIndex, arg) => {
659659
val (hasArgumentIndex, argumentIndex, flags, hasWidth, width, hasPrecision, precision, hasRelative, relativeIndex, conversion) = getFormatSpecifiers(part, argIndex, argIndex + 1, false, formattingStart)
660660
if (!reporter.hasReported()){
661-
val conversionWithType = checkFormatSpecifiers(argIndex + 1, hasArgumentIndex, argumentIndex, Some(argIndex + 1), start == 0, maxArgumentIndex, hasRelative, hasWidth, width, hasPrecision, precision, flags, conversion, Some(arg.unseal.tpe), part)
661+
val conversionWithType = checkFormatSpecifiers(argIndex + 1, hasArgumentIndex, argumentIndex, Some(argIndex + 1), start == 0, maxArgumentIndex, hasRelative, hasWidth, width, hasPrecision, precision, flags, conversion, Some(arg.asTerm.tpe), part)
662662
nextStart = conversion + 1
663663
conversionWithType :: checkPart(part, nextStart, argument, maxArgumentIndex)
664664
} else checkPart(part, conversion + 1, argument, maxArgumentIndex)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ object Const {
2323
case Inlined(_, Nil, e) => rec(e)
2424
case _ => None
2525
}
26-
rec(expr.unseal)
26+
rec(expr.asTerm)
2727
}
2828

2929
}

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

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ abstract class Expr[+T] private[scala] {
77

88
/** Show a source code like representation of this expression without syntax highlight */
99
def show(using qctx: QuoteContext): String =
10-
this.unseal.showWith(SyntaxHighlight.plain)
10+
this.asTerm.showWith(SyntaxHighlight.plain)
1111

1212
/** Show a source code like representation of this expression */
1313
def showWith(syntaxHighlight: SyntaxHighlight)(using qctx: QuoteContext): String =
14-
this.unseal.showWith(syntaxHighlight)
14+
this.asTerm.showWith(syntaxHighlight)
1515

1616
/** Return the unlifted value of this expression.
1717
*
@@ -41,11 +41,15 @@ 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] = {
45-
val tree = this.unseal
44+
@deprecated("Replaced with `asExprOf`", "0.27.0")
45+
def cast[U](using tp: scala.quoted.Type[U])(using qctx: QuoteContext): scala.quoted.Expr[U] = asExprOf[U]
46+
47+
/** Convert to an `quoted.Expr[X]` if this expression is a valid expression of type `X` or throws */
48+
def asExprOf[X](using tp: scala.quoted.Type[X])(using qctx: QuoteContext): scala.quoted.Expr[X] = {
49+
val tree = this.asTerm
4650
val expectedType = tp.unseal.tpe
4751
if (tree.tpe <:< expectedType)
48-
this.asInstanceOf[scala.quoted.Expr[U]]
52+
this.asInstanceOf[scala.quoted.Expr[X]]
4953
else
5054
throw new scala.tasty.reflect.ExprCastError(
5155
s"""Expr: ${tree.show}
@@ -55,7 +59,11 @@ abstract class Expr[+T] private[scala] {
5559
}
5660

5761
/** View this expression `quoted.Expr[T]` as a `Term` */
58-
def unseal(using qctx: QuoteContext): qctx.tasty.Term
62+
@deprecated("Replaced with `asTerm`", "0.27.0")
63+
def unseal(using qctx: QuoteContext): qctx.tasty.Term = asTerm
64+
65+
/** View this expression `quoted.Expr[T]` as a `Term` */
66+
def asTerm(using qctx: QuoteContext): qctx.tasty.Term
5967

6068
}
6169

@@ -67,20 +75,20 @@ object Expr {
6775
* Otherwise returns `expr`.
6876
*/
6977
def betaReduce[T](expr: Expr[T])(using qctx: QuoteContext): Expr[T] =
70-
qctx.tasty.internal.betaReduce(expr.unseal) match
71-
case Some(expr1) => expr1.seal.asInstanceOf[Expr[T]]
78+
qctx.tasty.internal.betaReduce(expr.asTerm) match
79+
case Some(expr1) => expr1.asExpr.asInstanceOf[Expr[T]]
7280
case _ => expr
7381

7482
/** Returns a null expresssion equivalent to `'{null}` */
7583
def nullExpr: QuoteContext ?=> Expr[Null] = qctx ?=> {
7684
import qctx.tasty._
77-
Literal(Constant(null)).seal.asInstanceOf[Expr[Null]]
85+
Literal(Constant(null)).asExpr.asInstanceOf[Expr[Null]]
7886
}
7987

8088
/** Returns a unit expresssion equivalent to `'{}` or `'{()}` */
8189
def unitExpr: QuoteContext ?=> Expr[Unit] = qctx ?=> {
8290
import qctx.tasty._
83-
Literal(Constant(())).seal.asInstanceOf[Expr[Unit]]
91+
Literal(Constant(())).asExpr.asInstanceOf[Expr[Unit]]
8492
}
8593

8694
/** Returns an expression containing a block with the given statements and ending with the expresion
@@ -89,7 +97,7 @@ object Expr {
8997
*/
9098
def block[T](statements: List[Expr[Any]], expr: Expr[T])(using qctx: QuoteContext): Expr[T] = {
9199
import qctx.tasty._
92-
Block(statements.map(_.unseal), expr.unseal).seal.asInstanceOf[Expr[T]]
100+
Block(statements.map(_.asTerm), expr.asTerm).asExpr.asInstanceOf[Expr[T]]
93101
}
94102

95103
/** Lift a value into an expression containing the construction of that value */
@@ -178,7 +186,7 @@ object Expr {
178186
/** Given a tuple of the form `(Expr[A1], ..., Expr[An])`, outputs a tuple `Expr[(A1, ..., An)]`. */
179187
def ofTuple[T <: Tuple: Tuple.IsMappedBy[Expr]: Type](tup: T)(using qctx: QuoteContext): Expr[Tuple.InverseMap[T, Expr]] = {
180188
val elems: Seq[Expr[Any]] = tup.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Expr[Any]]]
181-
ofTupleFromSeq(elems).cast[Tuple.InverseMap[T, Expr]]
189+
ofTupleFromSeq(elems).asExprOf[Tuple.InverseMap[T, Expr]]
182190
}
183191

184192
/** Find an implicit of type `T` in the current scope given by `qctx`.
@@ -192,7 +200,7 @@ object Expr {
192200
def summon[T](using tpe: Type[T])(using qctx: QuoteContext): Option[Expr[T]] = {
193201
import qctx.tasty._
194202
searchImplicit(tpe.unseal.tpe) match {
195-
case iss: ImplicitSearchSuccess => Some(iss.tree.seal.asInstanceOf[Expr[T]])
203+
case iss: ImplicitSearchSuccess => Some(iss.tree.asExpr.asInstanceOf[Expr[T]])
196204
case isf: ImplicitSearchFailure => None
197205
}
198206
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ object Lambda {
1919
import qctx.tasty._
2020
val argTypes = functionType.unseal.tpe match
2121
case AppliedType(_, functionArguments) => functionArguments.init.asInstanceOf[List[Type]]
22-
qctx.tasty.internal.lambdaExtractor(expr.unseal, argTypes).map { fn =>
22+
qctx.tasty.internal.lambdaExtractor(expr.asTerm, argTypes).map { fn =>
2323
def f(args: Tuple.Map[Args, Expr]): Expr[Res] =
24-
fn(args.toArray.toList.map(_.asInstanceOf[Expr[Any]].unseal)).seal.asInstanceOf[Expr[Res]]
24+
fn(args.toArray.toList.map(_.asInstanceOf[Expr[Any]].asTerm)).asExpr.asInstanceOf[Expr[Res]]
2525
tg.untupled(f)
2626
}
2727

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ object Liftable {
3333
/** Lift a primitive value `n` into `'{ n }` */
3434
def toExpr(x: T) = qctx ?=> {
3535
import qctx.tasty._
36-
Literal(Constant(x)).seal.asInstanceOf[Expr[T]]
36+
Literal(Constant(x)).asExpr.asInstanceOf[Expr[T]]
3737
}
3838
}
3939

4040
given ClassIsLiftable[T] as Liftable[Class[T]] = new Liftable[Class[T]] {
4141
/** Lift a `Class[T]` into `'{ classOf[T] }` */
4242
def toExpr(x: Class[T]) = qctx ?=> {
4343
import qctx.tasty._
44-
Ref(defn.Predef_classOf).appliedToType(Type(x)).seal.asInstanceOf[Expr[Class[T]]]
44+
Ref(defn.Predef_classOf).appliedToType(Type(x)).asExpr.asInstanceOf[Expr[Class[T]]]
4545
}
4646
}
4747

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ object Varargs {
1717
*/
1818
def apply[T](xs: Seq[Expr[T]])(using tp: Type[T], qctx: QuoteContext): Expr[Seq[T]] = {
1919
import qctx.tasty._
20-
Repeated(xs.map[Term](_.unseal).toList, tp.unseal).seal.asInstanceOf[Expr[Seq[T]]]
20+
Repeated(xs.map[Term](_.asTerm).toList, tp.unseal).asExpr.asInstanceOf[Expr[Seq[T]]]
2121
}
2222

2323
/** Matches a literal sequence of expressions and return a sequence of expressions.
@@ -35,12 +35,12 @@ object Varargs {
3535
def unapply[T](expr: Expr[Seq[T]])(using qctx: QuoteContext): Option[Seq[Expr[T]]] = {
3636
import qctx.tasty._
3737
def rec(tree: Term): Option[Seq[Expr[T]]] = tree match {
38-
case Typed(Repeated(elems, _), _) => Some(elems.map(x => x.seal.asInstanceOf[Expr[T]]))
38+
case Typed(Repeated(elems, _), _) => Some(elems.map(x => x.asExpr.asInstanceOf[Expr[T]]))
3939
case Block(Nil, e) => rec(e)
4040
case Inlined(_, Nil, e) => rec(e)
4141
case _ => None
4242
}
43-
rec(expr.unseal)
43+
rec(expr.asTerm)
4444
}
4545

4646
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ object report:
88

99
/** Report an error at the on the position of `expr` */
1010
def error(msg: => String, expr: Expr[Any])(using qctx: QuoteContext): Unit =
11-
qctx.tasty.error(msg, expr.unseal.pos)
11+
qctx.tasty.error(msg, expr.asTerm.pos)
1212

1313
/** Report an error at the position of the macro expansion and throws a StopQuotedContext */
1414
def throwError(msg: => String)(using qctx: QuoteContext): Nothing = {
@@ -27,7 +27,7 @@ object report:
2727

2828
/** Report a warning at the on the position of `expr` */
2929
def warning(msg: => String, expr: Expr[_])(using qctx: QuoteContext): Unit =
30-
qctx.tasty.warning(msg, expr.unseal.pos)
30+
qctx.tasty.warning(msg, expr.asTerm.pos)
3131

3232
/** Throwable used to stop the expansion of a macro after an error was reported */
3333
class StopQuotedContext extends Throwable

library/src-bootstrapped/scala/quoted/unsafe/UnsafeExpr.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ object UnsafeExpr {
2020
* change the parameter semantics as by-value parameter could be re-evaluated.
2121
*/
2222
def underlyingArgument[T](expr: Expr[T])(using qctx: QuoteContext): Expr[T] =
23-
expr.unseal.underlyingArgument.seal.asInstanceOf[Expr[T]]
23+
expr.asTerm.underlyingArgument.asExpr.asInstanceOf[Expr[T]]
2424

2525
// TODO generalize for any function arity
2626
/** Allows inspection or transformation of the body of the expression of function.
@@ -38,23 +38,23 @@ object UnsafeExpr {
3838
*/
3939
def open[T1, R, X](f: Expr[T1 => R])(content: (Expr[R], [t] => Expr[t] => Expr[T1] => Expr[t]) => X)(using qctx: QuoteContext): X = {
4040
val (params, bodyExpr) = paramsAndBody[R](f)
41-
content(bodyExpr, [t] => (e: Expr[t]) => (v: Expr[T1]) => bodyFn[t](e.unseal, params, List(v.unseal)).seal.asInstanceOf[Expr[t]])
41+
content(bodyExpr, [t] => (e: Expr[t]) => (v: Expr[T1]) => bodyFn[t](e.asTerm, params, List(v.asTerm)).asExpr.asInstanceOf[Expr[t]])
4242
}
4343

4444
def open[T1, T2, R, X](f: Expr[(T1, T2) => R])(content: (Expr[R], [t] => Expr[t] => (Expr[T1], Expr[T2]) => Expr[t]) => X)(using qctx: QuoteContext)(using DummyImplicit): X = {
4545
val (params, bodyExpr) = paramsAndBody[R](f)
46-
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]])
46+
content(bodyExpr, [t] => (e: Expr[t]) => (v1: Expr[T1], v2: Expr[T2]) => bodyFn[t](e.asTerm, params, List(v1.asTerm, v2.asTerm)).asExpr.asInstanceOf[Expr[t]])
4747
}
4848

4949
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)(using qctx: QuoteContext)(using DummyImplicit, DummyImplicit): X = {
5050
val (params, bodyExpr) = paramsAndBody[R](f)
51-
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]])
51+
content(bodyExpr, [t] => (e: Expr[t]) => (v1: Expr[T1], v2: Expr[T2], v3: Expr[T3]) => bodyFn[t](e.asTerm, params, List(v1.asTerm, v2.asTerm, v3.asTerm)).asExpr.asInstanceOf[Expr[t]])
5252
}
5353

5454
private def paramsAndBody[R](using qctx: QuoteContext)(f: Expr[Any]): (List[qctx.tasty.ValDef], Expr[R]) = {
5555
import qctx.tasty._
56-
val Block(List(DefDef("$anonfun", Nil, List(params), _, Some(body))), Closure(Ident("$anonfun"), None)) = f.unseal.etaExpand
57-
(params, body.seal.asInstanceOf[Expr[R]])
56+
val Block(List(DefDef("$anonfun", Nil, List(params), _, Some(body))), Closure(Ident("$anonfun"), None)) = f.asTerm.etaExpand
57+
(params, body.asExpr.asInstanceOf[Expr[R]])
5858
}
5959

6060
private def bodyFn[t](using qctx: QuoteContext)(e: qctx.tasty.Term, params: List[qctx.tasty.ValDef], args: List[qctx.tasty.Term]): qctx.tasty.Term = {

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

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -107,16 +107,13 @@ trait ExprMap {
107107
tree
108108
case _: Inlined =>
109109
transformTermChildren(tree, tpe)
110+
case _ if tree.isExpr =>
111+
type X
112+
val expr = tree.asExpr.asInstanceOf[Expr[X]]
113+
val t = tpe.seal.asInstanceOf[quoted.Type[X]]
114+
transform(expr)(using qctx, t).asTerm
110115
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-
}
116+
transformTermChildren(tree, tpe)
120117
}
121118

122119
def transformTypeTree(tree: TypeTree)(using ctx: Context): TypeTree = tree
@@ -155,7 +152,7 @@ trait ExprMap {
155152
trees mapConserve (transformTypeCaseDef(_))
156153

157154
}
158-
new MapChildren().transformTermChildren(e.unseal, tpe.unseal.tpe).seal.cast[T] // Cast will only fail if this implementation has a bug
155+
new MapChildren().transformTermChildren(e.asTerm, tpe.unseal.tpe).asExprOf[T]
159156
}
160157

161158
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
package scala.quoted
22

33
abstract class Expr[+T] private[scala]:
4-
def unseal(using qctx: QuoteContext): qctx.tasty.Term
4+
def unseal(using qctx: QuoteContext): qctx.tasty.Term = asTerm
5+
def asTerm(using qctx: QuoteContext): qctx.tasty.Term
6+
def asExprOf[U](using tp: scala.quoted.Type[U])(using qctx: QuoteContext): scala.quoted.Expr[U] =
7+
throw new Exception("Non bootstrapped lib")

library/src/scala/internal/quoted/Expr.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import scala.quoted._
1818
case _ => false
1919
}
2020

21-
def unseal(using qctx: QuoteContext): qctx.tasty.Term =
21+
def asTerm(using qctx: QuoteContext): qctx.tasty.Term =
2222
if (qctx.tasty.internal.compilerId != scopeId)
2323
throw new scala.quoted.ScopeException("Cannot call `scala.quoted.staging.run(...)` within a macro or another `run(...)`")
2424
tree.asInstanceOf[qctx.tasty.Term]
@@ -52,7 +52,7 @@ object Expr {
5252
*/
5353
def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutineeExpr: scala.quoted.Expr[Any])(using patternExpr: scala.quoted.Expr[Any],
5454
hasTypeSplices: Boolean, qctx: QuoteContext): Option[Tup] = {
55-
new Matcher.QuoteMatcher[qctx.type].termMatch(scrutineeExpr.unseal, patternExpr.unseal, hasTypeSplices).asInstanceOf[Option[Tup]]
55+
new Matcher.QuoteMatcher[qctx.type].termMatch(scrutineeExpr.asTerm, patternExpr.asTerm, hasTypeSplices).asInstanceOf[Option[Tup]]
5656
}
5757

5858
}

library/src/scala/internal/quoted/Matcher.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,14 +253,14 @@ object Matcher {
253253
if patternHole.symbol == internal.Definitions_InternalQuotedMatcher_patternHole &&
254254
s.tpe <:< tpt.tpe &&
255255
tpt2.tpe.derivesFrom(defn.RepeatedParamClass) =>
256-
matched(scrutinee.seal)
256+
matched(scrutinee.asExpr)
257257

258258
/* Term hole */
259259
// Match a scala.internal.Quoted.patternHole and return the scrutinee tree
260260
case (ClosedPatternTerm(scrutinee), TypeApply(patternHole, tpt :: Nil))
261261
if patternHole.symbol == internal.Definitions_InternalQuotedMatcher_patternHole &&
262262
scrutinee.tpe <:< tpt.tpe =>
263-
matched(scrutinee.seal)
263+
matched(scrutinee.asExpr)
264264

265265
/* Higher order term hole */
266266
// Matches an open term and wraps it into a lambda that provides the free variables
@@ -283,7 +283,7 @@ object Matcher {
283283
val argTypes = args.map(x => x.tpe.widenTermRefExpr)
284284
val resType = pattern.tpe
285285
val res = Lambda(MethodType(names)(_ => argTypes, _ => resType), bodyFn)
286-
matched(res.seal)
286+
matched(res.asExpr)
287287

288288
//
289289
// Match two equivalent trees

library/src/scala/tasty/Reflection.scala

Lines changed: 23 additions & 0 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+
501+
/** Convert to an `quoted.Expr[Any]` if the tree is a valid expression or throws */
502+
def asExpr(using QuoteContext): scala.quoted.Expr[Any] =
503+
assert(tree.isExpr, tree)
504+
new scala.internal.quoted.Expr(tree, internal.compilerId)
505+
491506
end extension
507+
508+
/** Convert to an `quoted.Expr[T]` if the tree is a valid expression or throws */
509+
extension [T](tree: Tree)
510+
def asExprOf(using scala.quoted.Type[T])(using QuoteContext): scala.quoted.Expr[T] =
511+
tree.asExpr.asExprOf[T]
512+
492513
end Tree
493514

494515
given (using ctx: Context) as TypeTest[Tree, PackageClause] = internal.PackageClause_TypeTest
@@ -654,12 +675,14 @@ class Reflection(private[scala] val internal: CompilerInterface) { self =>
654675
extension (self: Term):
655676

656677
/** Convert `Term` to an `quoted.Expr[Any]` if the term is a valid expression or throws */
678+
@deprecated("Replaced with `asExpr` (or `asExprOf`)", "0.27.0")
657679
def seal(using ctx: Context): scala.quoted.Expr[Any] =
658680
sealOpt.getOrElse {
659681
throw new Exception("Cannot seal a partially applied Term. Try eta-expanding the term first.")
660682
}
661683

662684
/** Convert `Term` to an `quoted.Expr[Any]` if the term is a valid expression */
685+
@deprecated("Replaced with `isExpr` and `asExpr` (or `asExprOf`)", "0.27.0")
663686
def sealOpt(using ctx: Context): Option[scala.quoted.Expr[Any]] =
664687
self.tpe.widen match
665688
case _: MethodType | _: PolyType => None

0 commit comments

Comments
 (0)