Capture leak when using varargs?

I’m writing the following code, where the interesting bit is in the capture set of oneOf.

//> using scala 3.nightly
//> using option -language:experimental.captureChecking -nowarn

import caps.*

trait Rand extends SharedCapability:
  def range(min: Int, max: Int): Int

def nextInt(max: Int): Rand ?-> Int =
  r ?=> r.range(0, max)

def oneOf[A](head: Rand ?=> A, tail: (Rand ?=> A)*): Rand ?->{head} A =
  val all: Seq[Rand ?->{head, tail*} A] = head +: tail

  all(nextInt(all.length))

Instinctively, I would expect oneOf to return Rand ?→{head, tail*} A, and am a little weirded out that the above code compiles. Isn’t this forgetting the fact that whatever is contained by tail is tracked?

I do need all to explicitly capture {head, tail*}, so my expectations seem at list a little reasonable…

1 Like

That’s indeed a bug in the implementation. In fact you could also have declaredoneOf to return a pure function Rand → A and this would have typechecked also, which is even more wrong than what you showed. Fix is forthcoming.

2 Likes

There’s now a PR that should fix this: Fix apply rule by odersky · Pull Request #24273 · scala/scala3 · GitHub

1 Like

Thank you so much - and sorry I keep running in all these corner cases…

1 Like

You should not be sorry at all! It’s a big help for us that you find these corner cases.

2 Likes