Skip to content

Commit 5060aba

Browse files
committed
Allow for type id in type patterns
Have `type T` as an alternate for lower-case `t` identifiers as variable binders in type patterns. Over time, we will deprecate the `t` form.
1 parent 945d2a8 commit 5060aba

File tree

5 files changed

+116
-45
lines changed

5 files changed

+116
-45
lines changed

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

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,25 @@ object desugar {
155155
ValDef(epname, tpt, EmptyTree).withFlags(paramFlags | Implicit)
156156
}
157157

158+
private def desugarTypeBindings(
159+
bindings: List[TypeDef],
160+
forPrimaryConstructor: Boolean = false)(implicit ctx: Context): (List[TypeDef], List[ValDef]) = {
161+
val epbuf = new ListBuffer[ValDef]
162+
def desugarContextBounds(rhs: Tree): Tree = rhs match {
163+
case ContextBounds(tbounds, cxbounds) =>
164+
epbuf ++= makeImplicitParameters(cxbounds, forPrimaryConstructor)
165+
tbounds
166+
case LambdaTypeTree(tparams, body) =>
167+
cpy.LambdaTypeTree(rhs)(tparams, desugarContextBounds(body))
168+
case _ =>
169+
rhs
170+
}
171+
val bindings1 = bindings mapConserve { tparam =>
172+
cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs))
173+
}
174+
(bindings1, epbuf.toList)
175+
}
176+
158177
/** Expand context bounds to evidence params. E.g.,
159178
*
160179
* def f[T >: L <: H : B](params)
@@ -172,21 +191,8 @@ object desugar {
172191
private def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false)(implicit ctx: Context): Tree = {
173192
val DefDef(name, tparams, vparamss, tpt, rhs) = meth
174193
val mods = meth.mods
175-
val epbuf = new ListBuffer[ValDef]
176-
def desugarContextBounds(rhs: Tree): Tree = rhs match {
177-
case ContextBounds(tbounds, cxbounds) =>
178-
epbuf ++= makeImplicitParameters(cxbounds, isPrimaryConstructor)
179-
tbounds
180-
case LambdaTypeTree(tparams, body) =>
181-
cpy.LambdaTypeTree(rhs)(tparams, desugarContextBounds(body))
182-
case _ =>
183-
rhs
184-
}
185-
val tparams1 = tparams mapConserve { tparam =>
186-
cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs))
187-
}
188-
189-
val meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), epbuf.toList)
194+
val (tparams1, evidenceParams) = desugarTypeBindings(tparams, isPrimaryConstructor)
195+
val meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), evidenceParams)
190196

191197
/** The longest prefix of parameter lists in vparamss whose total length does not exceed `n` */
192198
def takeUpTo(vparamss: List[List[ValDef]], n: Int): List[List[ValDef]] = vparamss match {
@@ -744,6 +750,20 @@ object desugar {
744750
}
745751
}
746752

