Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ff07e02

Browse files
committedApr 29, 2025·
New capture-vars syntax, "higher-kinds" style
1 parent 412c40a commit ff07e02

29 files changed

+227
-153
lines changed
 

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -534,12 +534,13 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
534534
TypeApply(capsInternalDot(nme.capsOf), tp :: Nil)
535535

536536
// 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))
537+
def makeCapsBound(refsL: List[Tree] = Nil, refsU: List[Tree] = Nil)(using Context): TypeBoundsTree =
538+
val lower = refsL match
539+
case Nil => Select(scalaDot(nme.caps), tpnme.CapSet)
540+
case refsL => makeRetaining(Select(scalaDot(nme.caps), tpnme.CapSet), refsL, tpnme.retains)
541+
val upper =
542+
makeRetaining(Select(scalaDot(nme.caps), tpnme.CapSet), refsU, if refsU.isEmpty then tpnme.retainsCap else tpnme.retains)
543+
TypeBoundsTree(lower, upper)
543544

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

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

Lines changed: 108 additions & 55 deletions
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

@@ -1601,7 +1602,7 @@ object Parsers {
16011602
case _ => None
16021603
}
16031604

1604-
/** CaptureRef ::= { SimpleRef `.` } SimpleRef [`*`]
1605+
/** CaptureRef ::= { SimpleRef `.` } SimpleRef [`*`] [`.` `rd`] -- under captureChecking
16051606
* | [ { SimpleRef `.` } SimpleRef `.` ] id
16061607
*/
16071608
def captureRef(): Tree =
@@ -1628,9 +1629,13 @@ object Parsers {
16281629

16291630
/** CaptureSet ::= `{` CaptureRef {`,` CaptureRef} `}` -- under captureChecking
16301631
*/
1631-
def captureSet(): List[Tree] = inBraces {
1632-
if in.token == RBRACE then Nil else commaSeparated(captureRef)
1633-
}
1632+
def captureSet(): List[Tree] =
1633+
if in.token != LBRACE then
1634+
syntaxError(em"expected '{' to start capture set", in.offset)
1635+
Nil
1636+
else inBraces {
1637+
if in.token == RBRACE then Nil else commaSeparated(captureRef)
1638+
}
16341639

16351640
def capturesAndResult(core: () => Tree): Tree =
16361641
if Feature.ccEnabled && in.token == LBRACE && canStartCaptureSetContentsTokens.contains(in.lookahead.token)
@@ -1644,9 +1649,9 @@ object Parsers {
16441649
* | InfixType
16451650
* FunType ::= (MonoFunType | PolyFunType)
16461651
* MonoFunType ::= FunTypeArgs (‘=>’ | ‘?=>’) Type
1647-
* | (‘->’ | ‘?->’ ) [CaptureSet] Type -- under pureFunctions
1652+
* | (‘->’ | ‘?->’ ) [CaptureSet] Type -- under pureFunctions and captureChecking
16481653
* PolyFunType ::= TypTypeParamClause '=>' Type
1649-
* | TypTypeParamClause ‘->’ [CaptureSet] Type -- under pureFunctions
1654+
* | TypTypeParamClause ‘->’ [CaptureSet] Type -- under pureFunctions and captureChecking
16501655
* FunTypeArgs ::= InfixType
16511656
* | `(' [ FunArgType {`,' FunArgType } ] `)'
16521657
* | '(' [ TypedFunParam {',' TypedFunParam } ')'
@@ -1889,7 +1894,7 @@ object Parsers {
18891894
if in.token == LPAREN then funParamClause() :: funParamClauses() else Nil
18901895

18911896
/** InfixType ::= RefinedType {id [nl] RefinedType}
1892-
* | RefinedType `^` // under capture checking
1897+
* | RefinedType `^` -- under captureChecking
18931898
*/
18941899
def infixType(inContextBound: Boolean = false): Tree = infixTypeRest(inContextBound)(refinedType())
18951900

@@ -1920,6 +1925,12 @@ object Parsers {
19201925
|| !canStartInfixTypeTokens.contains(ahead.token)
19211926
|| ahead.lineOffset > 0
19221927

1928+
inline def gobbleHat(): Boolean =
1929+
if Feature.ccEnabled && isIdent(nme.UPARROW) then
1930+
in.nextToken()
1931+
true
1932+
else false
1933+
19231934
def refinedTypeRest(t: Tree): Tree = {
19241935
argumentStart()
19251936
if in.isNestedStart then
@@ -2176,35 +2187,45 @@ object Parsers {
21762187
atSpan(startOffset(t), startOffset(id)) { Select(t, id.name) }
21772188
}
21782189

2179-
/** ArgTypes ::= Type {`,' Type}
2180-
* | NamedTypeArg {`,' NamedTypeArg}
2181-
* NamedTypeArg ::= id `=' Type
2190+
/** ArgTypes ::= TypeArg {‘,’ TypeArg}
2191+
* | NamedTypeArg {‘,’ NamedTypeArg}
2192+
* TypeArg ::= Type
2193+
* | CaptureSet -- under captureChecking
2194+
* NamedTypeArg ::= id ‘=’ TypeArg
21822195
* NamesAndTypes ::= NameAndType {‘,’ NameAndType}
2183-
* NameAndType ::= id ':' Type
2196+
* NameAndType ::= id ‘:’ Type
21842197
*/
21852198
def argTypes(namedOK: Boolean, wildOK: Boolean, tupleOK: Boolean): List[Tree] =
2186-
def argType() =
2187-
val t = typ()
2199+
inline def wildCardCheck(inline gen: Tree): Tree =
2200+
val t = gen
21882201
if wildOK then t else rejectWildcardType(t)
21892202

2190-
def namedArgType() =
2203+
def argType() = wildCardCheck(typ())
2204+
2205+
def typeArg() = wildCardCheck:
2206+
if Feature.ccEnabled && in.token == LBRACE && !isDclIntroNext then // is this a capture set and not a refinement type?
2207+
// This case is ambiguous w.r.t. an Object literal {}. But since CC is enabled, we probably expect it to designate the empty set
2208+
concreteCapsType(captureSet())
2209+
else typ()
2210+
2211+
def namedTypeArg() =
21912212
atSpan(in.offset):
21922213
val name = ident()
21932214
accept(EQUALS)
2194-
NamedArg(name.toTypeName, argType())
2215+
NamedArg(name.toTypeName, typeArg())
21952216

2196-
def namedElem() =
2217+
def nameAndType() =
21972218
atSpan(in.offset):
21982219
val name = ident()
21992220
acceptColon()
22002221
NamedArg(name, argType())
22012222

2202-
if namedOK && isIdent && in.lookahead.token == EQUALS then
2203-
commaSeparated(() => namedArgType())
2223+
if namedOK && (isIdent && in.lookahead.token == EQUALS) then
2224+
commaSeparated(() => namedTypeArg())
22042225
else if tupleOK && isIdent && in.lookahead.isColon && sourceVersion.enablesNamedTuples then
2205-
commaSeparated(() => namedElem())
2226+
commaSeparated(() => nameAndType()) // TODO: can capture-set variables occur here?
22062227
else
2207-
commaSeparated(() => argType())
2228+
commaSeparated(() => typeArg())
22082229
end argTypes
22092230

22102231
def paramTypeOf(core: () => Tree): Tree =
@@ -2248,7 +2269,7 @@ object Parsers {
22482269
PostfixOp(t, Ident(tpnme.raw.STAR))
22492270
else t
22502271

2251-
/** TypeArgs ::= `[' Type {`,' Type} `]'
2272+
/** TypeArgs ::= `[' TypeArg {`,' TypeArg} `]'
22522273
* NamedTypeArgs ::= `[' NamedTypeArg {`,' NamedTypeArg} `]'
22532274
*/
22542275
def typeArgs(namedOK: Boolean, wildOK: Boolean): List[Tree] =
@@ -2262,25 +2283,34 @@ object Parsers {
22622283
else
22632284
inBraces(refineStatSeq())
22642285

2265-
/** TypeBounds ::= [`>:' Type] [`<:' Type]
2266-
* | `^` -- under captureChecking
2286+
/** TypeBounds ::= [`>:' TypeBound ] [`<:' TypeBound ]
2287+
* TypeBound ::= Type
2288+
* | CaptureSet -- under captureChecking
22672289
*/
2268-
def typeBounds(): TypeBoundsTree =
2290+
def typeBounds(isCapParamOrMem: Boolean = false): TypeBoundsTree =
22692291
atSpan(in.offset):
2270-
if in.isIdent(nme.UPARROW) && Feature.ccEnabled then
2271-
in.nextToken()
2272-
makeCapsBound()
2273-
else
2274-
TypeBoundsTree(bound(SUPERTYPE), bound(SUBTYPE))
2292+
TypeBoundsTree(bound(SUPERTYPE, isCapParamOrMem), bound(SUBTYPE, isCapParamOrMem))
22752293

2276-
private def bound(tok: Int): Tree =
2277-
if (in.token == tok) { in.nextToken(); toplevelTyp() }
2294+
private def bound(tok: Int, isCapParamOrMem: Boolean = false): Tree =
2295+
if (in.token == tok) then
2296+
in.nextToken()
2297+
if Feature.ccEnabled && (in.token == LBRACE && !isDclIntroNext) then
2298+
capsBound(captureSet(), isLowerBound = tok == SUPERTYPE)
2299+
else toplevelTyp()
2300+
else if Feature.ccEnabled && isCapParamOrMem then
2301+
capsBound(Nil, isLowerBound = tok == SUPERTYPE) // FIXME: should we avoid the CapSet^{} lower bound and make it Nothing?
22782302
else EmptyTree
22792303

2304+
private def capsBound(refs: List[Tree], isLowerBound: Boolean = false): Tree =
2305+
if isLowerBound && refs.isEmpty then // lower bounds with empty capture sets become a pure CapSet
2306+
Select(scalaDot(nme.caps), tpnme.CapSet)
2307+
else
2308+
makeRetaining(Select(scalaDot(nme.caps), tpnme.CapSet), refs, if refs.isEmpty then tpnme.retainsCap else tpnme.retains)
2309+
22802310
/** TypeAndCtxBounds ::= TypeBounds [`:` ContextBounds]
22812311
*/
2282-
def typeAndCtxBounds(pname: TypeName): Tree = {
2283-
val t = typeBounds()
2312+
def typeAndCtxBounds(pname: TypeName, isCapParamOrMem: Boolean = false): Tree = {
2313+
val t = typeBounds(isCapParamOrMem)
22842314
val cbs = contextBounds(pname)
22852315
if (cbs.isEmpty) t
22862316
else atSpan((t.span union cbs.head.span).start) { ContextBounds(t, cbs) }
@@ -3397,7 +3427,7 @@ object Parsers {
33973427
* | opaque
33983428
* LocalModifier ::= abstract | final | sealed | open | implicit | lazy | erased |
33993429
* inline | transparent | infix |
3400-
* mut -- under cc
3430+
* mut -- under captureChecking
34013431
*/
34023432
def modifiers(allowed: BitSet = modifierTokens, start: Modifiers = Modifiers()): Modifiers = {
34033433
@tailrec
@@ -3486,22 +3516,25 @@ object Parsers {
34863516
recur(numLeadParams, firstClause = true, prevIsTypeClause = false)
34873517
end typeOrTermParamClauses
34883518

3489-
34903519
/** ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’
3491-
* ClsTypeParam ::= {Annotation} [‘+’ | ‘-’]
3492-
* id [HkTypeParamClause] TypeAndCtxBounds
3520+
* ClsTypeParam ::= {Annotation} [‘+’ | ‘-’]
3521+
* id [HkTypeParamClause] TypeAndCtxBounds
3522+
* | {Annotation} id [`^`] TypeAndCtxBounds -- under captureChecking
34933523
*
34943524
* DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’
3495-
* DefTypeParam ::= {Annotation}
3496-
* id [HkTypeParamClause] TypeAndCtxBounds
3525+
* DefTypeParam ::= {Annotation}
3526+
* id [HkTypeParamClause] TypeAndCtxBounds
3527+
* | {Annotation} id [`^`] TypeAndCtxBounds -- under captureChecking
34973528
*
34983529
* TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’
3499-
* TypTypeParam ::= {Annotation}
3500-
* (id | ‘_’) [HkTypeParamClause] TypeAndCtxBounds
3530+
* TypTypeParam ::= {Annotation}
3531+
* (id | ‘_’) [HkTypeParamClause] TypeAndCtxBounds
3532+
* | {Annotation} (id | ‘_’) [`^`] TypeAndCtxBounds -- under captureChecking
35013533
*
35023534
* HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} ‘]’
3503-
* HkTypeParam ::= {Annotation} [‘+’ | ‘-’]
3504-
* (id | ‘_’) [HkTypeParamClause] TypeBounds
3535+
* HkTypeParam ::= {Annotation} [‘+’ | ‘-’]
3536+
* (id | ‘_’) [HkTypePamClause] TypeBounds
3537+
* | {Annotation} (id | ‘_’) [`^`] TypeBounds -- under captureChecking
35053538
*/
35063539
def typeParamClause(paramOwner: ParamOwner): List[TypeDef] = inBracketsWithCommas {
35073540

@@ -3526,11 +3559,17 @@ object Parsers {
35263559
in.nextToken()
35273560
WildcardParamName.fresh().toTypeName
35283561
else ident().toTypeName
3562+
val isCap = gobbleHat()
3563+
if isCap && mods.isOneOf(Covariant | Contravariant) then
3564+
syntaxError(em"capture parameters cannot have `+/-` variance annotations") // TODO we might want to allow those
3565+
if isCap && in.token == LBRACKET then
3566+
syntaxError(em"capture parameters do not take type parameters")
3567+
in.nextToken()
35293568
val hkparams = typeParamClauseOpt(ParamOwner.Hk)
35303569
val bounds =
3531-
if paramOwner.acceptsCtxBounds then typeAndCtxBounds(name)
3532-
else if sourceVersion.enablesNewGivens && paramOwner == ParamOwner.Type then typeAndCtxBounds(name)
3533-
else typeBounds()
3570+
if paramOwner.acceptsCtxBounds then typeAndCtxBounds(name, isCap)
3571+
else if sourceVersion.enablesNewGivens && paramOwner == ParamOwner.Type then typeAndCtxBounds(name, isCap)
3572+
else typeBounds(isCap)
35343573
TypeDef(name, lambdaAbstract(hkparams, bounds)).withMods(mods)
35353574
}
35363575
}
@@ -4052,15 +4091,26 @@ object Parsers {
40524091
argumentExprss(mkApply(Ident(nme.CONSTRUCTOR), argumentExprs()))
40534092
}
40544093

4055-
/** TypeDef ::= id [HkTypeParamClause] {FunParamClause} TypeAndCtxBounds [‘=’ Type]
4094+
/** TypeDef ::= id [HkTypeParamClause] {FunParamClause} TypeAndCtxBounds [‘=’ TypeDefRHS ]
4095+
* | id [`^`] TypeAndCtxBounds [‘=’ TypeDefRHS ] -- under captureChecking
4096+
* TypeDefRHS ::= Type
4097+
* | CaptureSet -- under captureChecking
40564098
*/
4057-
def typeDefOrDcl(start: Offset, mods: Modifiers): Tree = {
4099+
def typeDefOrDcl(start: Offset, mods: Modifiers): Tree = { // FIXME: ^-qualified members should automatically receive the CapSet interval!
4100+
4101+
def typeDefRHS(): Tree =
4102+
if Feature.ccEnabled && in.token == LBRACE && !isDclIntroNext then
4103+
concreteCapsType(captureSet())
4104+
else toplevelTyp()
4105+
40584106
newLinesOpt()
40594107
atSpan(start, nameStart) {
40604108
val nameIdent = typeIdent()
4109+
val isCapDef = gobbleHat()
4110+
if isCapDef && in.token == LBRACKET then syntaxError(em"capture-set member declarations cannot have type parameters")
40614111
val tname = nameIdent.name.asTypeName
4062-
val tparams = typeParamClauseOpt(ParamOwner.Hk)
4063-
val vparamss = funParamClauses()
4112+
val tparams = if !isCapDef then typeParamClauseOpt(ParamOwner.Hk) else Nil
4113+
val vparamss = if !isCapDef then funParamClauses() else Nil
40644114

40654115
def makeTypeDef(rhs: Tree): Tree = {
40664116
val rhs1 = lambdaAbstractAll(tparams :: vparamss, rhs)
@@ -4073,12 +4123,12 @@ object Parsers {
40734123
in.token match {
40744124
case EQUALS =>
40754125
in.nextToken()
4076-
makeTypeDef(toplevelTyp())
4126+
makeTypeDef(typeDefRHS())
40774127
case SUBTYPE | SUPERTYPE =>
4078-
typeAndCtxBounds(tname) match
4128+
typeAndCtxBounds(tname, isCapDef) match
40794129
case bounds: TypeBoundsTree if in.token == EQUALS =>
40804130
val eqOffset = in.skipToken()
4081-
var rhs = toplevelTyp()
4131+
var rhs = typeDefRHS()
40824132
rhs match {
40834133
case mtt: MatchTypeTree =>
40844134
bounds match {
@@ -4096,17 +4146,20 @@ object Parsers {
40964146
makeTypeDef(rhs)
40974147
case bounds => makeTypeDef(bounds)
40984148
case SEMI | NEWLINE | NEWLINES | COMMA | RBRACE | OUTDENT | EOF =>
4099-
makeTypeDef(typeAndCtxBounds(tname))
4149+
makeTypeDef(typeAndCtxBounds(tname, isCapDef))
41004150
case _ if (staged & StageKind.QuotedPattern) != 0
41014151
|| sourceVersion.enablesNewGivens && in.isColon =>
4102-
makeTypeDef(typeAndCtxBounds(tname))
4152+
makeTypeDef(typeAndCtxBounds(tname, isCapDef))
41034153
case _ =>
41044154
syntaxErrorOrIncomplete(ExpectedTypeBoundOrEquals(in.token))
41054155
return EmptyTree // return to avoid setting the span to EmptyTree
41064156
}
41074157
}
41084158
}
41094159

4160+
private def concreteCapsType(refs: List[Tree]): Tree =
4161+
makeRetaining(Select(scalaDot(nme.caps), tpnme.CapSet), refs, tpnme.retains)
4162+
41104163
/** TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef
41114164
* | [‘case’] ‘object’ ObjectDef
41124165
* | ‘enum’ EnumDef

‎compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
225225
case FormatInterpolationErrorID // errorNumber: 209
226226
case ValueClassCannotExtendAliasOfAnyValID // errorNumber: 210
227227
case MatchIsNotPartialFunctionID // errorNumber: 211
228+
case ExpectedCaptureBoundOrEqualsID // errorNumber: 212
228229

229230
def errorNumber = ordinal - 1
230231

‎compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1923,6 +1923,23 @@ class ExpectedTypeBoundOrEquals(found: Token)(using Context)
19231923
|"""
19241924
}
19251925

1926+
class ExpectedCaptureBoundOrEquals(found: Token)(using Context)
1927+
extends SyntaxMsg(ExpectedCaptureBoundOrEqualsID) {
1928+
def msg(using Context) = i"${hl("=")}, ${hl(">:")}, or ${hl("<:")} expected, but ${Tokens.showToken(found)} found"
1929+
1930+
def explain(using Context) =
1931+
i"""Capture parameters and abstract captures may be constrained by a capture bound.
1932+
|Such capture bounds limit the concrete values of the capture variables and possibly
1933+
|reveal more information about the members of such captures.
1934+
|
1935+
|A lower type bound ${hl("B >: A")} expresses that the capture variable ${hl("B")}
1936+
|refers to a super capture of capture ${hl("A")}.
1937+
|
1938+
|An upper capture bound ${hl("T <: A")} declares that capture variable ${hl("T")}
1939+
|refers to a subcapture of ${hl("A")}.
1940+
|"""
1941+
}
1942+
19261943
class ClassAndCompanionNameClash(cls: Symbol, other: Symbol)(using Context)
19271944
extends NamingMsg(ClassAndCompanionNameClashID) {
19281945
def msg(using Context) =

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

Lines changed: 1 addition & 0 deletions
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.*

‎tests/neg-custom-args/captures/capset-bound.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ class IO
55
case class File(io: IO^)
66

77
def test(io1: IO^, io2: IO^) =
8-
def f[C >: CapSet^{io1} <: CapSet^](file: File^{C}) = ???
8+
def f[C^ >: {io1}](file: File^{C}) = ???
99
val f1: File^{io1} = ???
1010
val f2: File^{io2} = ???
1111
val f3: File^{io1, io2} = ???
12-
f[CapSet^{io1}](f1)
13-
f[CapSet^{io1}](f2) // error
14-
f[CapSet^{io1}](f3) // error
15-
f[CapSet^{io2}](f2) // error
16-
f[CapSet^{io1, io2}](f1)
17-
f[CapSet^{io1, io2}](f2)
18-
f[CapSet^{io1, io2}](f3)
12+
f[{io1}](f1)
13+
f[{io1}](f2) // error
14+
f[{io1}](f3) // error
15+
f[{io2}](f2) // error
16+
f[{io1, io2}](f1)
17+
f[{io1, io2}](f2)
18+
f[{io1, io2}](f3)

‎tests/neg-custom-args/captures/capset-bound2.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ class IO
55
def f[C^](io: IO^{C}) = ???
66

77
def test =
8-
f[CapSet](???)
9-
f[CapSet^{}](???)
10-
f[CapSet^](???)
8+
f[{}](???)
9+
f[{}](???)
10+
f[{cap}](???)
1111
f[Nothing](???) // error
1212
f[String](???) // error
Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,30 @@
1+
import language.experimental.captureChecking
12
import caps.*
23

34
trait Abstract[X^]:
4-
type C >: X <: CapSet^
5+
type C^ >: {X}
56
// Don't test the return type using Unit, because it is a pure type.
67
def boom(): AnyRef^{C}
78

8-
class Concrete extends Abstract[CapSet^{}]:
9-
type C = CapSet^{}
9+
class Concrete extends Abstract[{}]:
10+
type C^ = {}
1011
// TODO: Why do we get error without the return type here?
1112
def boom(): AnyRef = new Object
1213

13-
class Concrete2 extends Abstract[CapSet^{}]:
14-
type C = CapSet^{}
14+
class Concrete2 extends Abstract[{}]:
15+
type C^ = {}
1516
def boom(): AnyRef^ = new Object // error
1617

17-
class Concrete3 extends Abstract[CapSet^{}]:
18+
class Concrete3 extends Abstract[{}]:
1819
def boom(): AnyRef = new Object
1920

20-
class Concrete4(a: AnyRef^) extends Abstract[CapSet^{a}]:
21-
type C = CapSet // error
21+
class Concrete4(a: AnyRef^) extends Abstract[{a}]:
22+
type C^ = {} // error
2223
def boom(): AnyRef^{a} = a // error
2324

24-
class Concrete5(a: AnyRef^, b: AnyRef^) extends Abstract[CapSet^{a}]:
25-
type C = CapSet^{a}
25+
class Concrete5(a: AnyRef^, b: AnyRef^) extends Abstract[{a}]:
26+
type C^ = {a}
2627
def boom(): AnyRef^{b} = b // error
2728

28-
class Concrete6(a: AnyRef^, b: AnyRef^) extends Abstract[CapSet^{a}]:
29+
class Concrete6(a: AnyRef^, b: AnyRef^) extends Abstract[{a}]:
2930
def boom(): AnyRef^{b} = b // error

‎tests/neg-custom-args/captures/capture-poly.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import caps.*
33
trait Foo extends Capability
44

55
trait CaptureSet:
6-
type C >: CapSet <: CapSet^
6+
type C^
77

88
def capturePoly[C^](a: Foo^{C}): Foo^{C} = a
99
def capturePoly2(c: CaptureSet)(a: Foo^{c.C}): Foo^{c.C} = a
@@ -13,7 +13,7 @@ def test =
1313
val y: Foo^ = ???
1414

1515
object X extends CaptureSet:
16-
type C = CapSet^{x}
16+
type C = {x}
1717

1818
val z1: Foo^{X.C} = x
1919
val z2: Foo^{X.C} = y // error

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def test[C^] =
99

1010
// TODO: make "CapSet-ness" of type variables somehow contagious?
1111
// Then we don't have to spell out the bounds explicitly...
12-
def testTrans[C^, D >: CapSet <: C, E >: CapSet <: D, F >: C <: CapSet^] =
12+
def testTrans[C^, D^ <: C, E^ <: {D}, F^ >: C] =
1313
val d1: D = ???
1414
val d2: CapSet^{D} = d1
1515
val d3: D = d2
@@ -37,12 +37,12 @@ trait B[-C]
3737

3838
def testCong[C^, D^] =
3939
val a: A[C] = ???
40-
val b: A[CapSet^{C}] = a
41-
val c: A[CapSet^{D}] = a // error
42-
val d: A[CapSet^{C,D}] = a
40+
val b: A[{C}] = a
41+
val c: A[{D}] = a // error
42+
val d: A[{C,D}] = a
4343
val e: A[C] = d // error
4444
val f: B[C] = ???
45-
val g: B[CapSet^{C}] = f
45+
val g: B[{C}] = f
4646
val h: B[C] = g
47-
val i: B[CapSet^{C,D}] = h // error
47+
val i: B[{C,D}] = h // error
4848
val j: B[C] = i

‎tests/neg-custom-args/captures/capture-vars-subtyping2.scala

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ trait BoundsTest:
99
val b: Bar^ = ???
1010

1111
def testTransMixed[A^,
12-
B >: CapSet <: A,
13-
C >: CapSet <: CapSet^{B},
14-
D >: CapSet <: C,
15-
E >: CapSet <: CapSet^{D},
16-
F >: CapSet <: CapSet^{A,b},
17-
X >: CapSet <: CapSet^{F,D},
18-
Y >: CapSet^{F} <: CapSet^{F,A,b},
19-
Z >: CapSet^{b} <: CapSet^{b,Y}] =
12+
B^ <: {A},
13+
C^ <: {B},
14+
D^ <: C,
15+
E^ <: {D},
16+
F^ <: {A,b},
17+
X^ <: {F,D},
18+
Y^ >: {F} <: {F,A,b},
19+
Z^ >: {b} <: {b,Y}, T <: List[Bar^{Z}]] =
2020
val e: E = ???
2121
val e2: CapSet^{E} = e
2222
val ed: D = e
@@ -39,6 +39,6 @@ trait BoundsTest:
3939

4040
def callTransMixed =
4141
val x, y, z: Bar^ = ???
42-
testTransMixed[CapSet^{x,y,z}, CapSet^{x,y,z}, CapSet^{x,y,z}, CapSet^{x,y,z}, CapSet^{x,y,z}, CapSet^{x,y,z}, CapSet^{x,y,z}, CapSet^{x,y,z}, CapSet^{b,x,y,z}]
43-
testTransMixed[CapSet^{x,y,z}, CapSet^{x,y}, CapSet^{x,y}, CapSet^{x}, CapSet^{}, CapSet^{b,x}, CapSet^{b}, CapSet^{b,x}, CapSet^{b}]
44-
testTransMixed[CapSet^{x,y,z}, CapSet^{x,y}, CapSet^{x,y}, CapSet^{x}, CapSet^{}, CapSet^{b,x}, CapSet^{b}, CapSet^{b,x}, CapSet^{b,x,y,z}] // error
42+
testTransMixed[{x,y,z}, {x,y,z}, {x,y,z}, {x,y,z}, {x,y,z}, {x,y,z}, {x,y,z}, {x,y,z}, {b,x,y,z}, List[Bar^{b}]]
43+
testTransMixed[{x,y,z}, {x,y}, {x,y}, {x}, {}, {b,x}, {b}, {b,x}, {b}, List[Bar^{b}]]
44+
testTransMixed[{x,y,z}, {x,y}, {x,y}, {x}, {}, {b,x}, {b}, {b,x}, {b,x,y,z}, List[Bar^{}]] // error

‎tests/neg-custom-args/captures/cc-poly-source.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import caps.use
1717
def allListeners: Set[Listener^{X}] = listeners
1818

1919
def test1(lbl1: Label^, lbl2: Label^) =
20-
val src = Source[CapSet^{lbl1, lbl2}]
20+
val src = Source[{lbl1, lbl2}]
2121
def l1: Listener^{lbl1} = ???
2222
val l2: Listener^{lbl2} = ???
2323
src.register{l1}
@@ -31,7 +31,7 @@ import caps.use
3131
// we get an error here because we no longer allow contravariant cap
3232
// to subsume other capabilities. The problem can be solved by declaring
3333
// Label a SharedCapability, see cc-poly-source-capability.scala
34-
val src = Source[CapSet^{lbls*}]
34+
val src = Source[{lbls*}]
3535
for l <- listeners do
3636
src.register(l)
3737
val ls = src.allListeners
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import caps.CapSet
1+
import language.experimental.captureChecking
22

33
trait Async:
4-
def await[T, Cap^](using caps.Contains[Cap, this.type])(src: Source[T, Cap]^): T
4+
def await[T, Cap^](using caps.Contains[Cap, this.type])(src: Source[T, {Cap}]^): T
55

66
def foo(x: Async) = x.await(???) // error
77

88
trait Source[+T, Cap^]:
9-
final def await(using ac: Async^{Cap}) = ac.await[T, Cap](this) // Contains[Cap, ac] is assured because {ac} <: Cap.
9+
final def await(using ac: Async^{Cap}) = ac.await[T, {Cap}](this) // Contains[Cap, ac] is assured because {ac} <: Cap.
1010

1111
def test(using ac1: Async^, ac2: Async^, x: String) =
12-
val src1 = new Source[Int, CapSet^{ac1}] {}
12+
val src1 = new Source[Int, {ac1}] {}
1313
ac1.await(src1) // ok
14-
val src2 = new Source[Int, CapSet^{ac2}] {}
14+
val src2 = new Source[Int, {ac2}] {}
1515
ac1.await(src2) // error

‎tests/neg-custom-args/captures/i21868.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ trait AbstractWrong:
55
def f(): Unit^{C} // error
66

77
trait Abstract1:
8-
type C >: CapSet <: CapSet^
8+
type C^
99
def f(): Unit^{C}
1010

1111
// class Abstract2:
Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import language.experimental.captureChecking
12
import language.experimental.modularity
23
import caps.*
34

@@ -6,44 +7,44 @@ class IO
67
class File
78

89
trait Abstract:
9-
type C >: CapSet <: CapSet^
10+
type C^
1011
def f(file: File^{C}): Unit
1112

1213
class Concrete1 extends Abstract:
13-
type C = CapSet
14+
type C = {}
1415
def f(file: File) = ()
1516

1617
class Concrete2(io: IO^) extends Abstract:
17-
type C = CapSet^{io}
18+
type C = {io}
1819
def f(file: File^{io}) = ()
1920

2021
class Concrete3(io: IO^) extends Abstract:
21-
type C = CapSet^{io}
22+
type C = {io}
2223
def f(file: File) = () // error
2324

2425
trait Abstract2(tracked val io: IO^):
25-
type C >: CapSet <: CapSet^{io}
26+
type C^ <: {io}
2627
def f(file: File^{C}): Unit
2728

2829
class Concrete4(io: IO^) extends Abstract2(io):
29-
type C = CapSet
30+
type C = {}
3031
def f(file: File) = ()
3132

3233
class Concrete5(io1: IO^, io2: IO^) extends Abstract2(io1):
33-
type C = CapSet^{io2} // error
34+
type C = {io2} // error
3435
def f(file: File^{io2}) = ()
3536

3637
trait Abstract3[X^]:
37-
type C >: CapSet <: X
38+
type C^ <: X
3839
def f(file: File^{C}): Unit
3940

40-
class Concrete6(io: IO^) extends Abstract3[CapSet^{io}]:
41-
type C = CapSet
41+
class Concrete6(io: IO^) extends Abstract3[{io}]:
42+
type C = {}
4243
def f(file: File) = ()
4344

44-
class Concrete7(io1: IO^, io2: IO^) extends Abstract3[CapSet^{io1}]:
45-
type C = CapSet^{io2} // error
45+
class Concrete7(io1: IO^, io2: IO^) extends Abstract3[{io1}]:
46+
type C = {io2} // error
4647
def f(file: File^{io2}) = ()
4748

48-
class Concrete8(io1: IO^, io2: IO^) extends Abstract3[CapSet^{io1}]:
49+
class Concrete8(io1: IO^, io2: IO^) extends Abstract3[{io1}]:
4950
def f(file: File^{io2}) = () // error
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11

2+
import language.experimental.captureChecking
23
import caps.*
34

45
class IO
56
class File(io: IO^)
67

78
class Handler[C^]:
8-
def f(file: File^): File^{C^} = file // error
9-
def g(@consume file: File^{C^}): File^ = file // ok
9+
def f(file: File^): File^{C} = file // error
10+
def g(@consume file: File^{C}): File^ = file // ok

‎tests/neg-custom-args/captures/use-capset.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ private def g2[@use C^] = (xs: List[Object^{C}]) => xs.head // ok
99
def test(io: Object^)(@use xs: List[Object^{io}]): Unit =
1010
val h = () => f(xs)
1111
val _: () -> Unit = h // error: should be ->{io}
12-
val h2 = () => g[CapSet^{io}]
12+
val h2 = () => g[{io}]
1313
val _: () -> List[Object^{io}] -> Object^{io} = h2 // error, should be ->{io}
1414

‎tests/pos-custom-args/captures/cap-paramlist8-desugared.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@
1212
}
1313
val baz3:
1414
Int -> [C >: caps.CapSet <: caps.CapSet^,
15-
D >: caps.CapSet <: caps.CapSet^{C^}, E >: caps.CapSet <:
16-
caps.CapSet^{C^, x}] => () -> [F >: caps.CapSet^{x, y} <:
17-
caps.CapSet^{C^, E^}] => (x: Int) -> (Ctx[F]) ?-> Int
15+
D >: caps.CapSet <: caps.CapSet^{C}, E >: caps.CapSet <:
16+
caps.CapSet^{C, x}] => () -> [F >: caps.CapSet^{x, y} <:
17+
caps.CapSet^{C, E}] => (x: Int) -> (Ctx[F]) ?-> Int
1818
= (i: Int) => [
1919
C >: _root_.scala.caps.CapSet <: _root_.scala.caps.CapSet^{cap},
20-
D >: _root_.scala.caps.CapSet <: _root_.scala.caps.CapSet^{C^},
21-
E >: _root_.scala.caps.CapSet <: _root_.scala.caps.CapSet^{C^, x}] =>
20+
D >: _root_.scala.caps.CapSet <: _root_.scala.caps.CapSet^{C},
21+
E >: _root_.scala.caps.CapSet <: _root_.scala.caps.CapSet^{C, x}] =>
2222
() => [
2323
F
2424
>: _root_.scala.caps.CapSet^{x, y} <:
25-
_root_.scala.caps.CapSet^{C^, E^}
25+
_root_.scala.caps.CapSet^{C, E}
2626
] => (x: Int) => (ev: Ctx[F]) ?=> 1
2727
()
2828
}

‎tests/pos-custom-args/captures/cc-poly-1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import caps.{CapSet, Capability}
1313

1414
def test(c1: C, c2: C) =
1515
val d: D^{c1, c2} = D()
16-
val x = f[CapSet^{c1, c2}](d)
16+
val x = f[{c1, c2}](d)
1717
val _: D^{c1, c2} = x
1818
val d1: D^{c1} = D()
1919
val d2: D^{c2} = D()

‎tests/pos-custom-args/captures/cc-poly-source-capability.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import caps.use
1919
def allListeners: Set[Listener^{X}] = listeners
2020

2121
def test1(async1: Async, @use others: List[Async]) =
22-
val src = Source[CapSet^{async1, others*}]
22+
val src = Source[{async1, others*}]
2323
val _: Set[Listener^{async1, others*}] = src.allListeners
2424
val lst1 = listener(async1)
2525
val lsts = others.map(listener)
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
abstract class Source[+T, Cap^]
22

3-
extension[T, Cap^](src: Source[T, Cap]^)
4-
def transformValuesWith[U](f: (T -> U)^{Cap}): Source[U, Cap]^{src, f} = ???
3+
extension[T, Cap^](src: Source[T, {Cap}]^)
4+
def transformValuesWith[U](f: (T -> U)^{Cap}): Source[U, {Cap}]^{src, f} = ???
55

6-
def race[T, Cap^](sources: Source[T, Cap]^{Cap}*): Source[T, Cap]^{Cap} = ???
6+
def race[T, Cap^](sources: Source[T, {Cap}]^{Cap}*): Source[T, {Cap}]^{Cap} = ???
77

88
def either[T1, T2, Cap^](
9-
src1: Source[T1, Cap]^{Cap},
10-
src2: Source[T2, Cap]^{Cap}): Source[Either[T1, T2], Cap]^{Cap} =
9+
src1: Source[T1, {Cap}]^{Cap},
10+
src2: Source[T2, {Cap}]^{Cap}): Source[Either[T1, T2], {Cap}]^{Cap} =
1111
val left = src1.transformValuesWith(Left(_))
1212
val right = src2.transformValuesWith(Right(_))
1313
race(left, right)

‎tests/pos-custom-args/captures/gears-problem-poly.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ extension [T](@use fs: Seq[Future[T]^])
2626
def awaitAll = fs.awaitAllPoly
2727

2828
def awaitExplicit[T](@use fs: Seq[Future[T]^]): Unit =
29-
awaitAllPoly[T, CapSet^{fs*}](fs)
29+
awaitAllPoly[T, {fs*}](fs)
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
import caps.CapSet
22

33
trait Async:
4-
def await[T, Cap^](using caps.Contains[Cap, this.type])(src: Source[T, Cap]^): T =
4+
def await[T, Cap^](using caps.Contains[Cap, this.type])(src: Source[T, {Cap}]^): T =
5+
// FIXME: this is an irregularity: it works if we write caps.Contains[Cap, this.type], but we should expect to write caps.Contains[{Cap}, this.type]!
56
val x: Async^{this} = ???
67
val y: Async^{Cap} = x
78
val ac: Async^ = ???
8-
def f(using caps.Contains[Cap, ac.type]) =
9+
def f(using caps.Contains[Cap, ac.type]) = // FIXME dito
910
val x2: Async^{this} = ???
1011
val y2: Async^{Cap} = x2
1112
val x3: Async^{ac} = ???
1213
val y3: Async^{Cap} = x3
1314
???
1415

1516
trait Source[+T, Cap^]:
16-
final def await(using ac: Async^{Cap}) = ac.await[T, Cap](this) // Contains[Cap, ac] is assured because {ac} <: Cap.
17+
final def await(using ac: Async^{Cap}) = ac.await[T, {Cap}](this) // Contains[Cap, ac] is assured because {ac} <: Cap.
1718

1819
def test(using ac1: Async^, ac2: Async^, x: String) =
19-
val src1 = new Source[Int, CapSet^{ac1}] {}
20+
val src1 = new Source[Int, {ac1}] {}
2021
ac1.await(src1)

‎tests/pos-custom-args/captures/i21347.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import language.experimental.captureChecking
44

55
class Box[Cap^] {}
66

7-
def run[Cap^](f: Box[Cap]^{Cap} => Unit): Box[Cap]^{Cap} = ???
7+
def run[Cap^](f: Box[Cap]^{Cap} => Unit): Box[{Cap}]^{Cap} = ???
88

99
def main() =
1010
val b = run(_ => ())

‎tests/pos-custom-args/captures/i21507.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import language.experimental.captureChecking
33
trait Box[Cap^]:
44
def store(f: (() -> Unit)^{Cap}): Unit
55

6-
def run[Cap^](f: Box[Cap]^{Cap} => Unit): Box[Cap]^{Cap} =
7-
new Box[Cap]:
6+
def run[Cap^](f: Box[{Cap}]^{Cap} => Unit): Box[{Cap}]^{Cap} =
7+
new Box[{Cap}]:
88
private var item: () ->{Cap} Unit = () => ()
99
def store(f: () ->{Cap} Unit): Unit =
1010
item = f // was error, now ok

‎tests/pos-custom-args/captures/polycap.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import language.experimental.captureChecking
22

33
class Source[+T, Cap^]
44

5-
def completed[T, Cap^](result: T): Source[T, Cap] =
5+
def completed[T, Cap^](result: T): Source[T, {Cap}] =
66
//val fut = new Source[T, Cap]()
7-
val fut2 = new Source[T, Cap]()
8-
fut2: Source[T, Cap]
7+
val fut2 = new Source[T, {Cap}]()
8+
fut2: Source[T, {Cap}]
99

1010

1111

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// a.scala
22
import language.experimental.captureChecking
3-
import scala.caps.CapSet
43

54
trait A:
65
def f[C^](x: AnyRef^{C}): Unit
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import language.experimental.captureChecking
2-
import scala.caps.CapSet
32

43
class B extends A:
54
def f[C^](x: AnyRef^{C}): Unit = ???
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import language.experimental.captureChecking
2-
import scala.caps.CapSet
32

43
class B extends A:
54
def f[C^](x: AnyRef^{C}): Unit = ???

0 commit comments

Comments
 (0)
Please sign in to comment.