Skip to content

Commit 50db81d

Browse files
Merge pull request #11608 from dotty-staging/change-Type-encoding-signature
Change signature of quoted.Type encoding
2 parents 086d1a8 + 9f1f1ef commit 50db81d

File tree

9 files changed

+85
-48
lines changed

9 files changed

+85
-48
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -938,10 +938,15 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
938938
* The result can be the contents of a term or type quote, which
939939
* will return a term or type tree respectively.
940940
*/
941-
def unapply(tree: tpd.Tree)(using Context): Option[tpd.Tree] = tree match {
942-
case tree: GenericApply if tree.symbol.isQuote => Some(tree.args.head)
943-
case _ => None
944-
}
941+
def unapply(tree: tpd.Apply)(using Context): Option[tpd.Tree] =
942+
if tree.symbol == defn.QuotedRuntime_exprQuote then
943+
// quoted.runtime.Expr.quote[T](<body>)
944+
Some(tree.args.head)
945+
else if tree.symbol == defn.QuotedTypeModule_of then
946+
// quoted.Type.of[<body>](quotes)
947+
val TypeApply(_, body :: _) = tree.fun
948+
Some(body)
949+
else None
945950
}
946951

947952
/** Extractors for splices */

compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import scala.annotation.constructorOnly
3535
* Local type references can be used at the level of their definition or lower. If used used at a higher level,
3636
* it will be healed if possible, otherwise it is inconsistent.
3737
*
38-
* Type healing consists in transforming a phase inconsistent type `T` into a splice of `${summon[Type[T]]}`.
38+
* Type healing consists in transforming a phase inconsistent type `T` into `summon[Type[T]].Underlying`.
3939
*
4040
* As references to types do not necessarily have an associated tree it is not always possible to replace the types directly.
4141
* Instead we always generate a type alias for it and place it at the start of the surrounding quote. This also avoids duplication.
@@ -48,7 +48,7 @@ import scala.annotation.constructorOnly
4848
* is transformed to
4949
*
5050
* '{
51-
* type t$1 = ${summon[Type[T]]}
51+
* type t$1 = summon[Type[T]].Underlying
5252
* val x: List[t$1] = List[t$1]();
5353
* ()
5454
* }
@@ -103,7 +103,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
103103
}
104104

105105
/** Transform quoted trees while maintaining phase correctness */
106-
override protected def transformQuotation(body: Tree, quote: Tree)(using Context): Tree = {
106+
override protected def transformQuotation(body: Tree, quote: Apply)(using Context): Tree = {
107107
val taggedTypes = new PCPCheckAndHeal.QuoteTypeTags(quote.span)
108108

109109
if (ctx.property(InAnnotation).isDefined)
@@ -118,13 +118,20 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
118118
case Nil => body1
119119
case tags => tpd.Block(tags, body1).withSpan(body.span)
120120

121-
quote match {
122-
case Apply(fn1 @ TypeApply(fn0, targs), _) =>
123-
val targs1 = targs.map(targ => TypeTree(healTypeOfTerm(fn1.srcPos)(targ.tpe)))
124-
cpy.Apply(quote)(cpy.TypeApply(fn1)(fn0, targs1), body2 :: Nil)
125-
case quote: TypeApply =>
126-
cpy.TypeApply(quote)(quote.fun, body2 :: Nil)
127-
}
121+
if body.isTerm then
122+
// `quoted.runtime.Expr.quote[T](<body>)` --> `quoted.runtime.Expr.quote[T2](<body2>)`
123+
val TypeApply(fun, targs) = quote.fun
124+
val targs2 = targs.map(targ => TypeTree(healTypeOfTerm(quote.fun.srcPos)(targ.tpe)))
125+
cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), body2 :: Nil)
126+
else
127+
body.tpe match
128+
case tp @ TypeRef(x: TermRef, _) if tp.symbol == defn.QuotedType_splice =>
129+
// Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x`
130+
ref(x)
131+
case _ =>
132+
// `quoted.Type.of[<body>](quotes)` --> `quoted.Type.of[<body2>](quotes)`
133+
val TypeApply(fun, _) = quote.fun
134+
cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, body2 :: Nil), quote.args)
128135
}
129136