753+
def decomposeTypePattern(tree: Tree)(implicit ctx: Context): (Tree, List[TypeDef]) = {
754+
val bindingsBuf = new ListBuffer[TypeDef]
755+
val elimTypeDefs = new untpd.TreeMap {
756+
override def transform(tree: Tree)(implicit ctx: Context) = tree match {
757+
case tree: TypeDef =>
758+
bindingsBuf += tree
759+
Ident(tree.name).withPos(tree.pos)
760+
case _ =>
761+
super.transform(tree)
762+
}
763+
}
764+
(elimTypeDefs.transform(tree), bindingsBuf.toList)
765+
}
766+
747767
/** Expand variable identifier x to x @ _ */
748768
def patternVar(tree: Tree)(implicit ctx: Context) = {
749769
val Ident(name) = tree

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

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,20 @@ object Parsers {
323323
finally inEnum = saved
324324
}
325325

326+
private[this] var inTypePattern = false
327+
private[this] var inBindingTypePattern = false
328+
private def withinTypePattern[T](binding: Boolean)(body: => T): T = {
329+
val savedInType = inTypePattern
330+
val savedInBinding = inBindingTypePattern
331+
inTypePattern = true
332+
if (binding) inBindingTypePattern = true
333+
try body
334+
finally {
335+
inTypePattern = savedInType
336+
inBindingTypePattern = savedInBinding
337+
}
338+
}
339+
326340
def migrationWarningOrError(msg: String, offset: Int = in.offset) =
327341
if (in.isScala2Mode)
328342
ctx.migrationWarning(msg, source atPos Position(offset))
@@ -917,7 +931,16 @@ object Parsers {
917931
else Nil
918932
first :: rest
919933
}
920-
def typParser() = if (wildOK) typ() else toplevelTyp()
934+
def typParser() =
935+
if (in.token == TYPE && inTypePattern)
936+
if (inBindingTypePattern)
937+
typeParamCore(in.skipToken(), isConcreteOwner = true)
938+
else
939+
atPos(in.skipToken(), nameStart) {
940+
Bind(ident().toTypeName, Ident(nme.WILDCARD))
941+
}
942+
else if (wildOK) typ()
943+
else toplevelTyp()
921944
if (namedOK && in.token == IDENTIFIER)
922945
typParser() match {
923946
case Ident(name) if in.token == EQUALS =>
@@ -998,7 +1021,7 @@ object Parsers {
9981021

9991022
def typeDependingOn(location: Location.Value): Tree =
10001023
if (location == Location.InParens) typ()
1001-
else if (location == Location.InPattern) refinedType()
1024+
else if (location == Location.InPattern) withinTypePattern(binding = false)(refinedType())
10021025
else infixType()
10031026

10041027
/** Checks whether `t` is a wildcard type.
@@ -1058,7 +1081,7 @@ object Parsers {
10581081
* | PostfixExpr `match' `{' CaseClauses `}'
10591082
* Bindings ::= `(' [Binding {`,' Binding}] `)'
10601083
* Binding ::= (id | `_') [`:' Type]
1061-
* Ascription ::= `:' CompoundType
1084+
* Ascription ::= `:' InfixType
10621085
* | `:' Annotation {Annotation}
10631086
* | `:' `_' `*'
10641087
*/
@@ -1178,6 +1201,13 @@ object Parsers {
11781201
t
11791202
}
11801203

1204+
/** Ascription ::= `:' InfixType
1205+
* | `:' Annotation {Annotation}
1206+
* | `:' `_' `*'
1207+
* PatternAscription ::= `:' TypePattern
1208+
* | `:' `_' `*'
1209+
* TypePattern ::= RefinedType
1210+
*/
11811211
def ascription(t: Tree, location: Location.Value) = atPos(startOffset(t)) {
11821212
in.skipToken()
11831213
in.token match {
@@ -1539,7 +1569,7 @@ object Parsers {
15391569
if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1() :: patternAlts() }
15401570
else Nil
15411571

1542-
/** Pattern1 ::= PatVar Ascription
1572+
/** Pattern1 ::= PatVar PatternAscription
15431573
* | Pattern2
15441574
*/
15451575
def pattern1(): Tree = {
@@ -1774,11 +1804,11 @@ object Parsers {
17741804
/* -------- PARAMETERS ------------------------------------------- */
17751805

17761806
/** ClsTypeParamClause::= `[' ClsTypeParam {`,' ClsTypeParam} `]'
1777-
* ClsTypeParam ::= {Annotation} [`+' | `-']
1778-
* id [HkTypeParamClause] TypeParamBounds
1807+
* ClsTypeParam ::= {Annotation} [`+' | `-'] TypeParamCore
17791808
*
17801809
* DefTypeParamClause::= `[' DefTypeParam {`,' DefTypeParam} `]'
1781-
* DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds
1810+
* DefTypeParam ::= {Annotation} TypeParamCore
1811+
* TypeParamCore ::= id [HkTypeParamClause] TypeParamBounds
17821812
*
17831813
* TypTypeParamCaluse::= `[' TypTypeParam {`,' TypTypeParam} `]'
17841814
* TypTypeParam ::= {Annotation} id [HkTypePamClause] TypeBounds
@@ -1802,23 +1832,26 @@ object Parsers {
18021832
else EmptyFlags
18031833
}
18041834
}
1805-
atPos(start, nameStart) {
1806-
val name =
1807-
if (isConcreteOwner || in.token != USCORE) ident().toTypeName
1808-
else {
1809-
in.nextToken()
1810-
WildcardParamName.fresh().toTypeName
1811-
}
1812-
val hkparams = typeParamClauseOpt(ParamOwner.TypeParam)
1813-
val bounds =
1814-
if (isConcreteOwner) typeParamBounds(name)
1815-
else typeBounds()
1816-
TypeDef(name, lambdaAbstract(hkparams, bounds)).withMods(mods)
1817-
}
1835+
typeParamCore(start, isConcreteOwner).withMods(mods)
18181836
}
18191837
commaSeparated(() => typeParam())
18201838
}
18211839

1840+
/** TypeParamCore ::= id [HkTypeParamClause] TypeParamBounds */
1841+
def typeParamCore(start: Offset, isConcreteOwner: Boolean): TypeDef = atPos(start, nameStart) {
1842+
val name =
1843+
if (in.token == USCORE && !isConcreteOwner) {
1844+
in.nextToken()
1845+
WildcardParamName.fresh().toTypeName
1846+
}
1847+
else ident().toTypeName
1848+
val hkparams = typeParamClauseOpt(ParamOwner.TypeParam)
1849+
val bounds =
1850+
if (isConcreteOwner) typeParamBounds(name)
1851+
else typeBounds()
1852+
TypeDef(name, lambdaAbstract(hkparams, bounds))
1853+
}
1854+
18221855
def typeParamClauseOpt(ownerKind: ParamOwner.Value): List[TypeDef] =
18231856
if (in.token == LBRACKET) typeParamClause(ownerKind) else Nil
18241857

docs/docs/internals/syntax.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ ClassQualifier ::= ‘[’ id ‘]’
120120
Type ::= [FunArgMods] FunArgTypes ‘=>’ Type Function(ts, t)
121121
| HkTypeParamClause ‘=>’ Type TypeLambda(ps, t)
122122
| InfixType
123+
| ‘type’ TypeParamCore
124+
(if inside a BindingTypePattern)
123125
FunArgTypes ::= InfixType
124126
| ‘(’ [ FunArgType {‘,’ FunArgType } ] ‘)’
125127
| '(' TypedFunParam {',' TypedFunParam } ')'
@@ -137,8 +139,10 @@ SimpleType ::= SimpleType TypeArgs
137139
| ‘_’ TypeBounds
138140
| Refinement RefinedTypeTree(EmptyTree, refinement)
139141
| SimpleLiteral SingletonTypeTree(l)
140-
ArgTypes ::= Type {‘,’ Type}
141-
| NamedTypeArg {‘,’ NamedTypeArg}
142+
ArgTypes ::= ArgType {‘,’ ArgType}
143+
ArgType ::= Type
144+
| ‘type’ id
145+
(if inside a TypePattern)
142146
FunArgType ::= Type
143147
| ‘=>’ Type PrefixOp(=>, t)
144148
ParamType ::= [‘=>’] ParamValueType
@@ -225,7 +229,7 @@ CaseClauses ::= CaseClause { CaseClause }
225229
CaseClause ::= ‘case’ (Pattern [Guard] ‘=>’ Block | INT) CaseDef(pat, guard?, block) // block starts at =>
226230
227231
Pattern ::= Pattern1 { ‘|’ Pattern1 } Alternative(pats)
228-
Pattern1 ::= PatVar ‘:’ RefinedType Bind(name, Typed(Ident(wildcard), tpe))
232+
Pattern1 ::= PatVar ‘:’ TypePattern Bind(name, Typed(Ident(wildcard), tpe))
229233
| Pattern2
230234
Pattern2 ::= [id ‘@’] InfixPattern Bind(name, pat)
231235
InfixPattern ::= SimplePattern { id [nl] SimplePattern } InfixOp(pat, op, pat)
@@ -238,22 +242,23 @@ SimplePattern1 ::= Path
238242
| SimplePattern1 ‘.’ id
239243
PatVar ::= varid
240244
| ‘_’
245+
BindingTypePattern::= Type
246+
TypePattern ::= RefinedType
241247
Patterns ::= Pattern {‘,’ Pattern}
242248
ArgumentPatterns ::= ‘(’ [Patterns] ‘)’ Apply(fn, pats)
243249
| ‘(’ [Patterns ‘,’] Pattern2 ‘:’ ‘_’ ‘*’ ‘)’
244250
245-
Augmentation ::= ‘augment’ (id | [id] ClsTypeParamClause)
251+
Augmentation ::= ‘augment’ BindingTypePattern
246252
[[nl] ImplicitParamClause] TemplateClause Augment(name, templ)
247253
```
248254

249255
### Type and Value Parameters
250256
```ebnf
251257
ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’
252-
ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] TypeDef(Modifiers, name, tparams, bounds)
253-
id [HkTypeParamClause] TypeParamBounds Bound(below, above, context)
254-
258+
ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] TypeParamCore TypeDef(Modifiers, name, tparams, bounds)
255259
DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’
256-
DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds
260+
DefTypeParam ::= {Annotation} TypeParamCore
261+
TypeParamCore ::= id [HkTypeParamClause] TypeParamBounds Bound(below, above, context)
257262
258263
TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’
259264
TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds

tests/neg/i2494.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
enum
2-
object // error // error // error
1+
enum // error: cyclic reference
2+
object // error // error

tests/pos/typepats.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
object Test {
2+
val xs: Any = List(1, 2, 3)
3+
xs match {
4+
case xs: List[type T] =>
5+
}
6+
trait I[T]
7+
class C[T] extends I[T]
8+
val y: I[Int] = new C[Int]
9+
y match {
10+
case _: C[type T] =>
11+
val x: T = 3
12+
}
13+
}

0 commit comments

Comments
 (0)