Skip to content

Commit 7423d31

Browse files
Merge pull request #6930 from dotty-staging/add-type-quote-patterns
Add quoted type patterns
2 parents 175d670 + 738be3b commit 7423d31

File tree

22 files changed

+670
-427
lines changed

22 files changed

+670
-427
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -642,9 +642,14 @@ class Definitions {
642642
@tu lazy val InternalQuoted_patternHole: Symbol = InternalQuotedModule.requiredMethod("patternHole")
643643
@tu lazy val InternalQuoted_patternBindHoleAnnot: ClassSymbol = InternalQuotedModule.requiredClass("patternBindHole")
644644
@tu lazy val InternalQuoted_QuoteTypeTagAnnot: ClassSymbol = InternalQuotedModule.requiredClass("quoteTypeTag")
645+
@tu lazy val InternalQuoted_fromAboveAnnot: ClassSymbol = InternalQuotedModule.requiredClass("fromAbove")
645646

646-
@tu lazy val InternalQuotedMatcherModule: Symbol = ctx.requiredModule("scala.internal.quoted.Matcher")
647-
@tu lazy val InternalQuotedMatcher_unapply: Symbol = InternalQuotedMatcherModule.requiredMethod(nme.unapply)
647+
648+
@tu lazy val InternalQuotedExprModule: Symbol = ctx.requiredModule("scala.internal.quoted.Expr")
649+
@tu lazy val InternalQuotedExpr_unapply: Symbol = InternalQuotedExprModule.requiredMethod(nme.unapply)
650+
651+
@tu lazy val InternalQuotedTypeModule: Symbol = ctx.requiredModule("scala.internal.quoted.Type")
652+
@tu lazy val InternalQuotedType_unapply: Symbol = InternalQuotedTypeModule.requiredMethod(nme.unapply)
648653

649654
@tu lazy val QuotedTypeClass: ClassSymbol = ctx.requiredClass("scala.quoted.Type")
650655
@tu lazy val QuotedType_splice: Symbol = QuotedTypeClass.requiredType(tpnme.splice)

compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,13 @@ object PickledQuotes {
8080
val unpickled = unpickle(tastyBytes, args, isType = true)(ctx.addMode(Mode.ReadPositions))
8181
val tpt = unpickled match {
8282
case Block(aliases, tpt) =>
83-
// `@quoteTypeTag type` aliasses are not required after unpickling
84-
tpt
83+
// `@quoteTypeTag type` aliases are not required after unpickling.
84+
// Type definitions are placeholders for type holes in the pickled quote, at this point
85+
// those holes have been filled. As we already dealias al references to them in `dealiasTypeTags`
86+
// there is no need to keep their definitions in the tree. As artifacts of quote reification
87+
// they also do not have a meaningful position in the source.
88+
val aliases1 = aliases.filter(!_.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot))
89+
seq(aliases1, tpt)
8590
case tpt => tpt
8691
}
8792
tpt.withType(dealiasTypeTags(tpt.tpe))

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1809,6 +1809,7 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend
18091809

18101810
def Definitions_InternalQuoted_patternHole: Symbol = defn.InternalQuoted_patternHole
18111811
def Definitions_InternalQuoted_patternBindHoleAnnot: Symbol = defn.InternalQuoted_patternBindHoleAnnot
1812+
def Definitions_InternalQuoted_fromAboveAnnot: Symbol = defn.InternalQuoted_fromAboveAnnot
18121813

18131814
// Types
18141815

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

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ trait QuotesAndSplices {
4646
case _ =>
4747
}
4848
val tree1 =
49-
if (tree.quoted.isType) typedTypeApply(untpd.TypeApply(untpd.ref(defn.InternalQuoted_typeQuote.termRef), tree.quoted :: Nil), pt)(quoteContext)
50-
else if (ctx.mode.is(Mode.Pattern) && level == 0) typedQuotePattern(tree, pt)
49+
if (ctx.mode.is(Mode.Pattern) && level == 0) typedQuotePattern(tree, pt)
50+
else if (tree.quoted.isType) typedTypeApply(untpd.TypeApply(untpd.ref(defn.InternalQuoted_typeQuote.termRef), tree.quoted :: Nil), pt)(quoteContext)
5151
else typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprQuote.termRef), tree.quoted), pt)(quoteContext)
5252
tree1.withSpan(tree.span)
5353
}
@@ -163,6 +163,16 @@ trait QuotesAndSplices {
163163
})
164164

