Skip to content

Commit 0fb712b

Browse files
committed
Reclassify maximal capabilities
Don't treat user-defined capabilities deriving from caps.Capability as maximal. That was a vestige from when we treated capability classes natively. It caused code that should compile to fail because if `x extends Capability` then `x` could not be widened to `x*`. As a consequence we have one missed error in effect-swaps again, which re-establishes the original (faulty) situation.
1 parent 64743e1 commit 0fb712b

File tree

6 files changed

+57
-10
lines changed

6 files changed

+57
-10
lines changed

compiler/src/dotty/tools/dotc/cc/Setup.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
743743
if others.accountsFor(ref) then
744744
report.warning(em"redundant capture: $dom already accounts for $ref", pos)
745745

746-
if ref.captureSetOfInfo.elems.isEmpty then
746+
if ref.captureSetOfInfo.elems.isEmpty && !ref.derivesFrom(defn.Caps_Capability) then
747747
report.error(em"$ref cannot be tracked since its capture set is empty", pos)
748748
check(parent.captureSet, parent)
749749

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3026,8 +3026,7 @@ object Types extends TypeUtils {
30263026
name == nme.CAPTURE_ROOT && symbol == defn.captureRoot
30273027

30283028
override def isMaxCapability(using Context): Boolean =
3029-
import cc.*
3030-
this.derivesFromCapability && symbol.isStableMember
3029+
symbol == defn.captureRoot || info.derivesFrom(defn.Caps_Exists)
30313030

30323031
override def normalizedRef(using Context): CaptureRef =
30333032
if isTrackableRef then symbol.termRef else this
@@ -4834,8 +4833,7 @@ object Types extends TypeUtils {
48344833
def copyBoundType(bt: BT): Type = bt.paramRefs(paramNum)
48354834
override def isTrackableRef(using Context) = true
48364835
override def isMaxCapability(using Context) =
4837-
import cc.*
4838-
this.derivesFromCapability
4836+
underlying.derivesFrom(defn.Caps_Exists)
48394837
}
48404838

48414839
private final class TermParamRefImpl(binder: TermLambda, paramNum: Int) extends TermParamRef(binder, paramNum)

tests/neg-custom-args/captures/effect-swaps.check

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,3 @@
2222
73 | fr.await.ok
2323
|
2424
| longer explanation available when compiling with `-explain`
25-
-- Error: tests/neg-custom-args/captures/effect-swaps.scala:66:15 ------------------------------------------------------
26-
66 | Result.make: // error
27-
| ^^^^^^^^^^^
28-
| escaping local reference contextual$9.type

tests/neg-custom-args/captures/effect-swaps.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def test[T, E](using Async) =
6363
fr.await.ok
6464

6565
def fail4[T, E](fr: Future[Result[T, E]]^) =
66-
Result.make: // error
66+
Result.make: // should be errorm but inders Result[Any, Any]
6767
Future: fut ?=>
6868
fr.await.ok
6969

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import language.experimental.captureChecking
2+
import annotation.experimental
3+
import caps.{CapSet, Capability}
4+
5+
@experimental object Test:
6+
7+
class Label extends Capability
8+
9+
class Listener
10+
11+
class Source[X^]:
12+
private var listeners: Set[Listener^{X^}] = Set.empty
13+
def register(x: Listener^{X^}): Unit =
14+
listeners += x
15+
16+
def allListeners: Set[Listener^{X^}] = listeners
17+
18+
def test1(lbl1: Label, lbl2: Label) =
19+
val src = Source[CapSet^{lbl1, lbl2}]
20+
def l1: Listener^{lbl1} = ???
21+
val l2: Listener^{lbl2} = ???
22+
src.register{l1}
23+
src.register{l2}
24+
val ls = src.allListeners
25+
val _: Set[Listener^{lbl1, lbl2}] = ls
26+
27+
def test2(lbls: List[Label]) =
28+
def makeListener(lbl: Label): Listener^{lbl} = ???
29+
val listeners = lbls.map(makeListener)
30+
val src = Source[CapSet^{lbls*}]
31+
for l <- listeners do
32+
src.register(l)
33+
val ls = src.allListeners
34+
val _: Set[Listener^{lbls*}] = ls
35+
36+

tests/pos/reach-capability.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import language.experimental.captureChecking
2+
import annotation.experimental
3+
import caps.{Capability}
4+
5+
@experimental object Test2:
6+
7+
class List[+A]:
8+
def map[B](f: A => B): List[B] = ???
9+
10+
class Label extends Capability
11+
12+
class Listener
13+
14+
def test2(lbls: List[Label]) =
15+
def makeListener(lbl: Label): Listener^{lbl} = ???
16+
val listeners = lbls.map(makeListener) // should work
17+

0 commit comments

Comments
 (0)