Skip to content

Commit

Permalink
Reclassify maximal capabilities
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
odersky committed Jun 27, 2024
1 parent 60b0486 commit 38cf302
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 10 deletions.
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/cc/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
if others.accountsFor(ref) then
report.warning(em"redundant capture: $dom already accounts for $ref", pos)

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

Expand Down
6 changes: 2 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3027,8 +3027,7 @@ object Types extends TypeUtils {
name == nme.CAPTURE_ROOT && symbol == defn.captureRoot

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

override def normalizedRef(using Context): CaptureRef =
if isTrackableRef then symbol.termRef else this
Expand Down Expand Up @@ -4839,8 +4838,7 @@ object Types extends TypeUtils {
def copyBoundType(bt: BT): Type = bt.paramRefs(paramNum)
override def isTrackableRef(using Context) = true
override def isMaxCapability(using Context) =
import cc.*
this.derivesFromCapability
underlying.derivesFrom(defn.Caps_Exists)
}

private final class TermParamRefImpl(binder: TermLambda, paramNum: Int) extends TermParamRef(binder, paramNum)
Expand Down
4 changes: 0 additions & 4 deletions tests/neg-custom-args/captures/effect-swaps.check
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,3 @@
73 | fr.await.ok
|
| longer explanation available when compiling with `-explain`
-- Error: tests/neg-custom-args/captures/effect-swaps.scala:66:15 ------------------------------------------------------
66 | Result.make: // error
| ^^^^^^^^^^^
| escaping local reference contextual$9.type
2 changes: 1 addition & 1 deletion tests/neg-custom-args/captures/effect-swaps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def test[T, E](using Async) =
fr.await.ok

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

Expand Down
36 changes: 36 additions & 0 deletions tests/pos/cc-poly-source-capability.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import language.experimental.captureChecking
import annotation.experimental
import caps.{CapSet, Capability}

@experimental object Test:

class Label extends Capability

class Listener

class Source[X^]:
private var listeners: Set[Listener^{X^}] = Set.empty
def register(x: Listener^{X^}): Unit =
listeners += x

def allListeners: Set[Listener^{X^}] = listeners

def test1(lbl1: Label, lbl2: Label) =
val src = Source[CapSet^{lbl1, lbl2}]
def l1: Listener^{lbl1} = ???
val l2: Listener^{lbl2} = ???
src.register{l1}
src.register{l2}
val ls = src.allListeners
val _: Set[Listener^{lbl1, lbl2}] = ls

def test2(lbls: List[Label]) =
def makeListener(lbl: Label): Listener^{lbl} = ???
val listeners = lbls.map(makeListener)
val src = Source[CapSet^{lbls*}]
for l <- listeners do
src.register(l)
val ls = src.allListeners
val _: Set[Listener^{lbls*}] = ls


17 changes: 17 additions & 0 deletions tests/pos/reach-capability.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import language.experimental.captureChecking
import annotation.experimental
import caps.{Capability}

@experimental object Test2:

class List[+A]:
def map[B](f: A => B): List[B] = ???

class Label extends Capability

class Listener

def test2(lbls: List[Label]) =
def makeListener(lbl: Label): Listener^{lbl} = ???
val listeners = lbls.map(makeListener) // should work

0 comments on commit 38cf302

Please sign in to comment.