Skip to content

Commit bbac245

Browse files
committed
Support for named cap arguments
1 parent 781656a commit bbac245

File tree

3 files changed

+53
-38
lines changed

3 files changed

+53
-38
lines changed

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

+46-33
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,14 @@ object Parsers {
229229
if Feature.ccEnabled then
230230
val lookahead = in.LookaheadScanner()
231231
lookahead.nextToken()
232-
val res = lookahead.isIdent(nme.cap) && lookahead.lookahead.token == TYPE
233-
res
232+
lookahead.isIdent(nme.cap) && lookahead.lookahead.token == TYPE
233+
else false
234+
}
235+
def isCapAssignmentNext = {
236+
if isCapKw then
237+
val lookahead = in.LookaheadScanner()
238+
lookahead.nextToken()
239+
lookahead.isIdent && lookahead.lookahead.token == EQUALS
234240
else false
235241
}
236242
def isSimpleLiteral =
@@ -1614,7 +1620,7 @@ object Parsers {
16141620
case _ => None
16151621
}
16161622

1617-
/** CaptureRef ::= { SimpleRef `.` } SimpleRef [`*`]
1623+
/** CaptureRef ::= { SimpleRef `.` } SimpleRef [`*`] [`.` rd]
16181624
* | [ { SimpleRef `.` } SimpleRef `.` ] id
16191625
*/
16201626
def captureRef(): Tree =
@@ -1894,7 +1900,7 @@ object Parsers {
18941900
if in.token == LPAREN then funParamClause() :: funParamClauses() else Nil
18951901

18961902
/** InfixType ::= RefinedType {id [nl] RefinedType}
1897-
* | RefinedType `^` // under capture checking
1903+
* | RefinedType `^` -- under captureChecking
18981904
*/
18991905
def infixType(inContextBound: Boolean = false): Tree = infixTypeRest(inContextBound)(refinedType())
19001906

@@ -2182,44 +2188,47 @@ object Parsers {
21822188
atSpan(startOffset(t), startOffset(id)) { Select(t, id.name) }
21832189
}
21842190

2185-
/** ArgTypes ::= Type {`,' Type}
2186-
* | NamedTypeArg {`,' NamedTypeArg}
2187-
* NamedTypeArg ::= id `=' Type
2191+
/** ArgTypes ::= TypeArg {‘,’ TypeArg}
2192+
* | NamedTypeArg {‘,’ NamedTypeArg}
2193+
* TypeArg ::= Type
2194+
* | CaptureSet -- under captureChecking
2195+
* NamedTypeArg ::= id ‘=’ Type
2196+
* | ‘cap’ id ‘=’ CaptureSet -- under captureChecking
21882197
* NamesAndTypes ::= NameAndType {‘,’ NameAndType}
2189-
* NameAndType ::= id ':' Type
2198+
* NameAndType ::= id ‘:’ Type
21902199
*/
2191-
def argTypes(namedOK: Boolean, wildOK: Boolean, tupleOK: Boolean): List[Tree] = //TODO grammar doc
2200+
def argTypes(namedOK: Boolean, wildOK: Boolean, tupleOK: Boolean): List[Tree] =
21922201
inline def wildCardCheck(inline gen: Tree): Tree =
21932202
val t = gen
21942203
if wildOK then t else rejectWildcardType(t)
21952204

2196-
def argType() = wildCardCheck:
2197-
typ()
2205+
def argType() = wildCardCheck(typ())
21982206

2199-
def argOrCapType() = wildCardCheck:
2207+
def typeArg() = wildCardCheck:
22002208
if Feature.ccEnabled && in.token == LBRACE && !isDclIntroNext && !isCapTypeKwNext then // is this a capture set and not a refinement type?
22012209
// This case is ambiguous w.r.t. an Object literal {}. But since CC is enabled, we probably expect it to designate the empty set
22022210
concreteCapsType(captureSet())
22032211
else typ()
22042212

2205-
def namedArgType() =
2213+
def namedTypeArg() =
22062214
atSpan(in.offset):
2215+
val isCap = if isCapKw then { in.nextToken(); true } else false
22072216
val name = ident()
22082217
accept(EQUALS)
2209-
NamedArg(name.toTypeName, argType())
2218+
NamedArg(name.toTypeName, if isCap then concreteCapsType(captureSet()) else argType())
22102219

2211-
def namedElem() =
2220+
def nameAndType() =
22122221
atSpan(in.offset):
22132222
val name = ident()
22142223
acceptColon()
2215-
NamedArg(name, argType()) // TODO allow capsets here?
2224+
NamedArg(name, argType())
22162225

2217-
if namedOK && isIdent && in.lookahead.token == EQUALS then // TOOD support for named cap args
2218-
commaSeparated(() => namedArgType())
2226+
if namedOK && (isIdent && in.lookahead.token == EQUALS || isCapAssignmentNext) then
2227+
commaSeparated(() => namedTypeArg())
22192228
else if tupleOK && isIdent && in.lookahead.isColon && sourceVersion.enablesNamedTuples then
2220-
commaSeparated(() => namedElem())
2229+
commaSeparated(() => nameAndType())
22212230
else
2222-
commaSeparated(() => argOrCapType())
2231+
commaSeparated(() => typeArg())
22232232
end argTypes
22242233

22252234
def paramTypeOf(core: () => Tree): Tree =
@@ -3439,7 +3448,7 @@ object Parsers {
34393448
* | opaque
34403449
* LocalModifier ::= abstract | final | sealed | open | implicit | lazy | erased |
34413450
* inline | transparent | infix |
3442-
* mut | cap -- under cc
3451+
* mut | cap -- under captureChecking
34433452
*/
34443453
def modifiers(allowed: BitSet = modifierTokens, start: Modifiers = Modifiers()): Modifiers = {
34453454
@tailrec
@@ -3529,20 +3538,24 @@ object Parsers {
35293538
end typeOrTermParamClauses
35303539

35313540
/** ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’
3532-
* ClsTypeParam ::= {Annotation} [‘+’ | ‘-’]
3533-
* id [HkTypeParamClause] TypeAndCtxBounds
3541+
* ClsTypeParam ::= {Annotation} [‘+’ | ‘-’]
3542+
* id [HkTypeParamClause] TypeAndCtxBounds
3543+
* | {Annotation} ‘cap’ id CaptureSetAndCtxBounds -- under captureChecking
35343544
*
35353545
* DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’
3536-
* DefTypeParam ::= {Annotation}
3537-
* id [HkTypeParamClause] TypeAndCtxBounds
3546+
* DefTypeParam ::= {Annotation}
3547+
* id [HkTypeParamClause] TypeAndCtxBounds
3548+
* | {Annotation} ‘cap’ id CaptureSetAndCtxBounds -- under captureChecking
35383549
*
35393550
* TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’
3540-
* TypTypeParam ::= {Annotation}
3541-
* (id | ‘_’) [HkTypeParamClause] TypeAndCtxBounds
3551+
* TypTypeParam ::= {Annotation}
3552+
* (id | ‘_’) [HkTypeParamClause] TypeAndCtxBounds
3553+
* | {Annotation} ‘cap’ (id | ‘_’) CaptureSetAndCtxBounds -- under captureChecking
35423554
*
35433555
* HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} ‘]’
3544-
* HkTypeParam ::= {Annotation} [‘+’ | ‘-’]
3545-
* (id | ‘_’) [HkTypePamClause] TypeBounds
3556+
* HkTypeParam ::= {Annotation} [‘+’ | ‘-’]
3557+
* (id | ‘_’) [HkTypePamClause] TypeBounds
3558+
* | {Annotation} ‘cap’ (id | ‘_’) CaptureSetBounds -- under captureChecking
35463559
*/
35473560
def typeParamClause(paramOwner: ParamOwner): List[TypeDef] = inBracketsWithCommas {
35483561

@@ -3571,7 +3584,7 @@ object Parsers {
35713584
capParam(start, mods)
35723585
else typeParam(start, mods)
35733586

3574-
def capParam(startOffset: Int, mods0: Modifiers): TypeDef = { // TODO grammar doc
3587+
def capParam(startOffset: Int, mods0: Modifiers): TypeDef = {
35753588
val start = startOffset
35763589
var mods = mods0
35773590
if paramOwner.isClass then
@@ -3964,7 +3977,7 @@ object Parsers {
39643977
* | var VarDef
39653978
* | def DefDef
39663979
* | type {nl} TypeDef
3967-
* | cap type {nl} CapDef -- under capture checking
3980+
* | cap type {nl} CapDef -- under captureChecking
39683981
* | TmplDef
39693982
* EnumCase ::= `case' (id ClassConstr [`extends' ConstrApps]] | ids)
39703983
*/
@@ -4198,7 +4211,7 @@ object Parsers {
41984211
private def concreteCapsType(refs: List[Tree]): Tree =
41994212
makeRetaining(Select(scalaDot(nme.caps), tpnme.CapSet), refs, tpnme.retains)
42004213

4201-
/** CapDef ::= id CaptureSetAndCtxBounds [‘=’ CaptureSetOrRef] -- under capture checking
4214+
/** CapDef ::= id CaptureSetAndCtxBounds [‘=’ CaptureSetOrRef] -- under captureChecking
42024215
*/
42034216
def capDefOrDcl(start: Offset, mods: Modifiers): Tree =
42044217
newLinesOpt()
@@ -4825,7 +4838,7 @@ object Parsers {
48254838
* | ‘var’ VarDef
48264839
* | ‘def’ DefDef
48274840
* | ‘type’ {nl} TypeDef
4828-
* | ‘cap’ ‘type’ {nl} CapDef -- under capture checking
4841+
* | ‘cap’ ‘type’ {nl} CapDef -- under captureChecking
48294842
* (in reality we admit class defs and vars and filter them out afterwards in `checkLegal`)
48304843
*/
48314844
def refineStatSeq(): List[Tree] = {

tests/neg-custom-args/captures/capture-vars-subtyping.scala

-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ def test[cap C] =
77
val c: C = b
88
val d: CapSet^{C, c} = a
99

10-
// TODO: make "CapSet-ness" of type variables somehow contagious?
11-
// Then we don't have to spell out the bounds explicitly...
1210
def testTrans[cap C, cap D <: {C}, cap E <: {D}, cap F >: {C}] =
1311
val d1: D = ???
1412
val d2: CapSet^{D} = d1

tests/pos-custom-args/captures/cap-paramlists5.scala

+7-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ import language.experimental.namedTypeArguments
44
def test2 =
55
val x: Any^ = ???
66
def foo[cap A, cap B >: {A}, T, U](x: Int) = 1
7-
//foo[{x}, {x}, Int](0) // TODO will not work for {x} in first arg
7+
foo[{x}, {x}, Int, String](0)
88
foo[{}, {}, { def bar: Int }, { cap type D = {x} }](0)
99
trait Foo { cap type D }
1010
foo[{}, {}, Foo, Foo](0)
11-
// foo[cap A = {x}, cap B = {x}](0)
12-
// foo[cap A = {x}](0)
11+
foo[cap A = {x}, cap B = {x}](0)
12+
foo[cap A = {x}](0)
13+
foo[T = Int](0)
14+
foo[T = Int, cap A = {x}](1)
15+
foo[cap A = {x}, T = Int](1)
16+
foo[cap B = {}, U = String, cap A = {}](1)

0 commit comments

Comments
 (0)