After various tests, it doesn’t appear to make a difference whether I use the //>… bits or the command line you suggested. However, I think I made a mistake when testing the nightly this morning.
Trying again tonight:
- with 3.8.0-RC3: the problem is still the same.
- with 3.nightly, I get a different problem.
To remove potential ambiguities, here’s the exact code I run:
import caps.*
trait Rand extends SharedCapability:
def range(min: Int, max: Int): Int
object Rand:
def nextInt(max: Int): Rand ?-> Int =
r ?=> r.range(0, max)
def oneOf[A](head: Rand ?=> A, tail: (Rand ?=> A)*): Rand ?->{head, tail*} A =
val all: Seq[Rand ?->{head, tail*} A] = head +: tail
all(nextInt(all.length))
This is invoked with:
scala --scala-version 3.nightly -language:experimental.captureChecking rep.scala
The error I get is:
Compiling project (Scala 3.8.1-RC1-bin-20251229-e73ff2c-NIGHTLY, JVM (21))
[error] ./rep.scala:14:5
[error] Local reach capability tail* leaks into capture scope of method oneOf.
[error] You could try to abstract the capabilities referred to by tail* in a capset variable.
[error] all(nextInt(all.length))
[error] ^^^^^^^^^^^^^^^^^^^^^^^^
This is a little confusing, because if I remove the tail* bit of the capture set of oneOf, I get:
Compiling project (Scala 3.8.1-RC1-bin-20251229-e73ff2c-NIGHTLY, JVM (21))
[error] ./rep.scala:12:5
[error] Found: (contextual$1: Rand^'s1) ?->{head, tail*} A^'s2
[error] Required: (Rand^) ?->{head} A
[error]
[error] Note that capability tail* is not included in capture set {head}.
[error]
[error] where: ^ refers to the universal root capability
Which feels a little like the compiler is contracting itself - if I don’t include tail*, it complains it’s not there. If I do include it, it complains it’s leaking (which, I’m not entirely sure what that means here).
Is there anything I can do to help decide whether this is a bug or slightly confusing error messages? I’m sure that test case can be reduced a little more, for example.