Skip to content

Commit 6485049

Browse files
Merge pull request #10269 from dotty-staging/define-expr-methods-in-quote-context
Define Expr methods in QuoteContext
2 parents dc8708a + e42e9f8 commit 6485049

File tree

9 files changed

+110
-91
lines changed

9 files changed

+110
-91
lines changed

compiler/src/dotty/tools/dotc/quoted/ExprImpl.scala

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ final class ExprImpl(val tree: tpd.Tree, val scopeId: Int) extends scala.quoted.
2020
case _ => false
2121
}
2222

23-
def unseal(using qctx: QuoteContext): qctx.reflect.Term =
24-
checkScopeId(qctx.hashCode)
25-
tree.asInstanceOf[qctx.reflect.Term]
26-
2723
def checkScopeId(expectedScopeId: Int): Unit =
2824
if expectedScopeId != scopeId then
2925
throw new ScopeException("Cannot call `scala.quoted.staging.run(...)` within a macro or another `run(...)`")

compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,42 @@ object QuoteContextImpl {
4343

4444
class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickler, QuoteMatching:
4545

46+
extension [T](self: scala.quoted.Expr[T]):
47+
def show: String =
48+
reflect.TreeMethodsImpl.show(self.asReflectTree)
49+
50+
def showAnsiColored: String =
51+
reflect.TreeMethodsImpl.showAnsiColored(self.asReflectTree)
52+
53+
def matches(that: scala.quoted.Expr[Any]): Boolean =
54+
treeMatch(self.asReflectTree, that.asReflectTree).nonEmpty
55+
56+
def asReflectTree: reflect.Term =
57+
val expr = self.asInstanceOf[ExprImpl]
58+
expr.checkScopeId(QuoteContextImpl.this.hashCode)
59+
expr.tree
60+
61+
end extension
62+
63+
extension [X](self: scala.quoted.Expr[Any]):
64+
/** Checks is the `quoted.Expr[?]` is valid expression of type `X` */
65+
def isExprOf(using scala.quoted.Type[X]): Boolean =
66+
reflect.TypeReprMethodsImpl.<:<(self.asReflectTree.tpe)(reflect.TypeRepr.of[X])
67+
68+
/** Convert this to an `quoted.Expr[X]` if this expression is a valid expression of type `X` or throws */
69+
def asExprOf(using scala.quoted.Type[X]): scala.quoted.Expr[X] = {
70+
if isExprOf[X] then
71+
self.asInstanceOf[scala.quoted.Expr[X]]
72+
else
73+
throw Exception(
74+
s"""Expr cast exception: ${self.show}
75+
|of type: ${reflect.TypeReprMethodsImpl.show(self.asReflectTree.tpe)}
76+
|did not conform to type: ${reflect.TypeReprMethodsImpl.show(reflect.TypeRepr.of[X])}
77+
|""".stripMargin
78+
)
79+
}
80+
end extension
81+
4682
object reflect extends scala.tasty.Reflection:
4783

4884
def rootContext: Context = ctx
@@ -72,15 +108,15 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl
72108
case _ => false
73109
def asExpr: scala.quoted.Expr[Any] =
74110
if self.isExpr then
75-
new dotty.tools.dotc.quoted.ExprImpl(self, QuoteContextImpl.this.hashCode)
111+
new ExprImpl(self, QuoteContextImpl.this.hashCode)
76112
else self match
77113
case TermTypeTest(self) => throw new Exception("Expected an expression. This is a partially applied Term. Try eta-expanding the term first.")
78114
case _ => throw new Exception("Expected a Term but was: " + self)
79115
end extension
80116

81117
extension [T](self: Tree)
82118
def asExprOf(using tp: scala.quoted.Type[T]): scala.quoted.Expr[T] =
83-
self.asExpr.asExprOf[T](using tp)(using QuoteContextImpl.this)
119+
QuoteContextImpl.this.asExprOf[T](self.asExpr)(using tp)
84120
end extension
85121

86122
end TreeMethodsImpl
@@ -316,11 +352,11 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl
316352
object TermMethodsImpl extends TermMethods:
317353
extension (self: Term):
318354
def seal: scala.quoted.Expr[Any] =
319-
if self.isExpr then new dotty.tools.dotc.quoted.ExprImpl(self, QuoteContextImpl.this.hashCode)
355+
if self.isExpr then new ExprImpl(self, QuoteContextImpl.this.hashCode)
320356
else throw new Exception("Cannot seal a partially applied Term. Try eta-expanding the term first.")
321357

322358
def sealOpt: Option[scala.quoted.Expr[Any]] =
323-
if self.isExpr then Some(new dotty.tools.dotc.quoted.ExprImpl(self, QuoteContextImpl.this.hashCode))
359+
if self.isExpr then Some(new ExprImpl(self, QuoteContextImpl.this.hashCode))
324360
else None
325361

326362
def tpe: TypeRepr = self.tpe
@@ -1003,7 +1039,7 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl
10031039

10041040
object TypeTree extends TypeTreeModule:
10051041
def of[T <: AnyKind](using tp: scala.quoted.Type[T]): TypeTree =
1006-
tp.asInstanceOf[dotty.tools.dotc.quoted.TypeImpl].typeTree
1042+
tp.asInstanceOf[TypeImpl].typeTree
10071043
end TypeTree
10081044

10091045
object TypeTreeMethodsImpl extends TypeTreeMethods:
@@ -1572,7 +1608,7 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl
15721608

15731609
object TypeRepr extends TypeReprModule:
15741610
def of[T <: AnyKind](using tp: scala.quoted.Type[T]): TypeRepr =
1575-
tp.asInstanceOf[dotty.tools.dotc.quoted.TypeImpl].typeTree.tpe
1611+
tp.asInstanceOf[TypeImpl].typeTree.tpe
15761612
def typeConstructorOf(clazz: Class[?]): TypeRepr =
15771613
if (clazz.isPrimitive)
15781614
if (clazz == classOf[Boolean]) dotc.core.Symbols.defn.BooleanType
@@ -1609,7 +1645,7 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl
16091645
def seal: scala.quoted.Type[_] = self.asType
16101646

16111647
def asType: scala.quoted.Type[?] =
1612-
new dotty.tools.dotc.quoted.TypeImpl(Inferred(self), QuoteContextImpl.this.hashCode)
1648+
new TypeImpl(Inferred(self), QuoteContextImpl.this.hashCode)
16131649

16141650
def =:=(that: TypeRepr): Boolean = self =:= that
16151651
def <:<(that: TypeRepr): Boolean = self <:< that
@@ -2624,16 +2660,16 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl
26242660

26252661
def unpickleExpr[T](pickled: String | List[String], typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.QuoteContext) => scala.quoted.Expr[?]): scala.quoted.Expr[T] =
26262662
val tree = PickledQuotes.unpickleTerm(pickled, typeHole, termHole)(using reflect.rootContext)
2627-
new dotty.tools.dotc.quoted.ExprImpl(tree, hash).asInstanceOf[scala.quoted.Expr[T]]
2663+
new ExprImpl(tree, hash).asInstanceOf[scala.quoted.Expr[T]]
26282664

