Skip to content

Commit 6486ab8

Browse files
committed
More concise syntax for capture members
1 parent b6bc4b6 commit 6486ab8

File tree

4 files changed

+131
-24
lines changed

4 files changed

+131
-24
lines changed

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -528,12 +528,13 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
528528
TypeApply(Select(scalaDot(nme.caps), nme.capsOf), tp :: Nil)
529529

530530
// Capture set variable `[C^]` becomes: `[C >: CapSet <: CapSet^{cap}]`
531-
def makeCapsBound()(using Context): TypeBoundsTree =
532-
TypeBoundsTree(
533-
Select(scalaDot(nme.caps), tpnme.CapSet),
534-
makeRetaining(
535-
Select(scalaDot(nme.caps), tpnme.CapSet),
536-
Nil, tpnme.retainsCap))
531+
def makeCapsBound(refsL: List[Tree] = Nil, refsU: List[Tree] = Nil)(using Context): TypeBoundsTree =
532+
val lower = refsL match
533+
case Nil => Select(scalaDot(nme.caps), tpnme.CapSet)
534+
case refsL => makeRetaining(Select(scalaDot(nme.caps), tpnme.CapSet), refsL, tpnme.retains)
535+
val upper =
536+
makeRetaining(Select(scalaDot(nme.caps), tpnme.CapSet), refsU, if refsU.isEmpty then tpnme.retainsCap else tpnme.retains)
537+
TypeBoundsTree(lower, upper)
537538

