Skip to content

Commit f2ceaa1

Browse files
committed
Support type variable definitions in quoted patterns
1 parent 7f7b0b5 commit f2ceaa1

File tree

6 files changed

+57
-14
lines changed

6 files changed

+57
-14
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1585,6 +1585,19 @@ object Parsers {
15851585
t
15861586
end typ
15871587

1588+
/** TypeBlock ::= {TypeBlockStat semi} Type
1589+
* TypeBlockStat ::= ‘type’ {nl} TypeDcl
1590+
*/
1591+
def typeBlock(): Tree =
1592+
val tDefs = new ListBuffer[Tree]
1593+
while in.token == TYPE do
1594+
val mods = defAnnotsMods(modifierTokens)
1595+
tDefs += typeDefOrDcl(in.offset, in.skipToken(mods))
1596+
acceptStatSep()
1597+
val tpt = typ()
1598+
if tDefs.isEmpty then tpt else Block(tDefs.toList, tpt)
1599+
1600+
15881601
private def makeKindProjectorTypeDef(name: TypeName): TypeDef = {
15891602
val isVarianceAnnotated = name.startsWith("+") || name.startsWith("-")
15901603
// We remove the variance marker from the name without passing along the specified variance at all
@@ -2447,7 +2460,7 @@ object Parsers {
24472460
atSpan(in.skipToken()) {
24482461
withinStaged(StageKind.Quoted | (if (location.inPattern) StageKind.QuotedPattern else 0)) {
24492462
Quote {
2450-
if (in.token == LBRACKET) inBrackets(typ())
2463+
if (in.token == LBRACKET) inBrackets(typeBlock())
24512464
else stagedBlock()
24522465
}
24532466
}
@@ -3080,8 +3093,8 @@ object Parsers {
30803093
/* -------- PARAMETERS ------------------------------------------- */
30813094

30823095
/** DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent
3083-
* DefParamClause ::= DefTypeParamClause
3084-
* | DefTermParamClause
3096+
* DefParamClause ::= DefTypeParamClause
3097+
* | DefTermParamClause
30853098
* | UsingParamClause
30863099
*/
30873100
def typeOrTermParamClauses(
@@ -3179,7 +3192,7 @@ object Parsers {
31793192
* UsingClsTermParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’
31803193
* ClsParams ::= ClsParam {‘,’ ClsParam}
31813194
* ClsParam ::= {Annotation}
3182-
*
3195+
*
31833196
* TypelessClause ::= DefTermParamClause
31843197
* | UsingParamClause
31853198
*
@@ -3557,13 +3570,13 @@ object Parsers {
35573570
}
35583571
}
35593572

3560-
3573+
35613574

35623575
/** DefDef ::= DefSig [‘:’ Type] ‘=’ Expr
35633576
* | this TypelessClauses [DefImplicitClause] `=' ConstrExpr
35643577
* DefDcl ::= DefSig `:' Type
35653578
* DefSig ::= id [DefTypeParamClause] DefTermParamClauses
3566-
*
3579+
*
35673580
* if clauseInterleaving is enabled:
35683581
* DefSig ::= id [DefParamClauses] [DefImplicitClause]
35693582
*/
@@ -3602,8 +3615,8 @@ object Parsers {
36023615
val mods1 = addFlag(mods, Method)
36033616
val ident = termIdent()
36043617
var name = ident.name.asTermName
3605-
val paramss =
3606-
if in.featureEnabled(Feature.clauseInterleaving) then
3618+
val paramss =
3619+
if in.featureEnabled(Feature.clauseInterleaving) then
36073620
// If you are making interleaving stable manually, please refer to the PR introducing it instead, section "How to make non-experimental"
36083621
typeOrTermParamClauses(ParamOwner.Def, numLeadParams = numLeadParams)
36093622
else
@@ -3613,7 +3626,7 @@ object Parsers {
36133626
joinParams(tparams, vparamss)
36143627

36153628
var tpt = fromWithinReturnType { typedOpt() }
3616-
3629+
36173630
if (migrateTo3) newLineOptWhenFollowedBy(LBRACE)
36183631
val rhs =
36193632
if in.token == EQUALS then

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,11 +197,11 @@ trait QuotesAndSplices {
197197
* )
198198
* ```
199199
*/
200-
private def splitQuotePattern(quoted: Tree)(using Context): (Map[Symbol, Bind], Tree, List[Tree]) = {
200+
private def splitQuotePattern(quoted: Tree)(using Context): (Map[Symbol, Tree], Tree, List[Tree]) = {
201201
val ctx0 = ctx
202202

203-
val typeBindings: collection.mutable.Map[Symbol, Bind] = collection.mutable.Map.empty
204-
def getBinding(sym: Symbol): Bind =
203+
val typeBindings: collection.mutable.Map[Symbol, Tree] = collection.mutable.Map.empty
204+
def getBinding(sym: Symbol): Tree =
205205
typeBindings.getOrElseUpdate(sym, {
206206
val bindingBounds = sym.info
207207
val bsym = newPatternBoundSymbol(sym.name.toString.stripPrefix("$").toTypeName, bindingBounds, quoted.span)
@@ -395,7 +395,10 @@ trait QuotesAndSplices {
395395
val quoted0 = desugar.quotedPattern(quoted, untpd.TypedSplice(TypeTree(quotedPt)))
396396
val quoteCtx = quoteContext.addMode(Mode.QuotedPattern).retractMode(Mode.Pattern)
397397
val quoted1 =
398-
if quoted.isType then typedType(quoted0, WildcardType)(using quoteCtx)
398+
if quoted.isType then typedType(quoted0, WildcardType)(using quoteCtx) match
399+
case quoted1 @ Block(stats, Typed(tpt, _)) => cpy.Block(quoted1)(stats, tpt)
400+
case quoted1 => quoted1
401+
399402
else typedExpr(quoted0, WildcardType)(using quoteCtx)
400403

401404
val (typeBindings, shape, splices) = splitQuotePattern(quoted1)

docs/_docs/reference/syntax.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ ColonArgument ::= colon [LambdaStart]
274274
LambdaStart ::= FunParams (‘=>’ | ‘?=>’)
275275
| HkTypeParamClause ‘=>’
276276
Quoted ::= ‘'’ ‘{’ Block ‘}’
277-
| ‘'’ ‘[’ Type ‘]’
277+
| ‘'’ ‘[’ TypeBlock ‘]’
278278
ExprSplice ::= spliceId -- if inside quoted block
279279
| ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern
280280
| ‘$’ ‘{’ Pattern ‘}’ -- when inside quoted pattern
@@ -293,6 +293,8 @@ BlockStat ::= Import
293293
| Extension
294294
| Expr1
295295
| EndMarker
296+
TypeBlock ::= {TypeBlockStat semi} Type
297+
TypeBlockStat ::= ‘type’ {nl} TypeDcl
296298
297299
ForExpr ::= ‘for’ ‘(’ Enumerators0 ‘)’ {nl} [‘do‘ | ‘yield’] Expr
298300
| ‘for’ ‘{’ Enumerators0 ‘}’ {nl} [‘do‘ | ‘yield’] Expr
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import scala.quoted._
2+
3+
case class T(t: Type[_])
4+
5+
object T {
6+
def impl[T <: AnyKind](using tt: Type[T])(using Quotes): Expr[Unit] = {
7+
val t = T(tt)
8+
t.t match
9+
case '[type x; `x`] =>
10+
assert(Type.show[x] == "scala.Int", Type.show[x])
11+
case '[type f[X]; `f`] =>
12+
assert(Type.show[f] == "[A >: scala.Nothing <: scala.Any] => scala.collection.immutable.List[A]", Type.show[f])
13+
case '[type f <: AnyKind; `f`] =>
14+
assert(Type.show[f] == "[K >: scala.Nothing <: scala.Any, V >: scala.Nothing <: scala.Any] => scala.collection.immutable.Map[K, V]", Type.show[f])
15+
'{}
16+
}
17+
18+
inline def run[T <: AnyKind] = ${ impl[T] }
19+
}

tests/pos-macros/i10864a/Test_2.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@main
2+
def run =
3+
T.run[Int]
4+
T.run[List]
5+
T.run[Map]

tests/pos-macros/i7264.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ class Foo {
33
def f[T2](t: Type[T2])(using Quotes) = t match {
44
case '[ *:[Int, t2] ] =>
55
Type.of[ *:[Int, t2] ]
6+
case '[ type t <: Tuple; *:[`t`, `t`] ] =>
67
}
78
}

0 commit comments

Comments
 (0)