26292665
def unpickleType[T <: AnyKind](pickled: String | List[String], typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.QuoteContext) => scala.quoted.Expr[?]): scala.quoted.Type[T] =
26302666
val tree = PickledQuotes.unpickleTypeTree(pickled, typeHole, termHole)(using reflect.rootContext)
2631-
new dotty.tools.dotc.quoted.TypeImpl(tree, hash).asInstanceOf[scala.quoted.Type[T]]
2667+
new TypeImpl(tree, hash).asInstanceOf[scala.quoted.Type[T]]
26322668

26332669
object ExprMatch extends ExprMatchModule:
26342670
def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutinee: scala.quoted.Expr[Any])(using pattern: scala.quoted.Expr[Any]): Option[Tup] =
2635-
val scrutineeTree = scrutinee.unseal(using QuoteContextImpl.this)
2636-
val patternTree = pattern.unseal(using QuoteContextImpl.this)
2671+
val scrutineeTree = QuoteContextImpl.this.asReflectTree(scrutinee)
2672+
val patternTree = QuoteContextImpl.this.asReflectTree(pattern)
26372673
treeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]]
26382674
end ExprMatch
26392675

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

Lines changed: 1 addition & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,10 @@
11
package scala.quoted
22

33
/** Quoted expression of type `T` */
4-
abstract class Expr[+T] private[scala] {
5-
6-
/** Show a source code like representation of this expression without syntax highlight */
7-
def show(using qctx: QuoteContext): String = this.unseal.show
8-
9-
/** Shows the tree as fully typed source code colored with ANSI */
10-
def showAnsiColored(using qctx: QuoteContext): String = this.unseal.showAnsiColored
11-
12-
/** Pattern matches `this` against `that`. Effectively performing a deep equality check.
13-
* It does the equivalent of
14-
* ```
15-
* this match
16-
* case '{...} => true // where the contents of the pattern are the contents of `that`
17-
* case _ => false
18-
* ```
19-
*/
20-
final def matches(that: Expr[Any])(using qctx: QuoteContext): Boolean =
21-
val ExprMatch = qctx.asInstanceOf[scala.quoted.internal.QuoteMatching].ExprMatch
22-
ExprMatch.unapply[EmptyTuple, EmptyTuple](this)(using that).nonEmpty
23-
24-
/** Checks is the `quoted.Expr[?]` is valid expression of type `X` */
25-
def isExprOf[X](using tp: scala.quoted.Type[X])(using qctx: QuoteContext): Boolean =
26-
this.unseal.tpe <:< qctx.reflect.TypeRepr.of[X]
27-
28-
/** Convert this to an `quoted.Expr[X]` if this expression is a valid expression of type `X` or throws */
29-
def asExprOf[X](using tp: scala.quoted.Type[X])(using qctx: QuoteContext): scala.quoted.Expr[X] = {
30-
if isExprOf[X] then
31-
this.asInstanceOf[scala.quoted.Expr[X]]
32-
else
33-
throw Exception(
34-
s"""Expr cast exception: ${this.show}
35-
|of type: ${this.unseal.tpe.show}
36-
|did not conform to type: ${qctx.reflect.TypeRepr.of[X].show}
37-
|""".stripMargin
38-
)
39-
}
40-
41-
/** View this expression `quoted.Expr[T]` as a `Term` */
42-
def unseal(using qctx: QuoteContext): qctx.reflect.Term
43-
44-
}
4+
abstract class Expr[+T] private[scala]
455

