Skip to content

Commit 887799e

Browse files
committed
Support for cap parameter lists
1 parent 76f01d3 commit 887799e

File tree

2 files changed

+81
-4
lines changed

2 files changed

+81
-4
lines changed

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

+48-4
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ object Parsers {
224224
def isErasedKw = isErased && in.isSoftModifierInParamModifierPosition
225225
// Are we seeing a `cap` soft keyword for declaring a capture-set member or at the beginning a capture-variable parameter list?
226226
def isCapKw = Feature.ccEnabled && isIdent(nme.cap)
227+
def isCapKwNext = Feature.ccEnabled && in.lookahead.isIdent(nme.cap)
227228
def isSimpleLiteral =
228229
simpleLiteralTokens.contains(in.token)
229230
|| isIdent(nme.raw.MINUS) && numericLitTokens.contains(in.lookahead.token)
@@ -2439,7 +2440,7 @@ object Parsers {
24392440
closure(start, location, modifiers(BitSet(IMPLICIT)))
24402441
case LBRACKET =>
24412442
val start = in.offset
2442-
val tparams = typeParamClause(ParamOwner.Type)
2443+
val tparams = if isCapKwNext then capParamClause(ParamOwner.Type) else typeParamClause(ParamOwner.Type) // TODO document grammar
24432444
val arrowOffset = accept(ARROW)
24442445
val body = expr(location)
24452446
makePolyFunction(tparams, body, "literal", errorTermTree(arrowOffset), start, arrowOffset)
@@ -3474,6 +3475,7 @@ object Parsers {
34743475
* DefParamClause ::= DefTypeParamClause
34753476
* | DefTermParamClause
34763477
* | UsingParamClause
3478+
* | CapParamClause -- under capture checking
34773479
*/
34783480
def typeOrTermParamClauses(
34793481
paramOwner: ParamOwner, numLeadParams: Int = 0): List[List[TypeDef] | List[ValDef]] =
@@ -3491,16 +3493,54 @@ object Parsers {
34913493
else if in.token == LBRACKET then
34923494
if prevIsTypeClause then
34933495
syntaxError(
3494-
em"Type parameter lists must be separated by a term or using parameter list",
3496+
em"Type parameter lists must be separated by a term or using parameter list", //TODO adapt for capture params
34953497
in.offset
34963498
)
3497-
typeParamClause(paramOwner) :: recur(numLeadParams, firstClause, prevIsTypeClause = true)
3499+
if isCapKwNext then // TODO refine
3500+
capParamClause(paramOwner) :: recur(numLeadParams, firstClause, prevIsTypeClause = true)
3501+
else
3502+
typeParamClause(paramOwner) :: recur(numLeadParams, firstClause, prevIsTypeClause = true)
34983503
else Nil
34993504
end recur
35003505

35013506
recur(numLeadParams, firstClause = true, prevIsTypeClause = false)
35023507
end typeOrTermParamClauses
35033508

3509+
def capParamClause(paramOwner: ParamOwner): List[TypeDef] = inBracketsWithCommas {
3510+
3511+
def ensureNoVariance() =
3512+
if isIdent(nme.raw.PLUS) || isIdent(nme.raw.MINUS) then
3513+
syntaxError(em"no `+/-` variance annotation allowed here")
3514+
in.nextToken()
3515+
3516+
def ensureNoHKParams() =
3517+
if in.token == LBRACKET then
3518+
syntaxError(em"'cap' parameters cannot have type parameters")
3519+
in.nextToken()
3520+
3521+
def captureParam(): TypeDef = {
3522+
val start = in.offset
3523+
var mods = annotsAsMods() | Param
3524+
if paramOwner.isClass then
3525+
mods |= PrivateLocal
3526+
ensureNoVariance() // TODO: in the future, we might want to support variances on capture params, ruled out for now
3527+
atSpan(start, nameStart) {
3528+
val name =
3529+
if paramOwner.acceptsWildcard && in.token == USCORE then
3530+
in.nextToken()
3531+
WildcardParamName.fresh().toTypeName
3532+
else ident().toTypeName
3533+
ensureNoHKParams()
3534+
val bounds =
3535+
if paramOwner.acceptsCtxBounds then captureSetAndCtxBounds(name) // TODO: do we need a new attribute for paramOwner?
3536+
else if sourceVersion.enablesNewGivens && paramOwner == ParamOwner.Type then captureSetAndCtxBounds(name)
3537+
else captureSetBounds()
3538+
TypeDef(name, bounds).withMods(mods)
3539+
}
3540+
}
3541+
in.nextToken() // assumes we are just after the opening bracket at the 'cap' soft keyword
3542+
commaSeparated(() => captureParam())
3543+
}
35043544

35053545
/** ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’
35063546
* ClsTypeParam ::= {Annotation} [‘+’ | ‘-’]
@@ -3553,7 +3593,11 @@ object Parsers {
35533593
}
35543594

35553595
def typeParamClauseOpt(paramOwner: ParamOwner): List[TypeDef] =
3556-
if (in.token == LBRACKET) typeParamClause(paramOwner) else Nil
3596+
if (in.token == LBRACKET)
3597+
if isCapKwNext then capParamClause(paramOwner) // TODO grammar doc
3598+
else typeParamClause(paramOwner)
3599+
else
3600+
Nil
35573601

35583602
/** ContextTypes ::= FunArgType {‘,’ FunArgType}
35593603
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import language.experimental.captureChecking
2+
3+
trait Ctx[T]
4+
5+
def test =
6+
val x: Any^ = ???
7+
val y: Any^ = ???
8+
object O:
9+
val z: Any^ = ???
10+
val bar = [cap C, D <: C, E <: {C,x}, F >: {x,y} <: {C,E}] => (x: Int) => 1
11+
def foo[cap A >: y <: x,
12+
B,
13+
C <: {x},
14+
D : Ctx,
15+
E <: C,
16+
F <: {C},
17+
G <: {x, y},
18+
H >: {x} <: {x,y} : Ctx]()[cap I <: {y, G, H},
19+
J <: O.z,
20+
K <: {x, O.z},
21+
L <: {x, y, O.z},
22+
M >: {x, y, O.z} <: C : Ctx,
23+
N >: x <: x,
24+
O >: O.z <: O.z] = ???
25+
val baz = () => [cap C, D <: C, E <: {C,x}, F >: {x,y} <: {C,E} : Ctx] => (x: Int) => 1
26+
val baz2 = (i: Int) => [cap C, D <: C, E <: {C,x}, F >: {x,y} <: {C,E} : Ctx] => (x: Int) => 1
27+
val baz3 = (i: Int) => [cap C, D <: C, E <: {C,x}] => () => [cap F >: {x,y} <: {C,E} : Ctx] => (x: Int) => 1
28+
29+
30+
trait Foo[cap U,V,W]:
31+
cap C = caps.cap
32+
cap D = {caps.cap}
33+
cap E >: {V,W} <: U

0 commit comments

Comments
 (0)