Skip to content

Commit 76112f3

Browse files
committed
Support for cap parameter lists
1 parent cc8079a commit 76112f3

File tree

2 files changed

+81
-4
lines changed

2 files changed

+81
-4
lines changed

Diff for: 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)
@@ -2432,7 +2433,7 @@ object Parsers {
24322433
closure(start, location, modifiers(BitSet(IMPLICIT)))
24332434
case LBRACKET =>
24342435
val start = in.offset
2435-
val tparams = typeParamClause(ParamOwner.Type)
2436+
val tparams = if isCapKwNext then capParamClause(ParamOwner.Type) else typeParamClause(ParamOwner.Type) // TODO document grammar
24362437
val arrowOffset = accept(ARROW)
24372438
val body = expr(location)
24382439
makePolyFunction(tparams, body, "literal", errorTermTree(arrowOffset), start, arrowOffset)
@@ -3467,6 +3468,7 @@ object Parsers {
34673468
* DefParamClause ::= DefTypeParamClause
34683469
* | DefTermParamClause
34693470
* | UsingParamClause
3471+
* | CapParamClause -- under capture checking
34703472
*/
34713473
def typeOrTermParamClauses(
34723474
paramOwner: ParamOwner, numLeadParams: Int = 0): List[List[TypeDef] | List[ValDef]] =
@@ -3484,16 +3486,54 @@ object Parsers {
34843486
else if in.token == LBRACKET then
34853487
if prevIsTypeClause then
34863488
syntaxError(
3487-
em"Type parameter lists must be separated by a term or using parameter list",
3489+
em"Type parameter lists must be separated by a term or using parameter list", //TODO adapt for capture params
34883490
in.offset
34893491
)
3490-
typeParamClause(paramOwner) :: recur(numLeadParams, firstClause, prevIsTypeClause = true)
3492+
if isCapKwNext then // TODO refine
3493+
capParamClause(paramOwner) :: recur(numLeadParams, firstClause, prevIsTypeClause = true)
3494+
else
3495+
typeParamClause(paramOwner) :: recur(numLeadParams, firstClause, prevIsTypeClause = true)
34913496
else Nil
34923497
end recur
34933498

34943499
recur(numLeadParams, firstClause = true, prevIsTypeClause = false)
34953500
end typeOrTermParamClauses
34963501

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

34983538
/** ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’
34993539
* ClsTypeParam ::= {Annotation} [‘+’ | ‘-’]
@@ -3546,7 +3586,11 @@ object Parsers {
35463586
}
35473587

35483588
def typeParamClauseOpt(paramOwner: ParamOwner): List[TypeDef] =
3549-
if (in.token == LBRACKET) typeParamClause(paramOwner) else Nil
3589+
if (in.token == LBRACKET)
3590+
if isCapKwNext then capParamClause(paramOwner) // TODO grammar doc
3591+
else typeParamClause(paramOwner)
3592+
else
3593+
Nil
35503594

35513595
/** ContextTypes ::= FunArgType {‘,’ FunArgType}
35523596
*/

Diff for: tests/pos-custom-args/captures/cap-paramlists.scala

+33
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)