Skip to content

Commit e42e9f8

Browse files
committed
Define Expr methods in QuoteContext
This homogenizes the way Expr and Tree are implemented. All the logic is internally implemented by the implementation of QuoteContext. This implies that Expr does not need to implement any methods outside of the QuoteContext.
1 parent b4d7084 commit e42e9f8

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)