Skip to content

Commit 6a2b7c7

Browse files
committed
More concise syntax for capture members
1 parent 114b879 commit 6a2b7c7

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

@@ -1610,6 +1613,12 @@ object Parsers {
16101613
if in.token == RBRACE then Nil else commaSeparated(captureRef)
16111614
}
16121615

1616+
/** CaptureSetOrRef ::= `{` CaptureSet `}` | CaptureRef -- under captureChecking
1617+
*/
1618+
def captureSetOrRef(): List[Tree] =
1619+
if in.token == LBRACE then captureSet()
1620+
else List(captureRef())
1621+
16131622
def capturesAndResult(core: () => Tree): Tree =
16141623
if Feature.ccEnabled && in.token == LBRACE && in.offset == in.lastOffset
16151624
then CapturesAndResult(captureSet(), core())
@@ -1951,7 +1960,8 @@ object Parsers {
19511960

19521961
def typeBlockStats(): List[Tree] =
19531962
val tdefs = new ListBuffer[Tree]
1954-
while in.token == TYPE do tdefs += typeBlockStat()
1963+
while (in.token == TYPE) do
1964+
tdefs += typeBlockStat()
19551965
tdefs.toList
19561966

19571967
/** TypeBlockStat ::= ‘type’ {nl} TypeDef
@@ -2233,7 +2243,7 @@ object Parsers {
22332243
inBraces(refineStatSeq())
22342244

22352245
/** TypeBounds ::= [`>:' Type] [`<:' Type]
2236-
* | `^` -- under captureChecking
2246+
* | `^` -- under captureChecking TODO remove
22372247
*/
22382248
def typeBounds(): TypeBoundsTree =
22392249
atSpan(in.offset):
@@ -2247,6 +2257,19 @@ object Parsers {
22472257
if (in.token == tok) { in.nextToken(); toplevelTyp() }
22482258
else EmptyTree
22492259

2260+
private def capsType(refs: List[Tree], isLowerBound: Boolean = false): Tree =
2261+
if isLowerBound && refs.isEmpty then
2262+
Select(scalaDot(nme.caps), tpnme.CapSet)
2263+
else
2264+
makeRetaining(Select(scalaDot(nme.caps), tpnme.CapSet), refs, if refs.isEmpty then tpnme.retainsCap else tpnme.retains)
2265+
2266+
private def capsBound(tok: Int): Tree =
2267+
if (in.token == tok) then
2268+
in.nextToken()
2269+
capsType(captureSetOrRef(), isLowerBound = tok == SUPERTYPE)
2270+
else
2271+
capsType(Nil, isLowerBound = tok == SUPERTYPE)
2272+
22502273
/** TypeAndCtxBounds ::= TypeBounds [`:` ContextBounds]
22512274
*/
22522275
def typeAndCtxBounds(pname: TypeName): Tree = {
@@ -2256,6 +2279,15 @@ object Parsers {
22562279
else atSpan((t.span union cbs.head.span).start) { ContextBounds(t, cbs) }
22572280
}
22582281

2282+
/** TypeAndCtxBounds ::= TypeBounds [`:` ContextBounds] -- under captureChecking
2283+
*/
2284+
def captureSetAndCtxBounds(pname: TypeName): Tree = {
2285+
val t = TypeBoundsTree(capsBound(SUPERTYPE), capsBound(SUBTYPE))
2286+
val cbs = contextBounds(pname)
2287+
if (cbs.isEmpty) t
2288+
else atSpan((t.span union cbs.head.span).start) { ContextBounds(t, cbs) }
2289+
}
2290+
22592291
/** ContextBound ::= Type [`as` id] */
22602292
def contextBound(pname: TypeName): Tree =
22612293
val t = toplevelTyp(inContextBound = true)
@@ -3848,25 +3880,29 @@ object Parsers {
38483880
* | var VarDef
38493881
* | def DefDef
38503882
* | type {nl} TypeDef
3883+
* | cap {nl} CapDef -- under capture checking
38513884
* | TmplDef
38523885
* EnumCase ::= `case' (id ClassConstr [`extends' ConstrApps]] | ids)
38533886
*/
3854-
def defOrDcl(start: Int, mods: Modifiers): Tree = in.token match {
3855-
case VAL =>
3856-
in.nextToken()
3857-
patDefOrDcl(start, mods)
3858-
case VAR =>
3859-
val mod = atSpan(in.skipToken()) { Mod.Var() }
3860-
val mod1 = addMod(mods, mod)
3861-
patDefOrDcl(start, mod1)
3862-
case DEF =>
3863-
defDefOrDcl(start, in.skipToken(mods))
3864-
case TYPE =>
3865-
typeDefOrDcl(start, in.skipToken(mods))
3866-
case CASE if inEnum =>
3867-
enumCase(start, mods)
3868-
case _ =>
3869-
tmplDef(start, mods)
3887+
def defOrDcl(start: Int, mods: Modifiers): Tree =
3888+
in.token match {
3889+
case VAL =>
3890+
in.nextToken()
3891+
patDefOrDcl(start, mods)
3892+
case VAR =>
3893+
val mod = atSpan(in.skipToken()) { Mod.Var() }
3894+
val mod1 = addMod(mods, mod)
3895+
patDefOrDcl(start, mod1)
3896+
case DEF =>
3897+
defDefOrDcl(start, in.skipToken(mods))
3898+
case TYPE =>
3899+
typeDefOrDcl(start, in.skipToken(mods))
3900+
case CASE if inEnum =>
3901+
enumCase(start, mods)
3902+
case _ =>
3903+
if Feature.ccEnabled && isIdent(nme.cap) then //TODO do we want a dedicated CAP token? TokensCommon would need a Ctx to check if ccenabled
3904+
capDefOrDcl(start, in.skipToken(mods))
3905+
else tmplDef(start, mods)
38703906
}
38713907

38723908
/** PatDef ::= ids [‘:’ Type] [‘=’ Expr]
@@ -4075,6 +4111,52 @@ object Parsers {
40754111
}
40764112
}
40774113

4114+
/** CapDef ::= id CaptureSetAndCtxBounds [‘=’ CaptureSet] -- under capture checking
4115+
*/
4116+
def capDefOrDcl(start: Offset, mods: Modifiers): Tree =
4117+
newLinesOpt()
4118+
atSpan(start, nameStart) {
4119+
val nameIdent = typeIdent()
4120+
val tname = nameIdent.name.asTypeName
4121+
// val tparams = typeParamClauseOpt(ParamOwner.Hk) TODO: error message: type parameters not allowed
4122+
// val vparamss = funParamClauses()
4123+
4124+
def makeCapDef(refs: List[Tree] | Tree): Tree = {
4125+
val tdef = TypeDef(nameIdent.name.toTypeName,
4126+
refs.match
4127+
case refs: List[Tree] => capsType(refs)
4128+
case bounds: Tree => bounds)
4129+
4130+
if (nameIdent.isBackquoted)
4131+
tdef.pushAttachment(Backquoted, ())
4132+
finalizeDef(tdef, mods, start)
4133+
}
4134+
4135+
in.token.match
4136+
case EQUALS =>
4137+
in.nextToken()
4138+
makeCapDef(captureSetOrRef())
4139+
case SUBTYPE | SUPERTYPE =>
4140+
captureSetAndCtxBounds(tname) match
4141+
case bounds: TypeBoundsTree if in.token == EQUALS => //TODO ask Martin: can this case even happen?
4142+
val eqOffset = in.skipToken()
4143+
var rhs = capsType(captureSetOrRef())
4144+
if mods.is(Opaque) then
4145+
rhs = TypeBoundsTree(bounds.lo, bounds.hi, rhs)
4146+
else
4147+
syntaxError(em"cannot combine bound and alias", eqOffset)
4148+
makeCapDef(rhs)
4149+
case bounds => makeCapDef(bounds)
4150+
case SEMI | NEWLINE | NEWLINES | COMMA | RBRACE | OUTDENT | EOF =>
4151+
makeCapDef(captureSetAndCtxBounds(tname))
4152+
case _ if (staged & StageKind.QuotedPattern) != 0 //TODO not sure if we need this case for capsets
4153+
|| sourceVersion.enablesNewGivens && in.isColon =>
4154+
makeCapDef(captureSetAndCtxBounds(tname))
4155+
case _ =>
4156+
syntaxErrorOrIncomplete(ExpectedTypeBoundOrEquals(in.token)) //TODO change error message
4157+
return EmptyTree // return to avoid setting the span to EmptyTree
4158+
}
4159+
40784160
/** TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef
40794161
* | [‘case’] ‘object’ ObjectDef
40804162
* | ‘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)