Skip to content

Commit 0a9404a

Browse files
committed
Implement alternative seal API
This version allow sealing terms even if the type is not statically known while keeping the type safty guaratees on quoted expressions. Added reflective tpe on `quoted.Expr[T]` to get its `quoted.Type[T]` even when `T` is just an existential type. Added `asExprOf` to cast an `Expr[_]` to an `Expr[U]`. Fails if the exression is not a subtype of `U`.
1 parent 1199f85 commit 0a9404a

File tree

3 files changed

+60
-33
lines changed

3 files changed

+60
-33
lines changed

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

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,32 @@ import dotty.tools.dotc.core.Flags._
66
import dotty.tools.dotc.core.Symbols.defn
77
import dotty.tools.dotc.core.StdNames.nme
88
import dotty.tools.dotc.core.quoted.PickledQuotes
9-
import dotty.tools.dotc.core.Types
9+
import dotty.tools.dotc.core.{Contexts, Types}
10+
11+
import scala.quoted.Expr
1012

1113
trait QuotedOpsImpl extends scala.tasty.reflect.QuotedOps with CoreImpl {
1214

13-
def QuotedExprDeco[T](x: scala.quoted.Expr[T]): QuotedExprAPI = new QuotedExprAPI {
14-
def unseal(implicit ctx: Context): Term = PickledQuotes.quotedExprToTree(x)
15+
def QuotedExprDeco[T](thisExpr: scala.quoted.Expr[T]) = new QuotedExprAPI {
16+
type Tpe = T
17+
18+
def unseal(implicit ctx: Context): Term = PickledQuotes.quotedExprToTree(thisExpr)
19+
20+
def asExprOf[T: scala.quoted.Type](implicit ctx: Context): scala.quoted.Expr[T] = {
21+
val expectedType = QuotedTypeDeco(implicitly[scala.quoted.Type[T]]).unseal.tpe
22+
if (unseal.tpe <:< expectedType) thisExpr.asInstanceOf[scala.quoted.Expr[T]]
23+
else throw new scala.tasty.TastyTypecheckError(
24+
s"""Expr: ${unseal.show}
25+
|did not conform to type: ${expectedType.show}
26+
|""".stripMargin
27+
)
28+
}
29+
30+
def tpe(implicit ctx: Context): quoted.Type[T] = {
31+
val tree = unseal
32+
val tpt = tpd.TypeTree(tree.tpe).withPos(tree.pos)
33+
new scala.quoted.Types.TreeType(tpt).asInstanceOf[quoted.Type[T]]
34+
}
1535
}
1636

1737
def QuotedTypeDeco[T](x: scala.quoted.Type[T]): QuotedTypeAPI = new QuotedTypeAPI {
@@ -20,10 +40,10 @@ trait QuotedOpsImpl extends scala.tasty.reflect.QuotedOps with CoreImpl {
2040

2141
def TermToQuoteDeco(term: Term): TermToQuotedAPI = new TermToQuotedAPI {
2242

23-
def seal[T: scala.quoted.Type](implicit ctx: Context): scala.quoted.Expr[T] = {
24-
25-
val expectedType = QuotedTypeDeco(implicitly[scala.quoted.Type[T]]).unseal.tpe
43+
def seal[T: scala.quoted.Type](implicit ctx: Context): scala.quoted.Expr[T] =
44+
QuotedExprDeco(seal2).asExprOf[T]
2645

46+
def seal2(implicit ctx: Context): scala.quoted.Expr[Tpe] = {
2747
def etaExpand(term: Term): Term = term.tpe.widen match {
2848
case mtpe: Types.MethodType if !mtpe.isParamDependent =>
2949
val closureResType = mtpe.resType match {
@@ -37,22 +57,8 @@ trait QuotedOpsImpl extends scala.tasty.reflect.QuotedOps with CoreImpl {
3757
}
3858

3959
val expanded = etaExpand(term)
40-
if (expanded.tpe <:< expectedType) {
41-
new scala.quoted.Exprs.TastyTreeExpr(expanded).asInstanceOf[scala.quoted.Expr[T]]
42-
} else {
43-
throw new scala.tasty.TastyTypecheckError(
44-
s"""Term: ${term.show}
45-
|did not conform to type: ${expectedType.show}
46-
|""".stripMargin
47-
)
48-
}
60+
new scala.quoted.Exprs.TastyTreeExpr(expanded).asInstanceOf[scala.quoted.Expr[Tpe]]
4961
}
5062
}
5163

52-
def TypeToQuoteDeco(tpe: Types.Type): TypeToQuotedAPI = new TypeToQuotedAPI {
53-
def seal(implicit ctx: Context): quoted.Type[_] = {
54-
val dummyPos = ctx.owner.pos // FIXME
55-
new scala.quoted.Types.TreeType(tpd.TypeTree(tpe).withPos(dummyPos))
56-
}
57-
}
5864
}

library/src-bootstrapped/scala/tasty/reflect/utils/TreeUtils.scala

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ trait TreeUtils {
99
import reflect._
1010

1111
def let(rhs: Term)(in: Term.Ident => Term): Term = {
12-
type T // TODO probably it is better to use the Sealed contruct rather than let the user create their own existential type
13-
implicit val rhsTpe: quoted.Type[T] = rhs.tpe.seal.asInstanceOf[quoted.Type[T]]
14-
val rhsExpr = rhs.seal[T]
12+
val rhsExpr = rhs.seal2
13+
val rhsTpe = rhsExpr.tpe
1514
val expr = '{
16-
val x = ~rhsExpr
17-
~in(('(x)).unseal.asInstanceOf[Term.Ident]).seal[Any]
15+
val x: ~rhsTpe = ~rhsExpr
16+
~in(('(x)).unseal.asInstanceOf[Term.Ident]).seal2
1817
}
1918
expr.unseal
2019
}
Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,34 @@
11
package scala.tasty.reflect
22

3-
/** Extension methods on scala.quoted.{Expr|Type} to convert to scala.tasty.Tasty objects */
3+
/** Extension methods on scala.quoted.{Expr|Type} to convert to scala.tasty.Tasty objects
4+
*
5+
* +------+ .unseal +------------------+
6+
* | Term | <--------------- | quoted.Expr[T] |
7+
* +------+ |------------------|
8+
* \ | ^ |
9+
* \ | |.asExprOf[T] |
10+
* \ .seal2 | | |
11+
* +-----------------> quoted.Expr[_] |
12+
* +------------------+
13+
*/
414
trait QuotedOps extends Core {
515

616
trait QuotedExprAPI {
17+
/** Type of this expression */
18+
type Tpe
19+
720
/** View this expression `Expr[T]` as a `Term` */
821
def unseal(implicit ctx: Context): Term
22+
23+
/** Convert any `Expr[_]` to an `Expr[T]` and check that it conforms to `T` */
24+
def asExprOf[T: scala.quoted.Type](implicit ctx: Context): scala.quoted.Expr[T]
25+
26+
/** Get the quoted.Type of this expression */
27+
def tpe(implicit ctx: Context): quoted.Type[Tpe]
28+
}
29+
implicit def QuotedExprDeco[T](expr: quoted.Expr[T]): QuotedExprAPI {
30+
type Tpe = T
931
}
10-
implicit def QuotedExprDeco[T](expr: quoted.Expr[T]): QuotedExprAPI
1132

1233
trait QuotedTypeAPI {
1334
/** View this expression `Type[T]` as a `TypeTree` */
@@ -18,12 +39,13 @@ trait QuotedOps extends Core {
1839
trait TermToQuotedAPI {
1940
/** Convert `Term` to an `Expr[T]` and check that it conforms to `T` */
2041
def seal[T: scala.quoted.Type](implicit ctx: Context): scala.quoted.Expr[T]
42+
43+
/** Type of this expression */
44+
type Tpe
45+
46+
/** Convert `Term` to an `Expr[Tpe]` */
47+
def seal2(implicit ctx: Context): scala.quoted.Expr[Tpe]
2148
}
2249
implicit def TermToQuoteDeco(term: Term): TermToQuotedAPI
2350

24-
trait TypeToQuotedAPI {
25-
/** Convert `Type` to an `quoted.Type[T]` */
26-
def seal(implicit ctx: Context): scala.quoted.Type[_]
27-
}
28-
implicit def TypeToQuoteDeco(tpe: Type): TypeToQuotedAPI
2951
}

0 commit comments

Comments
 (0)