Skip to content

Commit 8f4d87d

Browse files
committed
New capture-vars syntax, "higher-kinds" style
Capture variables and members are marked by the sticky `CaptureVar` attachment to their TypeDef and the respective rhs.
1 parent 1db01aa commit 8f4d87d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+799
-174
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,7 @@ object desugar {
642642
.withMods(mods & (GivenOrImplicit | Erased | hasDefault | Tracked) | Param)
643643
}
644644

645-
/** Desugar type def (not param): Under x.moduliity this can expand
645+
/** Desugar type def (not param): Under x.modularity this can expand
646646
* context bounds, which are expanded to evidence ValDefs. These will
647647
* ultimately map to deferred givens.
648648
*/

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

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ object Trees {
3434

3535
val SyntheticUnit: Property.StickyKey[Unit] = Property.StickyKey()
3636

37+
/** Property key for marking capture-set variables and members */
38+
val CaptureVar: Property.StickyKey[Unit] = Property.StickyKey()
39+
3740
/** Trees take a parameter indicating what the type of their `tpe` field
3841
* is. Two choices: `Type` or `Untyped`.
3942
* Untyped trees have type `Tree[Untyped]`.

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

-8
Original file line numberDiff line numberDiff line change
@@ -533,14 +533,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
533533
def makeCapsOf(tp: RefTree)(using Context): Tree =
534534
TypeApply(capsInternalDot(nme.capsOf), tp :: Nil)
535535

536-
// Capture set variable `[C^]` becomes: `[C >: CapSet <: CapSet^{cap}]`
537-
def makeCapsBound()(using Context): TypeBoundsTree =
538-
TypeBoundsTree(
539-
Select(scalaDot(nme.caps), tpnme.CapSet),
540-
makeRetaining(
541-
Select(scalaDot(nme.caps), tpnme.CapSet),
542-
Nil, tpnme.retainsCap))
543-
544536
def makeConstructor(tparams: List[TypeDef], vparamss: List[List[ValDef]], rhs: Tree = EmptyTree)(using Context): DefDef =
545537
DefDef(nme.CONSTRUCTOR, joinParams(tparams, vparamss), TypeTree(), rhs)
546538

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

+95-45
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ object Parsers {
227227
def isNumericLit = numericLitTokens contains in.token
228228
def isTemplateIntro = templateIntroTokens contains in.token
229229
def isDclIntro = dclIntroTokens contains in.token
230+
def isDclIntroNext = dclIntroTokens contains in.lookahead.token
230231
def isStatSeqEnd = in.isNestedEnd || in.token == EOF || in.token == RPAREN
231232
def mustStartStat = mustStartStatTokens contains in.token
232233

@@ -1591,8 +1592,7 @@ object Parsers {
15911592
case _ => None
15921593
}
15931594

1594-
/** CaptureRef ::= { SimpleRef `.` } SimpleRef [`*`]
1595-
* | [ { SimpleRef `.` } SimpleRef `.` ] id
1595+
/** CaptureRef ::= { SimpleRef `.` } SimpleRef [`*`] [`.` `rd`] -- under captureChecking
15961596
*/
15971597
def captureRef(): Tree =
15981598

@@ -1618,9 +1618,10 @@ object Parsers {
16181618

16191619
/** CaptureSet ::= `{` CaptureRef {`,` CaptureRef} `}` -- under captureChecking
16201620
*/
1621-
def captureSet(): List[Tree] = inBraces {
1622-
if in.token == RBRACE then Nil else commaSeparated(captureRef)
1623-
}
1621+
def captureSet(): List[Tree] =
1622+
inBraces {
1623+
if in.token == RBRACE then Nil else commaSeparated(captureRef)
1624+
}
16241625

16251626
def capturesAndResult(core: () => Tree): Tree =
16261627
if Feature.ccEnabled && in.token == LBRACE && canStartCaptureSetContentsTokens.contains(in.lookahead.token)
@@ -1634,9 +1635,9 @@ object Parsers {
16341635
* | InfixType
16351636
* FunType ::= (MonoFunType | PolyFunType)
16361637
* MonoFunType ::= FunTypeArgs (‘=>’ | ‘?=>’) Type
1637-
* | (‘->’ | ‘?->’ ) [CaptureSet] Type -- under pureFunctions
1638+
* | (‘->’ | ‘?->’ ) [CaptureSet] Type -- under pureFunctions and captureChecking
16381639
* PolyFunType ::= TypTypeParamClause '=>' Type
1639-
* | TypTypeParamClause ‘->’ [CaptureSet] Type -- under pureFunctions
1640+
* | TypTypeParamClause ‘->’ [CaptureSet] Type -- under pureFunctions and captureChecking
16401641
* FunTypeArgs ::= InfixType
16411642
* | `(' [ FunArgType {`,' FunArgType } ] `)'
16421643
* | '(' [ TypedFunParam {',' TypedFunParam } ')'
@@ -1879,7 +1880,7 @@ object Parsers {
18791880
if in.token == LPAREN then funParamClause() :: funParamClauses() else Nil
18801881

18811882
/** InfixType ::= RefinedType {id [nl] RefinedType}
1882-
* | RefinedType `^` // under capture checking
1883+
* | RefinedType `^` -- under captureChecking
18831884
*/
18841885
def infixType(inContextBound: Boolean = false): Tree = infixTypeRest(inContextBound)(refinedType())
18851886

@@ -1910,6 +1911,12 @@ object Parsers {
19101911
|| !canStartInfixTypeTokens.contains(ahead.token)
19111912
|| ahead.lineOffset > 0
19121913

1914+
inline def gobbleHat(): Boolean =
1915+
if Feature.ccEnabled && isIdent(nme.UPARROW) then
1916+
in.nextToken()
1917+
true
1918+
else false
1919+
19131920
def refinedTypeRest(t: Tree): Tree = {
19141921
argumentStart()
19151922
if in.isNestedStart then
@@ -2166,35 +2173,45 @@ object Parsers {
21662173
atSpan(startOffset(t), startOffset(id)) { Select(t, id.name) }
21672174
}
21682175

2169-
/** ArgTypes ::= Type {`,' Type}
2170-
* | NamedTypeArg {`,' NamedTypeArg}
2171-
* NamedTypeArg ::= id `=' Type
2176+
/** ArgTypes ::= TypeArg {‘,’ TypeArg}
2177+
* | NamedTypeArg {‘,’ NamedTypeArg}
2178+
* TypeArg ::= Type
2179+
* | CaptureSet -- under captureChecking
2180+
* NamedTypeArg ::= id ‘=’ TypeArg
21722181
* NamesAndTypes ::= NameAndType {‘,’ NameAndType}
2173-
* NameAndType ::= id ':' Type
2182+
* NameAndType ::= id ‘:’ Type
21742183
*/
21752184
def argTypes(namedOK: Boolean, wildOK: Boolean, tupleOK: Boolean): List[Tree] =
2176-
def argType() =
2177-
val t = typ()
2185+
def wildCardCheck(gen: Tree): Tree =
2186+
val t = gen
21782187
if wildOK then t else rejectWildcardType(t)
21792188

2180-
def namedArgType() =
2189+
def argType() = wildCardCheck(typ())
2190+
2191+
def typeArg() = wildCardCheck:
2192+
if Feature.ccEnabled && in.token == LBRACE && !isDclIntroNext then // is this a capture set and not a refinement type?
2193+
// This case is ambiguous w.r.t. an Object literal {}. But since CC is enabled, we probably expect it to designate the empty set
2194+
concreteCapsType(captureSet())
2195+
else typ()
2196+
2197+
def namedTypeArg() =
21812198
atSpan(in.offset):
21822199
val name = ident()
21832200
accept(EQUALS)
2184-
NamedArg(name.toTypeName, argType())
2201+
NamedArg(name.toTypeName, typeArg())
21852202

2186-
def namedElem() =
2203+
def nameAndType() =
21872204
atSpan(in.offset):
21882205
val name = ident()
21892206
acceptColon()
21902207
NamedArg(name, argType())
21912208

2192-
if namedOK && isIdent && in.lookahead.token == EQUALS then
2193-
commaSeparated(() => namedArgType())
2209+
if namedOK && (isIdent && in.lookahead.token == EQUALS) then
2210+
commaSeparated(() => namedTypeArg())
21942211
else if tupleOK && isIdent && in.lookahead.isColon && sourceVersion.enablesNamedTuples then
2195-
commaSeparated(() => namedElem())
2212+
commaSeparated(() => nameAndType())
21962213
else
2197-
commaSeparated(() => argType())
2214+
commaSeparated(() => typeArg())
21982215
end argTypes
21992216

22002217
def paramTypeOf(core: () => Tree): Tree =
@@ -2238,7 +2255,7 @@ object Parsers {
22382255
PostfixOp(t, Ident(tpnme.raw.STAR))
22392256
else t
22402257

2241-
/** TypeArgs ::= `[' Type {`,' Type} `]'
2258+
/** TypeArgs ::= `[' TypeArg {`,' TypeArg} `]'
22422259
* NamedTypeArgs ::= `[' NamedTypeArg {`,' NamedTypeArg} `]'
22432260
*/
22442261
def typeArgs(namedOK: Boolean, wildOK: Boolean): List[Tree] =
@@ -2252,21 +2269,28 @@ object Parsers {
22522269
else
22532270
inBraces(refineStatSeq())
22542271

2255-
/** TypeBounds ::= [`>:' Type] [`<:' Type]
2256-
* | `^` -- under captureChecking
2272+
/** TypeBounds ::= [`>:' TypeBound ] [`<:' TypeBound ]
2273+
* TypeBound ::= Type
2274+
* | CaptureSet -- under captureChecking
22572275
*/
22582276
def typeBounds(): TypeBoundsTree =
22592277
atSpan(in.offset):
2260-
if in.isIdent(nme.UPARROW) && Feature.ccEnabled then
2261-
in.nextToken()
2262-
makeCapsBound()
2263-
else
2264-
TypeBoundsTree(bound(SUPERTYPE), bound(SUBTYPE))
2278+
TypeBoundsTree(bound(SUPERTYPE), bound(SUBTYPE))
22652279

22662280
private def bound(tok: Int): Tree =
2267-
if (in.token == tok) { in.nextToken(); toplevelTyp() }
2281+
if in.token == tok then
2282+
in.nextToken()
2283+
if Feature.ccEnabled && in.token == LBRACE && !isDclIntroNext then
2284+
capsBound(captureSet(), isLowerBound = tok == SUPERTYPE)
2285+
else toplevelTyp()
22682286
else EmptyTree
22692287

2288+
private def capsBound(refs: List[Tree], isLowerBound: Boolean = false): Tree =
2289+
if isLowerBound && refs.isEmpty then // lower bounds with empty capture sets become a pure CapSet
2290+
Select(scalaDot(nme.caps), tpnme.CapSet)
2291+
else
2292+
concreteCapsType(refs)
2293+
22702294
/** TypeAndCtxBounds ::= TypeBounds [`:` ContextBounds]
22712295
*/
22722296
def typeAndCtxBounds(pname: TypeName): Tree = {
@@ -3391,7 +3415,7 @@ object Parsers {
33913415
* | opaque
33923416
* LocalModifier ::= abstract | final | sealed | open | implicit | lazy | erased |
33933417
* inline | transparent | infix |
3394-
* mut -- under cc
3418+
* mut -- under captureChecking
33953419
*/
33963420
def modifiers(allowed: BitSet = modifierTokens, start: Modifiers = Modifiers()): Modifiers = {
33973421
@tailrec
@@ -3480,22 +3504,25 @@ object Parsers {
34803504
recur(numLeadParams, firstClause = true, prevIsTypeClause = false)
34813505
end typeOrTermParamClauses
34823506

3483-
34843507
/** ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’
3485-
* ClsTypeParam ::= {Annotation} [‘+’ | ‘-’]
3486-
* id [HkTypeParamClause] TypeAndCtxBounds
3508+
* ClsTypeParam ::= {Annotation} [‘+’ | ‘-’]
3509+
* id [HkTypeParamClause] TypeAndCtxBounds
3510+
* | {Annotation} [‘+’ | ‘-’] id `^` TypeAndCtxBounds -- under captureChecking
34873511
*
34883512
* DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’
3489-
* DefTypeParam ::= {Annotation}
3490-
* id [HkTypeParamClause] TypeAndCtxBounds
3513+
* DefTypeParam ::= {Annotation}
3514+
* id [HkTypeParamClause] TypeAndCtxBounds
3515+
* | {Annotation} id `^` TypeAndCtxBounds -- under captureChecking
34913516
*
34923517
* TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’
3493-
* TypTypeParam ::= {Annotation}
3494-
* (id | ‘_’) [HkTypeParamClause] TypeAndCtxBounds
3518+
* TypTypeParam ::= {Annotation}
3519+
* (id | ‘_’) [HkTypeParamClause] TypeAndCtxBounds
3520+
* | {Annotation} (id | ‘_’) `^` TypeAndCtxBounds -- under captureChecking
34953521
*
34963522
* HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} ‘]’
3497-
* HkTypeParam ::= {Annotation} [‘+’ | ‘-’]
3498-
* (id | ‘_’) [HkTypeParamClause] TypeBounds
3523+
* HkTypeParam ::= {Annotation} [‘+’ | ‘-’]
3524+
* (id | ‘_’) [HkTypePamClause] TypeBounds
3525+
* | {Annotation} [‘+’ | ‘-’] (id | ‘_’) `^` TypeBounds -- under captureChecking
34993526
*/
35003527
def typeParamClause(paramOwner: ParamOwner): List[TypeDef] = inBracketsWithCommas {
35013528

@@ -3520,12 +3547,18 @@ object Parsers {
35203547
in.nextToken()
35213548
WildcardParamName.fresh().toTypeName
35223549
else ident().toTypeName
3550+
val isCap = gobbleHat()
35233551
val hkparams = typeParamClauseOpt(ParamOwner.Hk)
35243552
val bounds =
35253553
if paramOwner.acceptsCtxBounds then typeAndCtxBounds(name)
35263554
else if sourceVersion.enablesNewGivens && paramOwner == ParamOwner.Type then typeAndCtxBounds(name)
35273555
else typeBounds()
3528-
TypeDef(name, lambdaAbstract(hkparams, bounds)).withMods(mods)
3556+
val res = TypeDef(name, lambdaAbstract(hkparams, bounds)).withMods(mods)
3557+
if isCap then
3558+
res.pushAttachment(CaptureVar, ())
3559+
// putting the attachment here as well makes post-processing in the typer easier
3560+
bounds.pushAttachment(CaptureVar, ())
3561+
res
35293562
}
35303563
}
35313564
commaSeparated(() => typeParam())
@@ -4046,33 +4079,47 @@ object Parsers {
40464079
argumentExprss(mkApply(Ident(nme.CONSTRUCTOR), argumentExprs()))
40474080
}
40484081

4049-
/** TypeDef ::= id [HkTypeParamClause] {FunParamClause} TypeAndCtxBounds [‘=’ Type]
4082+
/** TypeDef ::= id [HkTypeParamClause] {FunParamClause} TypeAndCtxBounds [‘=’ TypeDefRHS ]
4083+
* | id `^` TypeAndCtxBounds [‘=’ TypeDefRHS ] -- under captureChecking
4084+
* TypeDefRHS ::= Type
4085+
* | CaptureSet -- under captureChecking
40504086
*/
40514087
def typeDefOrDcl(start: Offset, mods: Modifiers): Tree = {
4088+
4089+
def typeDefRHS(): Tree =
4090+
if Feature.ccEnabled && in.token == LBRACE && !isDclIntroNext then
4091+
concreteCapsType(captureSet())
4092+
else toplevelTyp()
4093+
40524094
newLinesOpt()
40534095
atSpan(start, nameStart) {
40544096
val nameIdent = typeIdent()
4097+
val isCapDef = gobbleHat()
40554098
val tname = nameIdent.name.asTypeName
40564099
val tparams = typeParamClauseOpt(ParamOwner.Hk)
40574100
val vparamss = funParamClauses()
40584101

40594102
def makeTypeDef(rhs: Tree): Tree = {
40604103
val rhs1 = lambdaAbstractAll(tparams :: vparamss, rhs)
40614104
val tdef = TypeDef(nameIdent.name.toTypeName, rhs1)
4062-
if (nameIdent.isBackquoted)
4105+
if nameIdent.isBackquoted then
40634106
tdef.pushAttachment(Backquoted, ())
4107+
if isCapDef then
4108+
tdef.pushAttachment(CaptureVar, ())
4109+
// putting the attachment here as well makes post-processing in the typer easier
4110+
rhs.pushAttachment(CaptureVar, ())
40644111
finalizeDef(tdef, mods, start)
40654112
}
40664113

40674114
in.token match {
40684115
case EQUALS =>
40694116
in.nextToken()
4070-
makeTypeDef(toplevelTyp())
4117+
makeTypeDef(typeDefRHS())
40714118
case SUBTYPE | SUPERTYPE =>
40724119
typeAndCtxBounds(tname) match
40734120
case bounds: TypeBoundsTree if in.token == EQUALS =>
40744121
val eqOffset = in.skipToken()
4075-
var rhs = toplevelTyp()
4122+
var rhs = typeDefRHS()
40764123
rhs match {
40774124
case mtt: MatchTypeTree =>
40784125
bounds match {
@@ -4101,6 +4148,9 @@ object Parsers {
41014148
}
41024149
}
41034150

4151+
private def concreteCapsType(refs: List[Tree]): Tree =
4152+
makeRetaining(Select(scalaDot(nme.caps), tpnme.CapSet), refs, tpnme.retains)
4153+
41044154
/** TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef
41054155
* | [‘case’] ‘object’ ObjectDef
41064156
* | ‘enum’ EnumDef

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
451451
homogenize(tp) match
452452
case tp: TermRef if tp.symbol == defn.captureRoot => "cap"
453453
case tp: SingletonType => toTextRef(tp)
454-
case tp: (TypeRef | TypeParamRef) => toText(tp) ~ "^"
454+
case tp: (TypeRef | TypeParamRef) => toText(tp)
455455
case ReadOnlyCapability(tp1) => toTextCaptureRef(tp1) ~ ".rd"
456456
case ReachCapability(tp1) => toTextCaptureRef(tp1) ~ "*"
457457
case MaybeCapability(tp1) => toTextCaptureRef(tp1) ~ "?"

compiler/src/dotty/tools/dotc/typer/Applications.scala

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import annotation.threadUnsafe
3737
import scala.util.control.NonFatal
3838
import dotty.tools.dotc.inlines.Inlines
3939
import scala.annotation.tailrec
40+
import dotty.tools.dotc.cc.isRetains
4041

4142
object Applications {
4243
import tpd.*

0 commit comments

Comments
 (0)