538539
def makeConstructor(tparams: List[TypeDef], vparamss: List[List[ValDef]], rhs: Tree = EmptyTree)(using Context): DefDef =
539540
DefDef(nme.CONSTRUCTOR, joinParams(tparams, vparamss), TypeTree(), rhs)

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ object StdNames {
443443
val bytes: N = "bytes"
444444
val canEqual_ : N = "canEqual"
445445
val canEqualAny : N = "canEqualAny"
446+
val cap: N = "cap"
446447
val caps: N = "caps"
447448
val capsOf: N = "capsOf"
448449
val captureChecking: N = "captureChecking"

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

Lines changed: 100 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ import config.SourceVersion.*
3535
import config.SourceVersion
3636
import dotty.tools.dotc.config.MigrationVersion
3737
import dotty.tools.dotc.util.chaining.*
38+
import dotty.tools.dotc.config.Feature.ccEnabled
39+
import dotty.tools.dotc.core.Types.AndType.make
3840

3941
object Parsers {
4042

@@ -256,6 +258,7 @@ object Parsers {
256258
|| defIntroTokens.contains(in.token)
257259
|| allowedMods.contains(in.token)
258260
|| in.isSoftModifierInModifierPosition && !excludedSoftModifiers.contains(in.name)
261+
|| Feature.ccEnabled && isIdent(nme.cap) //TODO have it as proper keyword/token instead?
259262

260263
def isStatSep: Boolean = in.isStatSep
261264

@@ -1617,6 +1620,12 @@ object Parsers {
16171620
if in.token == RBRACE then Nil else commaSeparated(captureRef)
16181621
}
16191622

1623+
/** CaptureSetOrRef ::= `{` CaptureSet `}` | CaptureRef -- under captureChecking
1624+
*/
1625+
def captureSetOrRef(): List[Tree] =
1626+
if in.token == LBRACE then captureSet()
1627+
else List(captureRef())
1628+
16201629
def capturesAndResult(core: () => Tree): Tree =
16211630
if Feature.ccEnabled && in.token == LBRACE && in.offset == in.lastOffset
16221631
then CapturesAndResult(captureSet(), core())
@@ -1958,7 +1967,8 @@ object Parsers {
19581967

19591968
def typeBlockStats(): List[Tree] =
19601969
val tdefs = new ListBuffer[Tree]
1961-
while in.token == TYPE do tdefs += typeBlockStat()
1970+
while (in.token == TYPE) do
1971+
tdefs += typeBlockStat()
19621972
tdefs.toList
19631973

19641974
/** TypeBlockStat ::= ‘type’ {nl} TypeDef
@@ -2240,7 +2250,7 @@ object Parsers {
22402250
inBraces(refineStatSeq())
22412251

22422252
/** TypeBounds ::= [`>:' Type] [`<:' Type]
2243-
* | `^` -- under captureChecking
2253+
* | `^` -- under captureChecking TODO remove
22442254
*/
22452255
def typeBounds(): TypeBoundsTree =
22462256
atSpan(in.offset):
@@ -2254,6 +2264,19 @@ object Parsers {
22542264
if (in.token == tok) { in.nextToken(); toplevelTyp() }
22552265
else EmptyTree
22562266

2267+
private def capsType(refs: List[Tree], isLowerBound: Boolean = false): Tree =
2268+
if isLowerBound && refs.isEmpty then
2269+
Select(scalaDot(nme.caps), tpnme.CapSet)
2270+
else
2271+
makeRetaining(Select(scalaDot(nme.caps), tpnme.CapSet), refs, if refs.isEmpty then tpnme.retainsCap else tpnme.retains)
2272+
2273+
private def capsBound(tok: Int): Tree =
2274+
if (in.token == tok) then
2275+
in.nextToken()
2276+
capsType(captureSetOrRef(), isLowerBound = tok == SUPERTYPE)
2277+
else
2278+
capsType(Nil, isLowerBound = tok == SUPERTYPE)
2279+
22572280
/** TypeAndCtxBounds ::= TypeBounds [`:` ContextBounds]
22582281
*/
22592282
def typeAndCtxBounds(pname: TypeName): Tree = {
@@ -2263,6 +2286,15 @@ object Parsers {
22632286
else atSpan((t.span union cbs.head.span).start) { ContextBounds(t, cbs) }
22642287
}
22652288

2289+
/** TypeAndCtxBounds ::= TypeBounds [`:` ContextBounds] -- under captureChecking
2290+
*/
2291+
def captureSetAndCtxBounds(pname: TypeName): Tree = {
2292+
val t = TypeBoundsTree(capsBound(SUPERTYPE), capsBound(SUBTYPE))
2293+
val cbs = contextBounds(pname)
2294+
if (cbs.isEmpty) t
2295+
else atSpan((t.span union cbs.head.span).start) { ContextBounds(t, cbs) }
2296+
}
2297+
22662298
/** ContextBound ::= Type [`as` id] */
22672299
def contextBound(pname: TypeName): Tree =
22682300
val t = toplevelTyp(inContextBound = true)
@@ -3855,25 +3887,29 @@ object Parsers {
38553887
* | var VarDef
38563888
* | def DefDef
38573889
* | type {nl} TypeDef
3890+
* | cap {nl} CapDef -- under capture checking
38583891
* | TmplDef
38593892
* EnumCase ::= `case' (id ClassConstr [`extends' ConstrApps]] | ids)
38603893
*/
3861-
def defOrDcl(start: Int, mods: Modifiers): Tree = in.token match {
3862-
case VAL =>
3863-
in.nextToken()
3864-
patDefOrDcl(start, mods)
3865-
case VAR =>
3866-
val mod = atSpan(in.skipToken()) { Mod.Var() }
3867-
val mod1 = addMod(mods, mod)
3868-
patDefOrDcl(start, mod1)
3869-
case DEF =>
3870-
defDefOrDcl(start, in.skipToken(mods))
3871-
case TYPE =>
3872-
typeDefOrDcl(start, in.skipToken(mods))
3873-
case CASE if inEnum =>
3874-
enumCase(start, mods)
3875-
case _ =>
3876-
tmplDef(start, mods)
3894+
def defOrDcl(start: Int, mods: Modifiers): Tree =
3895+
in.token match {
3896+
case VAL =>
3897+
in.nextToken()
3898+
patDefOrDcl(start, mods)
3899+
case VAR =>
3900+
val mod = atSpan(in.skipToken()) { Mod.Var() }
3901+
val mod1 = addMod(mods, mod)
3902+
patDefOrDcl(start, mod1)
3903+
case DEF =>
3904+
defDefOrDcl(start, in.skipToken(mods))
3905+
case TYPE =>
3906+
typeDefOrDcl(start, in.skipToken(mods))
3907+
case CASE if inEnum =>
3908+
enumCase(start, mods)
3909+
case _ =>
3910+
if Feature.ccEnabled && isIdent(nme.cap) then //TODO do we want a dedicated CAP token? TokensCommon would need a Ctx to check if ccenabled
3911+
capDefOrDcl(start, in.skipToken(mods))
3912+
else tmplDef(start, mods)
38773913
}
38783914

38793915
/** PatDef ::= ids [‘:’ Type] [‘=’ Expr]
@@ -4082,6 +4118,52 @@ object Parsers {
40824118
}
40834119
}
40844120

4121+
/** CapDef ::= id CaptureSetAndCtxBounds [‘=’ CaptureSet] -- under capture checking
4122+
*/
4123+
def capDefOrDcl(start: Offset, mods: Modifiers): Tree =
4124+
newLinesOpt()
4125+
atSpan(start, nameStart) {
4126+
val nameIdent = typeIdent()
4127+
val tname = nameIdent.name.asTypeName
4128+
// val tparams = typeParamClauseOpt(ParamOwner.Hk) TODO: error message: type parameters not allowed
4129+
// val vparamss = funParamClauses()
4130+
4131+
def makeCapDef(refs: List[Tree] | Tree): Tree = {
4132+
val tdef = TypeDef(nameIdent.name.toTypeName,
4133+
refs.match
4134+
case refs: List[Tree] => capsType(refs)
4135+
case bounds: Tree => bounds)
4136+
4137+
if (nameIdent.isBackquoted)
4138+
tdef.pushAttachment(Backquoted, ())
4139+
finalizeDef(tdef, mods, start)
4140+
}
4141+
4142+
in.token.match
4143+
case EQUALS =>
4144+
in.nextToken()
4145+
makeCapDef(captureSetOrRef())
4146+
case SUBTYPE | SUPERTYPE =>
4147+
captureSetAndCtxBounds(tname) match
4148+
case bounds: TypeBoundsTree if in.token == EQUALS => //TODO ask Martin: can this case even happen?
4149+
val eqOffset = in.skipToken()
4150+
var rhs = capsType(captureSetOrRef())
4151+
if mods.is(Opaque) then
4152+
rhs = TypeBoundsTree(bounds.lo, bounds.hi, rhs)
4153+
else
4154+
syntaxError(em"cannot combine bound and alias", eqOffset)
4155+
makeCapDef(rhs)
4156+
case bounds => makeCapDef(bounds)
4157+
case SEMI | NEWLINE | NEWLINES | COMMA | RBRACE | OUTDENT | EOF =>
4158+
makeCapDef(captureSetAndCtxBounds(tname))
4159+
case _ if (staged & StageKind.QuotedPattern) != 0 //TODO not sure if we need this case for capsets
4160+
|| sourceVersion.enablesNewGivens && in.isColon =>
4161+
makeCapDef(captureSetAndCtxBounds(tname))
4162+
case _ =>
4163+
syntaxErrorOrIncomplete(ExpectedTypeBoundOrEquals(in.token)) //TODO change error message
4164+
return EmptyTree // return to avoid setting the span to EmptyTree
4165+
}
4166+
40854167
/** TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef
40864168
* | [‘case’] ‘object’ ObjectDef
40874169
* | ‘enum’ EnumDef
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import language.experimental.captureChecking
2+
3+
def test =
4+
val x: Any^ = ???
5+
val y: Any^ = ???
6+
object O:
7+
val z: Any^ = ???
8+
trait CaptureSet:
9+
cap A >: y <: x
10+
cap B = x
11+
cap C <: {x}
12+
cap D
13+
cap E <: C
14+
cap F <: {C}
15+
cap G <: {x, y}
16+
cap H >: {x} <: {x,y}
17+
cap I = {y, G, H}
18+
cap J = {O.z}
19+
cap K = {x, O.z}
20+
cap L <: {x, y, O.z}
21+
cap M >: {x, y, O.z} <: C
22+
cap N >: x <: x
23+
cap O >: O.z <: O.z

0 commit comments

Comments
 (0)