Skip to content

Commit ab1b3a9

Browse files
committed
Quoted pattern type variables without backticks
With this change we can support references to quote pattern type variables without backticks. * Reduces overhead when using explicit type variable definition ```scala case '{ type t; ... : F[t] } ``` * Enable the use of many type variable definitions without an explicit type variable definition ```scala case '{ ... : F[t, t] } ```
1 parent 0ad4a57 commit ab1b3a9

File tree

3 files changed

+97
-17
lines changed

3 files changed

+97
-17
lines changed

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,35 @@ object desugar {
363363
adaptToExpectedTpt(tree)
364364
}
365365

366+
/** Split out the quoted pattern type variable definition from the pattern.
367+
*
368+
* Type variable definitions are all the `type t` defined at the start of a quoted pattern.
369+
* Were name `t` is a pattern type variable name (i.e. lower case letters).
370+
*
371+
* ```
372+
* type t1; ...; type tn; <pattern>
373+
* ```
374+
* is split into
375+
* ```
376+
* (List(<type t1>; ...; <type tn>), <pattern>)
377+
* ```
378+
*/
379+
def quotedPatternTypeVariables(tree: untpd.Tree)(using Context): (List[untpd.TypeDef], untpd.Tree) =
380+
tree match
381+
case untpd.Block(stats, expr) =>
382+
val untpdTypeVariables = stats.takeWhile {
383+
case tdef @ untpd.TypeDef(name, _) => name.isVarPattern
384+
case _ => false
385+
}.asInstanceOf[List[untpd.TypeDef]]
386+
val otherStats = stats.dropWhile {
387+
case tdef @ untpd.TypeDef(name, _) => name.isVarPattern
388+
case _ => false
389+
}
390+
val pattern = if otherStats.isEmpty then expr else untpd.cpy.Block(tree)(otherStats, expr)
391+
(untpdTypeVariables, pattern)
392+
case _ =>
393+
(Nil, tree)
394+
366395
/** Add all evidence parameters in `params` as implicit parameters to `meth`.
367396
* If the parameters of `meth` end in an implicit parameter list or using clause,
368397
* evidence parameters are added in front of that list. Otherwise they are added

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

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import dotty.tools.dotc.core.Decorators._
1111
import dotty.tools.dotc.core.Flags._
1212
import dotty.tools.dotc.core.NameKinds.PatMatGivenVarName
1313
import dotty.tools.dotc.core.Names._
14+
import dotty.tools.dotc.core.NameOps.*
1415
import dotty.tools.dotc.core.StagingContext._
1516
import dotty.tools.dotc.core.StdNames._
1617
import dotty.tools.dotc.core.Symbols._
@@ -19,17 +20,18 @@ import dotty.tools.dotc.inlines.PrepareInlineable
1920
import dotty.tools.dotc.transform.SymUtils._
2021
import dotty.tools.dotc.typer.Implicits._
2122
import dotty.tools.dotc.typer.Inferencing._
23+
import dotty.tools.dotc.util.Property
2224
import dotty.tools.dotc.util.Spans._
2325
import dotty.tools.dotc.util.Stats.record
2426
import dotty.tools.dotc.reporting.IllegalVariableInPatternAlternative
2527
import scala.collection.mutable
2628

27-
2829
/** Type quotes `'{ ... }` and splices `${ ... }` */
2930
trait QuotesAndSplices {
3031
self: Typer =>
3132

32-
import tpd._
33+
import tpd.*
34+
import QuotesAndSplices.*
3335

3436
/** Translate `'{ e }` into `scala.quoted.Expr.apply(e)` and `'[T]` into `scala.quoted.Type.apply[T]`
3537
* while tracking the quotation level in the context.
@@ -149,19 +151,26 @@ trait QuotesAndSplices {
149151
* The resulting pattern is the split in `splitQuotePattern`.
150152
*/
151153
def typedQuotedTypeVar(tree: untpd.Ident, pt: Type)(using Context): Tree =
152-
def spliceOwner(ctx: Context): Symbol =
153-
if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner
154-
val name = tree.name.toTypeName
155-
val nameOfSyntheticGiven = PatMatGivenVarName.fresh(tree.name.toTermName)
156-
val expr = untpd.cpy.Ident(tree)(nameOfSyntheticGiven)
157154
val typeSymInfo = pt match
158155
case pt: TypeBounds => pt
159156
case _ => TypeBounds.empty
160-
val typeSym = newSymbol(spliceOwner(ctx), name, EmptyFlags, typeSymInfo, NoSymbol, tree.span)
161-
typeSym.addAnnotation(Annotation(New(ref(defn.QuotedRuntimePatterns_patternTypeAnnot.typeRef)).withSpan(tree.span)))
162-
val pat = typedPattern(expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))(
163-
using spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx)))
164-
pat.select(tpnme.Underlying)
157+
getQuotedPatternTypeVariable(tree.name.asTypeName) match
158+
case Some(typeSym) =>
159+
if !(typeSymInfo =:= TypeBounds.empty) then
160+
report.warning(em"Ignored bound$typeSymInfo\n\nConsider defining bounds explicitly `'{ $typeSym$typeSymInfo; ... }`", tree.srcPos)
161+
ref(typeSym)
162+
case None =>
163+
def spliceOwner(ctx: Context): Symbol =
164+
if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner
165+
val name = tree.name.toTypeName
166+
val nameOfSyntheticGiven = PatMatGivenVarName.fresh(tree.name.toTermName)
167+
val expr = untpd.cpy.Ident(tree)(nameOfSyntheticGiven)
168+
val typeSym = newSymbol(spliceOwner(ctx), name, EmptyFlags, typeSymInfo, NoSymbol, tree.span)
169+
typeSym.addAnnotation(Annotation(New(ref(defn.QuotedRuntimePatterns_patternTypeAnnot.typeRef)).withSpan(tree.span)))
170+
addQuotedPatternTypeVariable(typeSym)
171+
val pat = typedPattern(expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))(
172+
using spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx)))
173+
pat.select(tpnme.Underlying)
165174