466
object Expr {
477

48-
extension [T](expr: Expr[T]):
49-
/** Return the unlifted value of this expression.
50-
*
51-
* Returns `None` if the expression does not contain a value or contains side effects.
52-
* Otherwise returns the `Some` of the value.
53-
*/
54-
def unlift(using qctx: QuoteContext, unlift: Unliftable[T]): Option[T] =
55-
unlift.fromExpr(expr)
56-
57-
/** Return the unlifted value of this expression.
58-
*
59-
* Emits an error and throws if the expression does not contain a value or contains side effects.
60-
* Otherwise returns the value.
61-
*/
62-
def unliftOrError(using qctx: QuoteContext, unlift: Unliftable[T]): T =
63-
def reportError =
64-
val msg = s"Expected a known value. \n\nThe value of: ${expr.show}\ncould not be unlifted using $unlift"
65-
report.throwError(msg, expr)
66-
unlift.fromExpr(expr).getOrElse(reportError)
67-
end extension
68-
698
/** `e.betaReduce` returns an expression that is functionally equivalent to `e`,
709
* however if `e` is of the form `((y1, ..., yn) => e2)(e1, ..., en)`
7110
* then it optimizes this the top most call by returning the result of beta-reducing the application.
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
package scala.quoted
22

3-
abstract class Expr[+T] private[scala]:
4-
def unseal(using qctx: QuoteContext): qctx.reflect.Term
5-
def asExprOf[X](using tp: scala.quoted.Type[X])(using qctx: QuoteContext): scala.quoted.Expr[X] = ???
3+
abstract class Expr[+T] private[scala]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package scala.quoted
2+
3+
trait Unliftable[T]:
4+
def fromExpr(x: Expr[T]): QuoteContext ?=> Option[T]

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

Lines changed: 0 additions & 5 deletions
This file was deleted.

library/src/scala/quoted/QuoteContext.scala

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,59 @@ package scala.quoted
1010
*/
1111
trait QuoteContext { self: internal.QuoteUnpickler & internal.QuoteMatching =>
1212

13+
// Extension methods for `Expr[T]`
14+
extension [T](self: Expr[T]):
15+
/** Show a source code like representation of this expression without syntax highlight */
16+
def show: String
17+
18+
/** Shows the tree as fully typed source code colored with ANSI */
19+
def showAnsiColored: String
20+
21+
/** Pattern matches `this` against `that`. Effectively performing a deep equality check.
22+
* It does the equivalent of
23+
* ```
24+
* this match
25+
* case '{...} => true // where the contents of the pattern are the contents of `that`
26+
* case _ => false
27+
* ```
28+
*/
29+
def matches(that: Expr[Any]): Boolean
30+
31+
/** Return the unlifted value of this expression.
32+
*
33+
* Returns `None` if the expression does not contain a value or contains side effects.
34+
* Otherwise returns the `Some` of the value.
35+
*/
36+
def unlift(using unlift: Unliftable[T]): Option[T] =
37+
unlift.fromExpr(self)(using QuoteContext.this)
38+
39+
/** Return the unlifted value of this expression.
40+
*
41+
* Emits an error and throws if the expression does not contain a value or contains side effects.
42+
* Otherwise returns the value.
43+
*/
44+
def unliftOrError(using unlift: Unliftable[T]): T =
45+
def reportError =
46+
val msg = s"Expected a known value. \n\nThe value of: ${self.show}\ncould not be unlifted using $unlift"
47+
report.throwError(msg, self)(using QuoteContext.this)
48+
unlift.fromExpr(self)(using QuoteContext.this).getOrElse(reportError)
49+
50+
/** View this expression `quoted.Expr[T]` as a `Term` */
51+
def unseal: reflect.Term = self.asReflectTree // TODO remove
52+
53+
/** View this expression `quoted.Expr[T]` as a `Term` */
54+
def asReflectTree: reflect.Term
55+
end extension
56+
57+
// Extension methods for `Expr[Any]` that take another explicit type parameter
58+
extension [X](self: Expr[Any]):
59+
/** Checks is the `quoted.Expr[?]` is valid expression of type `X` */
60+
def isExprOf(using tp: scala.quoted.Type[X]): Boolean
61+
62+
/** Convert this to an `quoted.Expr[X]` if this expression is a valid expression of type `X` or throws */
63+
def asExprOf(using tp: scala.quoted.Type[X]): scala.quoted.Expr[X]
64+
end extension
65+
1366
/** Low-level Typed AST API metaprogramming API.
1467
* This API does not have the static type guarantiees that `Expr` and `Type` provide.
1568
*/
Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import scala.quoted._
22

3-
object Macros {
4-
5-
implicit inline def identityMaped[T](x: => T): T = ${ impl('x) }
3+
object Macros:
4+
implicit inline def identityMaped[T](x: => T): T = ${ MacrosImpl.impl('x) }
65

6+
object MacrosImpl:
77
def impl[T: Type](x: Expr[T])(using qctx: QuoteContext) : Expr[T] = {
8-
import qctx.reflect.{_, given} // FIXME: #8919
8+
import qctx.reflect._
99
val identityMap = new TreeMap { }
1010
val transformed = identityMap.transformTerm(x.unseal).asExprOf[T]
1111
transformed
1212
}
13-
14-
}

0 commit comments

Comments
 (0)