130137
/** Transform splice
@@ -136,13 +143,13 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
136143
val body1 = transform(body)(using spliceContext)
137144
splice.fun match {
138145
case fun @ TypeApply(_, _ :: Nil) =>
139-
// Type of the splice itsel must also be healed
140-
// internal.Quoted.expr[F[T]](... T ...) --> internal.Quoted.expr[F[$t]](... T ...)
146+
// Type of the splice itself must also be healed
147+
// `quoted.runtime.Expr.quote[F[T]](... T ...)` --> `internal.Quoted.expr[F[$t]](... T ...)`
141148
val tp = healType(splice.srcPos)(splice.tpe.widenTermRefExpr)
142149
cpy.Apply(splice)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), body1 :: Nil)
143150
case f @ Apply(fun @ TypeApply(_, _), qctx :: Nil) =>
144-
// Type of the splice itsel must also be healed
145-
// internal.Quoted.expr[F[T]](... T ...) --> internal.Quoted.expr[F[$t]](... T ...)
151+
// Type of the splice itself must also be healed
152+
// `quoted.runtime.Expr.quote[F[T]](... T ...)` --> `internal.Quoted.expr[F[$t]](... T ...)`
146153
val tp = healType(splice.srcPos)(splice.tpe.widenTermRefExpr)
147154
cpy.Apply(splice)(cpy.Apply(f)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), qctx :: Nil), body1 :: Nil)
148155
}

compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ class PickleQuotes extends MacroTransform {
127127
* `scala.quoted.Unpickler.unpickleExpr` that matches `tpe` with
128128
* core and splices as arguments.
129129
*/
130-
override protected def transformQuotation(body: Tree, quote: Tree)(using Context): Tree = {
130+
override protected def transformQuotation(body: Tree, quote: Apply)(using Context): Tree = {
131131
val isType = quote.symbol eq defn.QuotedTypeModule_of
132132
if (level > 0) {
133133
val body1 = nested(isQuote = true).transform(body)(using quoteContext)
@@ -139,14 +139,14 @@ class PickleQuotes extends MacroTransform {
139139
val body2 =
140140
if (body1.isType) body1
141141
else Inlined(Inliner.inlineCallTrace(ctx.owner, quote.sourcePos), Nil, body1)
142-
pickledQuote(body2, splices, body.tpe, isType).withSpan(quote.span)
142+
pickledQuote(quote, body2, splices, body.tpe, isType).withSpan(quote.span)
143143
}
144144
else
145145
body
146146
}
147147
}
148148

149-
private def pickledQuote(body: Tree, splices: List[Tree], originalTp: Type, isType: Boolean)(using Context) = {
149+
private def pickledQuote(quote: Apply, body: Tree, splices: List[Tree], originalTp: Type, isType: Boolean)(using Context) = {
150150
/** Encode quote using Reflection.Literal
151151
*
152152
* Generate the code
@@ -279,7 +279,7 @@ class PickleQuotes extends MacroTransform {
279279
*
280280
* Generate the code
281281
* ```scala
282-
* qctx => qctx.reflect.TypeReprMethods.asType(
282+
* qctx.reflect.TypeReprMethods.asType(
283283
* qctx.reflect.TypeRepr.typeConstructorOf(classOf[<type>]])
284284
* ).asInstanceOf[scala.quoted.Type[<type>]]
285285
* ```
@@ -288,17 +288,13 @@ class PickleQuotes extends MacroTransform {
288288
def taggedType() =
289289
val typeType = defn.QuotedTypeClass.typeRef.appliedTo(body.tpe)
290290
val classTree = TypeApply(ref(defn.Predef_classOf.termRef), body :: Nil)
291-
val lambdaTpe = MethodType(defn.QuotesClass.typeRef :: Nil, typeType)
292-
def callTypeConstructorOf(ts: List[Tree]) = {
293-
val reflect = ts.head.select("reflect".toTermName)
294-
val typeRepr = reflect.select("TypeRepr".toTermName).select("typeConstructorOf".toTermName).appliedTo(classTree)
295-
reflect.select("TypeReprMethods".toTermName).select("asType".toTermName).appliedTo(typeRepr).asInstance(typeType)
296-
}
297-
Lambda(lambdaTpe, callTypeConstructorOf).withSpan(body.span)
291+
val reflect = quote.args.head.select("reflect".toTermName)
292+
val typeRepr = reflect.select("TypeRepr".toTermName).select("typeConstructorOf".toTermName).appliedTo(classTree)
293+
reflect.select("TypeReprMethods".toTermName).select("asType".toTermName).appliedTo(typeRepr).asInstance(typeType)
298294

299295
if (isType) {
300296
if (splices.isEmpty && body.symbol.isPrimitiveValueClass) taggedType()
301-
else pickleAsTasty()
297+
else pickleAsTasty().select(nme.apply).appliedTo(quote.args.head) // TODO do not create lambda
302298
}
303299
else getLiteral(body) match {
304300
case Some(lit) => pickleAsValue(lit)
@@ -485,9 +481,9 @@ class PickleQuotes extends MacroTransform {
485481
transform(tree)(using ctx.withSource(tree.source))
486482
else reporting.trace(i"Reifier.transform $tree at $level", show = true) {
487483
tree match {
488-
case Apply(Select(TypeApply(fn, (body: RefTree) :: Nil), _), _) if fn.symbol == defn.QuotedTypeModule_of && isCaptured(body.symbol, level + 1) =>
489-
// Optimization: avoid the full conversion when capturing `x`
490-
// in '{ x } to '{ ${x$1} } and go directly to `x$1`
484+
case Apply(TypeApply(fn, (body: RefTree) :: Nil), _) if fn.symbol == defn.QuotedTypeModule_of && isCaptured(body.symbol, level + 1) =>
485+
// Optimization: avoid the full conversion when capturing `X` with `x$1: Type[X$1]`
486+
// in `Type.of[X]` to `Type.of[x$1.Underlying]` and go directly to `X$1`
491487
capturers(body.symbol)(body)
492488
case tree: RefTree if isCaptured(tree.symbol, level) =>
493489
val body = capturers(tree.symbol).apply(tree)

compiler/src/dotty/tools/dotc/transform/Splicer.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ object Splicer {
149149
case Apply(Select(Apply(fn, quoted :: Nil), nme.apply), _) if fn.symbol == defn.QuotedRuntime_exprQuote =>
150150
// OK
151151

152-
case Apply(Select(TypeApply(fn, List(quoted)), nme.apply), _)if fn.symbol == defn.QuotedTypeModule_of =>
152+
case Apply(TypeApply(fn, List(quoted)), _)if fn.symbol == defn.QuotedTypeModule_of =>
153153
// OK
154154

155155
case Literal(Constant(value)) =>
@@ -233,7 +233,7 @@ object Splicer {
233233
}
234234
interpretQuote(quoted1)
235235

236-
case Apply(Select(TypeApply(fn, quoted :: Nil), _), _) if fn.symbol == defn.QuotedTypeModule_of =>
236+
case Apply(TypeApply(fn, quoted :: Nil), _) if fn.symbol == defn.QuotedTypeModule_of =>
237237
interpretTypeQuote(quoted)
238238

239239
case Literal(Constant(value)) =>

compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,17 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
6464
case _ =>
6565
}
6666

67-
/** Transform the quote `quote` which contains the quoted `body`. */
68-
protected def transformQuotation(body: Tree, quote: Tree)(using Context): Tree =
69-
quote match {
70-
case quote: Apply => cpy.Apply(quote)(quote.fun, body :: Nil)
71-
case quote: TypeApply => cpy.TypeApply(quote)(quote.fun, body :: Nil)
72-
}
67+
/** Transform the quote `quote` which contains the quoted `body`.
68+
*
69+
* - `quoted.runtime.Expr.quote[T](<body0>)` --> `quoted.runtime.Expr.quote[T](<body>)`
70+
* - `quoted.Type.of[<body0>](quotes)` --> `quoted.Type.of[<body>](quotes)`
71+
*/
72+
protected def transformQuotation(body: Tree, quote: Apply)(using Context): Tree =
73+
if body.isTerm then
74+
cpy.Apply(quote)(quote.fun, body :: Nil)
75+
else
76+
val TypeApply(fun, _) = quote.fun
77+
cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, body :: Nil), quote.args)
7378

7479
/** Transform the expression splice `splice` which contains the spliced `body`. */
7580
protected def transformSplice(body: Tree, splice: Apply)(using Context): Tree
@@ -98,17 +103,17 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
98103
case Apply(Select(Quoted(quotedTree), _), _) if quotedTree.isType =>
99104
dropEmptyBlocks(quotedTree) match
100105
case SplicedType(t) =>
101-
// '[ x.$splice ] --> x
106+
// Optimization: `quoted.Type.of[x.Underlying]` --> `x`
102107
transform(t)
103108
case _ =>
104109
super.transform(tree)
105110

106-
case Quoted(quotedTree) =>
111+
case tree @ Quoted(quotedTree) =>
107112
val old = inQuoteOrSplice
108113
inQuoteOrSplice = true
109114
try dropEmptyBlocks(quotedTree) match {
110115
case Spliced(t) =>
111-
// '{ $x } --> x
116+
// Optimization: `'{ $x }` --> `x`
112117
// and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]`
113118
transform(t).asInstance(tree.tpe)
114119
case _ => transformQuotation(quotedTree, tree)
@@ -119,7 +124,9 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
119124
val old = inQuoteOrSplice
120125
inQuoteOrSplice = true
121126
try dropEmptyBlocks(splicedTree) match {
122-
case Quoted(t) => transform(t) // ${ 'x } --> x
127+
case Quoted(t) =>
128+
// Optimization: `${ 'x }` --> `x`
129+
transform(t)
123130
case _ => transformSplice(splicedTree, tree)
124131
}
125132
finally inQuoteOrSplice = old

compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ trait QuotesAndSplices {
461461
val quoteClass = if (tree.quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass
462462
val quotedPattern =
463463
if (tree.quoted.isTerm) ref(defn.QuotedRuntime_exprQuote.termRef).appliedToType(defn.AnyType).appliedTo(shape).select(nme.apply).appliedTo(qctx)
464-
else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).select(nme.apply).appliedTo(qctx)
464+
else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).appliedTo(qctx)
465465

466466
val matchModule = if tree.quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch
467467
val unapplyFun = qctx.asInstance(defn.QuoteMatchingClass.typeRef).select(matchModule).select(nme.unapply)

library/src/scala/quoted/Type.scala renamed to library/src-bootstrapped/scala/quoted/Type.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ object Type:
1818

1919
/** Return a quoted.Type with the given type */
2020
@compileTimeOnly("Reference to `scala.quoted.Type.of` was not handled by PickleQuotes")
21-
given of[T <: AnyKind]: (Quotes ?=> Type[T]) = ???
21+
given of[T <: AnyKind](using Quotes): Type[T] = ???
2222

2323
end Type
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package scala.quoted
2+
3+
import scala.annotation.compileTimeOnly
4+
5+
/** Type (or type constructor) `T` needed contextually when using `T` in a quoted expression `'{... T ...}` */
6+
abstract class Type[T <: AnyKind] private[scala]:
7+
/** The type represented `Type` */
8+
type Underlying = T
9+
throw Exception("non-bootstrapped-lib")
10+
end Type
11+
12+
/** Methods to interact with the current `Type[T]` in scope */
13+
object Type:
14+
15+
/** Show a source code like representation of this type without syntax highlight */
16+
def show[T <: AnyKind](using Type[T])(using Quotes): String = throw Exception("non-bootstrapped-lib")
17+
18+
/** Return a quoted.Type with the given type */
19+
@compileTimeOnly("Reference to `scala.quoted.Type.of` was not handled by PickleQuotes")
20+
given of[T <: AnyKind]: (Quotes ?=> Type[T]) = throw Exception("non-bootstrapped-lib")
21+
22+
end Type
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
((q: scala.quoted.Quotes) ?=> {
2-
val t: scala.quoted.Type[scala.Predef.String] = scala.quoted.Type.of[scala.Predef.String].apply(using q)
2+
val t: scala.quoted.Type[scala.Predef.String] = scala.quoted.Type.of[scala.Predef.String](q)
33

44
(t: scala.quoted.Type[scala.Predef.String])
55
})

0 commit comments

Comments
 (0)