166175
def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree =
167176
val tpt = typedType(tree.tpt)
@@ -392,11 +401,24 @@ trait QuotesAndSplices {
392401
case Some(argPt: ValueType) => argPt // excludes TypeBounds
393402
case _ => defn.AnyType
394403
}
395-
val quoted0 = desugar.quotedPattern(quoted, untpd.TypedSplice(TypeTree(quotedPt)))
396-
val quoteCtx = quoteContext.addMode(Mode.QuotedPattern).retractMode(Mode.Pattern)
397-
val quoted1 =
398-
if quoted.isType then typedType(quoted0, WildcardType)(using quoteCtx)
399-
else typedExpr(quoted0, WildcardType)(using quoteCtx)
404+
val (untpdTypeVariables, quoted0) = desugar.quotedPatternTypeVariables(desugar.quotedPattern(quoted, untpd.TypedSplice(TypeTree(quotedPt))))
405+
406+
val (typeTypeVariables, patternCtx) =
407+
val quoteCtx = quotePatternContext()
408+
if untpdTypeVariables.isEmpty then (Nil, quoteCtx)
409+
else typedBlockStats(untpdTypeVariables)(using quoteCtx)
410+
411+
val quoted1 = inContext(patternCtx) {
412+
for typeVariable <- typeTypeVariables do
413+
addQuotedPatternTypeVariable(typeVariable.symbol)
414+
415+
val pattern =
416+
if quoted.isType then typedType(quoted0, WildcardType)
417+
else typedExpr(quoted0, WildcardType)
418+
419+
if untpdTypeVariables.isEmpty then pattern
420+
else tpd.Block(typeTypeVariables, pattern)
421+
}
400422

401423
val (typeBindings, shape, splices) = splitQuotePattern(quoted1)
402424

@@ -453,3 +475,24 @@ trait QuotesAndSplices {
453475
proto = quoteClass.typeRef.appliedTo(replaceBindings(quoted1.tpe) & quotedPt))
454476
}
455477
}
478+
479+
object QuotesAndSplices {
480+
import tpd._
481+
482+
/** Key for mapping from quoted pattern type variable names into their symbol */
483+
private val TypeVariableKey = new Property.Key[collection.mutable.Map[TypeName, Symbol]]
484+
485+
/** Get the symbol for the quoted pattern type variable if it exists */
486+
def getQuotedPatternTypeVariable(name: TypeName)(using Context): Option[Symbol] =
487+
ctx.property(TypeVariableKey).get.get(name)
488+
489+
/** Get the symbol for the quoted pattern type variable if it exists */
490+
def addQuotedPatternTypeVariable(sym: Symbol)(using Context): Unit =
491+
ctx.property(TypeVariableKey).get.update(sym.name.asTypeName, sym)
492+
493+
/** Context used to type the contents of a quoted */
494+
def quotePatternContext()(using Context): Context =
495+
quoteContext.fresh.setNewScope
496+
.addMode(Mode.QuotedPattern).retractMode(Mode.Pattern)
497+
.setProperty(TypeVariableKey, collection.mutable.Map.empty)
498+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import scala.quoted.*
2+
3+
def foo(expr: Expr[Any])(using Quotes): Any =
4+
expr match
5+
case '{ $x: Map[t, t] } =>
6+
case '{ type t; $x: Any } =>
7+
case '{ type t; $x: Map[t, t] } =>
8+
case '{ ($x: Set[t]).toSet[t] } =>

0 commit comments

Comments
 (0)