165165
object splitter extends tpd.TreeMap {
166+
private var variance: Int = 1
167+
168+
@forceInline private def atVariance[T](v: Int)(op: => T): T = {
169+
val saved = variance
170+
variance = v
171+
val res = op
172+
variance = saved
173+
res
174+
}
175+
166176
val patBuf = new mutable.ListBuffer[Tree]
167177
val freshTypePatBuf = new mutable.ListBuffer[Tree]
168178
val freshTypeBindingsBuff = new mutable.ListBuffer[Tree]
@@ -206,11 +216,21 @@ trait QuotesAndSplices {
206216
super.transform(tree)
207217
case tdef: TypeDef if tdef.symbol.hasAnnotation(defn.InternalQuoted_patternBindHoleAnnot) =>
208218
transformTypeBindingTypeDef(tdef, typePatBuf)
219+
case tree @ AppliedTypeTree(tpt, args) =>
220+
val args1: List[Tree] = args.zipWithConserve(tpt.tpe.typeParams.map(_.paramVariance)) { (arg, v) =>
221+
arg.tpe match {
222+
case _: TypeBounds => transform(arg)
223+
case _ => atVariance(variance * v)(transform(arg))
224+
}
225+
}
226+
cpy.AppliedTypeTree(tree)(transform(tpt), args1)
209227
case _ =>
210228
super.transform(tree)
211229
}
212230

213-
def transformTypeBindingTypeDef(tdef: TypeDef, buff: mutable.Builder[Tree, List[Tree]]): Tree = {
231+
private def transformTypeBindingTypeDef(tdef: TypeDef, buff: mutable.Builder[Tree, List[Tree]])(implicit ctx: Context): Tree = {
232+
if (variance == -1)
233+
tdef.symbol.addAnnotation(Annotation(New(ref(defn.InternalQuoted_fromAboveAnnot.typeRef)).withSpan(tdef.span)))
214234
val bindingType = getBinding(tdef.symbol).symbol.typeRef
215235
val bindingTypeTpe = AppliedType(defn.QuotedTypeClass.typeRef, bindingType :: Nil)
216236
assert(tdef.name.startsWith("$"))
@@ -248,7 +268,7 @@ trait QuotesAndSplices {
248268
}
249269

250270
/** Type a quote pattern `case '{ <quoted> } =>` qiven the a current prototype. Typing the pattern
251-
* will also transform it into a call to `scala.internal.quoted.Matcher.unapply`.
271+
* will also transform it into a call to `scala.internal.quoted.Expr.unapply`.
252272
*
253273
* Code directly inside the quote is typed as an expression using Mode.QuotedPattern. Splices
254274
* within the quotes become patterns again and typed acordingly.
@@ -275,7 +295,7 @@ trait QuotesAndSplices {
275295
* and the patterns in the splices. All these are recombined into a call to `Matcher.unapply`.
276296
*
277297
* ```
278-
* case scala.internal.quoted.Matcher.unapply[
298+
* case scala.internal.quoted.Expr.unapply[
279299
* Tuple1[$t @ _], // Type binging definition
280300
* Tuple2[Type[$t], Expr[List[$t]]] // Typing the result of the pattern match
281301
* ](
@@ -344,14 +364,16 @@ trait QuotesAndSplices {
344364

345365
val splicePat = typed(untpd.Tuple(splices.map(x => untpd.TypedSplice(replaceBindingsInTree.transform(x)))).withSpan(quoted.span), patType)
346366

367+
val unapplySym = if (tree.quoted.isTerm) defn.InternalQuotedExpr_unapply else defn.InternalQuotedType_unapply
368+
val quoteClass = if (tree.quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass
369+
val quotedPattern =
370+
if (tree.quoted.isTerm) ref(defn.InternalQuoted_exprQuote.termRef).appliedToType(defn.AnyType).appliedTo(shape).select(nme.apply).appliedTo(qctx)
371+
else ref(defn.InternalQuoted_typeQuote.termRef).appliedToTypeTrees(shape :: Nil)
347372
UnApply(
348-
fun = ref(defn.InternalQuotedMatcher_unapply.termRef).appliedToTypeTrees(typeBindingsTuple :: TypeTree(patType) :: Nil),
349-
implicits =
350-
ref(defn.InternalQuoted_exprQuote.termRef).appliedToType(defn.AnyType).appliedTo(shape).select(nme.apply).appliedTo(qctx) ::
351-
Literal(Constant(typeBindings.nonEmpty)) ::
352-
qctx :: Nil,
373+
fun = ref(unapplySym.termRef).appliedToTypeTrees(typeBindingsTuple :: TypeTree(patType) :: Nil),
374+
implicits = quotedPattern :: Literal(Constant(typeBindings.nonEmpty)) :: qctx :: Nil,
353375
patterns = splicePat :: Nil,
354-
proto = defn.QuotedExprClass.typeRef.appliedTo(replaceBindings(quoted1.tpe) & quotedPt))
376+
proto = quoteClass.typeRef.appliedTo(replaceBindings(quoted1.tpe) & quotedPt))
355377
}
356378
}
357379

docs/docs/reference/other-new-features/quoted-pattern-spec.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,18 @@ def foo(x: Expr[Int]) given tasty.Reflect: Expr[Int] = x match {
1818
```
1919
In the example above we have `$a` which provides a `Bind[Int]`, `$x` which provides an `Expr[Int]` and `${Bind(`a`)}` which probides an `Expr[Int]` that is pattern matched against `Bind(`a`)` to check that it is a reference to `a`.
2020

21-
Quoted patterns are transformed during typer to a call of `scala.internal.quoted.Matcher.unapply` which splits the quoted code into the patterns and a reifiable quote that will be used as witnesses at runtime.
21+
Quoted patterns are transformed during typer to a call of `scala.internal.quoted.Expr.unapply` which splits the quoted code into the patterns and a reifiable quote that will be used as witnesses at runtime.
2222

2323
```scala
2424
def foo(x: Expr[Int]) given tasty.Reflect: Expr[Int] = x match {
25-
case scala.internal.quoted.Matcher.unapply[Tuple3[Bind[Int], Expr[Int], Expr[Int]]](Tuple3(a, x, Bind(`a`), y))('{ @patternBindHole val a: Int = patternHole[Int]; patternHole[Int] + 1 }) =>
25+
case scala.internal.quoted.Expr.unapply[Tuple3[Bind[Int], Expr[Int], Expr[Int]]](Tuple3(a, x, Bind(`a`), y))('{ @patternBindHole val a: Int = patternHole[Int]; patternHole[Int] + 1 }) =>
2626
}
2727
```
2828

2929

3030
## Runtime semantics
3131

32-
At runtime to a `quoted.Expr` can be matched to another using `scala.internal.quoted.Matcher.unapply`.
32+
At runtime to a `quoted.Expr` can be matched to another using `scala.internal.quoted.Expr.unapply`.
3333

3434
```scala
3535
def unapply[Tup <: Tuple](scrutineeExpr: Expr[_])(implicit patternExpr: Expr[_], reflection: Reflection): Option[Tup]
@@ -49,7 +49,7 @@ def matched[T](x: T) = Some(Tuple1(x))
4949
def (x: Matching) && (y: Matching) = if (x == None || y == None) None else Some(x.get ++ y.get)
5050
def fold[T](m: Mattching*) given Env: Matching = m.fold(matched)(_ && _)
5151

52-
// `a =#= b` stands for `a` matches `b`
52+
// `a =#= b` stands for `a` matches `b`
5353
def (scrutinee: Tree) =#= pattern: Tree) given Env: Matching // described by cases in the tables below
5454

5555
def envWith(equiv: (Symbol, Symbol)*) given Env: Env // Adds to the current environment the fact that s1 from the scrutinee is equivalent to s2 in the pattern

0 commit comments

Comments
 (0)