diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index e8e3646bd087..b91c7a712f4c 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -527,10 +527,14 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def makeCapsOf(tp: RefTree)(using Context): Tree = TypeApply(Select(scalaDot(nme.caps), nme.capsOf), tp :: Nil) - def makeCapsBound()(using Context): Tree = - makeRetaining( + // `type C^` and `[C^]` becomes: + // `type C >: CapSet <: CapSet^{cap}` and `[C >: CapSet <: CapSet^{cap}]` + def makeCapsBound()(using Context): TypeBoundsTree = + TypeBoundsTree( Select(scalaDot(nme.caps), tpnme.CapSet), - Nil, tpnme.retainsCap) + makeRetaining( + Select(scalaDot(nme.caps), tpnme.CapSet), + Nil, tpnme.retainsCap)) def makeConstructor(tparams: List[TypeDef], vparamss: List[List[ValDef]], rhs: Tree = EmptyTree)(using Context): DefDef = DefDef(nme.CONSTRUCTOR, joinParams(tparams, vparamss), TypeTree(), rhs) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 2e441553689c..0ab82e7a0a10 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2240,7 +2240,7 @@ object Parsers { atSpan(in.offset): if in.isIdent(nme.UPARROW) && Feature.ccEnabled then in.nextToken() - TypeBoundsTree(EmptyTree, makeCapsBound()) + makeCapsBound() else TypeBoundsTree(bound(SUPERTYPE), bound(SUBTYPE)) @@ -4057,8 +4057,11 @@ object Parsers { || sourceVersion.isAtLeast(`3.6`) && in.isColon => makeTypeDef(typeAndCtxBounds(tname)) case _ => - syntaxErrorOrIncomplete(ExpectedTypeBoundOrEquals(in.token)) - return EmptyTree // return to avoid setting the span to EmptyTree + if in.isIdent(nme.UPARROW) && Feature.ccEnabled then + makeTypeDef(typeAndCtxBounds(tname)) + else + syntaxErrorOrIncomplete(ExpectedTypeBoundOrEquals(in.token)) + return EmptyTree // return to avoid setting the span to EmptyTree } } } diff --git a/tests/neg-custom-args/captures/capset-bound2.scala b/tests/neg-custom-args/captures/capset-bound2.scala new file mode 100644 index 000000000000..679606f0e43c --- /dev/null +++ b/tests/neg-custom-args/captures/capset-bound2.scala @@ -0,0 +1,13 @@ +import caps.* + +class IO + +def f[C^](io: IO^{C^}) = ??? + +def test = + f[CapSet](???) + f[CapSet^{}](???) + f[CapSet^](???) + f[Nothing](???) // error + f[String](???) // error + \ No newline at end of file diff --git a/tests/neg-custom-args/captures/capset-members.scala b/tests/neg-custom-args/captures/capset-members.scala new file mode 100644 index 000000000000..bcc92835b95f --- /dev/null +++ b/tests/neg-custom-args/captures/capset-members.scala @@ -0,0 +1,21 @@ +import caps.* + +trait Abstract[X^]: + type C >: X <: CapSet^ + def boom(): Unit^{C^} + +class Concrete extends Abstract[CapSet^{}]: + type C = CapSet^{} + def boom() = () + +class Concrete2 extends Abstract[CapSet^{}]: + type C = CapSet^{} & CapSet^{} + def boom() = () + +class Concrete3 extends Abstract[CapSet^{}]: + type C = CapSet^{} | CapSet^{} + def boom() = () + +class Concrete4 extends Abstract[CapSet^{}]: + type C = Nothing // error + def boom() = ()