diff --git a/scala2-library-cc/src/scala/collection/mutable/Buffer.scala b/scala2-library-cc/src/scala/collection/mutable/Buffer.scala index f9aa9cf28c72..3ff614bfc556 100644 --- a/scala2-library-cc/src/scala/collection/mutable/Buffer.scala +++ b/scala2-library-cc/src/scala/collection/mutable/Buffer.scala @@ -15,7 +15,7 @@ package mutable import scala.annotation.nowarn import language.experimental.captureChecking - +import caps.unboxed /** A `Buffer` is a growable and shrinkable `Seq`. */ trait Buffer[A] @@ -180,7 +180,7 @@ trait IndexedBuffer[A] extends IndexedSeq[A] override def iterableFactory: SeqFactory[IndexedBuffer] = IndexedBuffer - def flatMapInPlace(f: A => IterableOnce[A]^): this.type = { + def flatMapInPlace(@unboxed f: A => IterableOnce[A]^): this.type = { // There's scope for a better implementation which copies elements in place. var i = 0 val s = size diff --git a/tests/neg-custom-args/captures/i15749a.scala b/tests/neg-custom-args/captures/i15749a.scala index 0158928f4e39..109a73b2b130 100644 --- a/tests/neg-custom-args/captures/i15749a.scala +++ b/tests/neg-custom-args/captures/i15749a.scala @@ -1,4 +1,6 @@ import caps.cap +import caps.unboxed + class Unit object u extends Unit @@ -16,7 +18,7 @@ def test = def force[A](thunk: Unit ->{cap} A): A = thunk(u) - def forceWrapper[A](mx: Wrapper[Unit ->{cap} A]): Wrapper[A] = + def forceWrapper[A](@unboxed mx: Wrapper[Unit ->{cap} A]): Wrapper[A] = // Γ ⊢ mx: Wrapper[□ {cap} Unit => A] // `force` should be typed as ∀(□ {cap} Unit -> A) A, but it can not strictMap[Unit ->{mx*} A, A](mx)(t => force[A](t)) // error // should work diff --git a/tests/neg-custom-args/captures/reaches.check b/tests/neg-custom-args/captures/reaches.check index f20dbdf311ad..6fdbdefea206 100644 --- a/tests/neg-custom-args/captures/reaches.check +++ b/tests/neg-custom-args/captures/reaches.check @@ -1,12 +1,12 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:23:11 -------------------------------------- -23 | cur = (() => f.write()) :: Nil // error since {f*} !<: {xs*} +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:24:11 -------------------------------------- +24 | cur = (() => f.write()) :: Nil // error since {f*} !<: {xs*} | ^^^^^^^^^^^^^^^^^^^^^^^ | Found: List[box () ->{f} Unit] | Required: List[box () ->{xs*} Unit] | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:34:7 --------------------------------------- -34 | (() => f.write()) :: Nil // error since {f*} !<: {xs*} +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:35:7 --------------------------------------- +35 | (() => f.write()) :: Nil // error since {f*} !<: {xs*} | ^^^^^^^^^^^^^^^^^^^^^^^ | Found: List[box () ->{f} Unit] | Required: box List[box () ->{xs*} Unit]^? @@ -15,34 +15,42 @@ | cannot be included in outer capture set {xs*} of value cur | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/reaches.scala:37:6 ------------------------------------------------------------ -37 | var cur: List[Proc] = xs // error: Illegal type for var +-- Error: tests/neg-custom-args/captures/reaches.scala:38:6 ------------------------------------------------------------ +38 | var cur: List[Proc] = xs // error: Illegal type for var | ^ | Mutable variable cur cannot have type List[box () => Unit] since | the part box () => Unit of that type captures the root capability `cap`. --- Error: tests/neg-custom-args/captures/reaches.scala:44:15 ----------------------------------------------------------- -44 | val cur = Ref[List[Proc]](xs) // error: illegal type for type argument to Ref +-- Error: tests/neg-custom-args/captures/reaches.scala:45:15 ----------------------------------------------------------- +45 | val cur = Ref[List[Proc]](xs) // error: illegal type for type argument to Ref | ^^^^^^^^^^^^^^^ | Sealed type variable T cannot be instantiated to List[box () => Unit] since | the part box () => Unit of that type captures the root capability `cap`. | This is often caused by a local capability in an argument of constructor Ref | leaking as part of its result. --- Error: tests/neg-custom-args/captures/reaches.scala:54:31 ----------------------------------------------------------- -54 | val id: Id[Proc, Proc] = new Id[Proc, () -> Unit] // error +-- Error: tests/neg-custom-args/captures/reaches.scala:55:31 ----------------------------------------------------------- +55 | val id: Id[Proc, Proc] = new Id[Proc, () -> Unit] // error | ^^^^^^^^^^^^^^^^^^^^ | Sealed type variable A cannot be instantiated to box () => Unit since | that type captures the root capability `cap`. | This is often caused by a local capability in an argument of constructor Id | leaking as part of its result. --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:63:27 -------------------------------------- -63 | val f1: File^{id*} = id(f) // error, since now id(f): File^ +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:64:27 -------------------------------------- +64 | val f1: File^{id*} = id(f) // error, since now id(f): File^ | ^^^^^ | Found: File^{id, f} | Required: File^{id*} | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/reaches.scala:80:5 ------------------------------------------------------------ -80 | ps.map((x, y) => compose1(x, y)) // error: cannot mix cap and * (should work now) +-- Error: tests/neg-custom-args/captures/reaches.scala:81:5 ------------------------------------------------------------ +81 | ps.map((x, y) => compose1(x, y)) // error: cannot mix cap and * (should work now) // error // error | ^^^^^^ | Reach capability cap and universal capability cap cannot both | appear in the type [B](f: ((box A ->{ps*} A, box A ->{ps*} A)) => B): List[B] of this expression +-- Error: tests/neg-custom-args/captures/reaches.scala:81:10 ----------------------------------------------------------- +81 | ps.map((x, y) => compose1(x, y)) // error: cannot mix cap and * (should work now) // error // error + | ^ + | Local reach capability ps* leaks into capture scope of method mapCompose +-- Error: tests/neg-custom-args/captures/reaches.scala:81:13 ----------------------------------------------------------- +81 | ps.map((x, y) => compose1(x, y)) // error: cannot mix cap and * (should work now) // error // error + | ^ + | Local reach capability ps* leaks into capture scope of method mapCompose diff --git a/tests/neg-custom-args/captures/reaches.scala b/tests/neg-custom-args/captures/reaches.scala index eadb76c69e5b..f3b4e532e1a2 100644 --- a/tests/neg-custom-args/captures/reaches.scala +++ b/tests/neg-custom-args/captures/reaches.scala @@ -1,5 +1,6 @@ //> using options -source 3.4 // (to make sure we use the sealed policy) +import caps.unboxed class File: def write(): Unit = ??? @@ -12,7 +13,7 @@ class Ref[T](init: T): def get: T = x def set(y: T) = { x = y } -def runAll0(xs: List[Proc]): Unit = +def runAll0(@unboxed xs: List[Proc]): Unit = var cur: List[() ->{xs*} Unit] = xs // OK, by revised VAR while cur.nonEmpty do val next: () ->{xs*} Unit = cur.head @@ -22,7 +23,7 @@ def runAll0(xs: List[Proc]): Unit = usingFile: f => cur = (() => f.write()) :: Nil // error since {f*} !<: {xs*} -def runAll1(xs: List[Proc]): Unit = +def runAll1(@unboxed xs: List[Proc]): Unit = val cur = Ref[List[() ->{xs*} Unit]](xs) // OK, by revised VAR while cur.get.nonEmpty do val next: () ->{xs*} Unit = cur.get.head @@ -77,4 +78,4 @@ def compose1[A, B, C](f: A => B, g: B => C): A ->{f, g} C = z => g(f(z)) def mapCompose[A](ps: List[(A => A, A => A)]): List[A ->{ps*} A] = - ps.map((x, y) => compose1(x, y)) // error: cannot mix cap and * (should work now) + ps.map((x, y) => compose1(x, y)) // error: cannot mix cap and * (should work now) // error // error diff --git a/tests/neg-custom-args/captures/widen-reach.check b/tests/neg-custom-args/captures/widen-reach.check index dbe811ab99ec..06d21ff445d8 100644 --- a/tests/neg-custom-args/captures/widen-reach.check +++ b/tests/neg-custom-args/captures/widen-reach.check @@ -1,17 +1,11 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/widen-reach.scala:13:26 ---------------------------------- +-- Error: tests/neg-custom-args/captures/widen-reach.scala:13:26 ------------------------------------------------------- 13 | val y2: IO^ -> IO^ = y1.foo // error | ^^^^^^ - | Found: IO^ ->{x*} IO^{x*} - | Required: IO^ -> (ex$6: caps.Exists) -> IO^{ex$6} - | - | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/widen-reach.scala:14:30 ---------------------------------- + | Local reach capability x* leaks into capture scope of method test +-- Error: tests/neg-custom-args/captures/widen-reach.scala:14:30 ------------------------------------------------------- 14 | val y3: IO^ -> IO^{x*} = y1.foo // error | ^^^^^^ - | Found: IO^ ->{x*} IO^{x*} - | Required: IO^ -> IO^{x*} - | - | longer explanation available when compiling with `-explain` + | Local reach capability x* leaks into capture scope of method test -- [E164] Declaration Error: tests/neg-custom-args/captures/widen-reach.scala:9:6 -------------------------------------- 9 | val foo: IO^ -> IO^ = x => x // error | ^ diff --git a/tests/neg/i20503.scala b/tests/neg/i20503.scala index e8770b934ad1..463e4e3f9686 100644 --- a/tests/neg/i20503.scala +++ b/tests/neg/i20503.scala @@ -1,4 +1,5 @@ import language.experimental.captureChecking +import caps.unboxed class List[+A]: def head: A = ??? @@ -7,10 +8,11 @@ class List[+A]: def foreach[U](f: A => U): Unit = ??? def nonEmpty: Boolean = ??? -def runOps(ops: List[() => Unit]): Unit = +def runOps(@unboxed ops: List[() => Unit]): Unit = // See i20156, due to limitation in expressiveness of current system, // we could map over the list of impure elements. OK with existentials. ops.foreach(op => op()) def main(): Unit = - val f: List[() => Unit] -> Unit = runOps // error + val f: List[() => Unit] -> Unit = (ops: List[() => Unit]) => runOps(ops) // error + val _: List[() => Unit] -> Unit = runOps // error diff --git a/tests/neg/leak-problem-unboxed.scala b/tests/neg/leak-problem-unboxed.scala new file mode 100644 index 000000000000..8591145583e2 --- /dev/null +++ b/tests/neg/leak-problem-unboxed.scala @@ -0,0 +1,32 @@ +import language.experimental.captureChecking +import caps.unboxed + +// Some capabilities that should be used locally +trait Async: + // some method + def read(): Unit +def usingAsync[X](op: Async^ => X): X = ??? + +case class Box[+T](get: T) + +def useBoxedAsync(@unboxed x: Box[Async^]): Unit = + val t0 = x + val t1 = t0.get // ok + t1.read() + +def useBoxedAsync1(@unboxed x: Box[Async^]): Unit = x.get.read() // ok + +def test(): Unit = + + val f: Box[Async^] => Unit = (x: Box[Async^]) => useBoxedAsync(x) // error + val _: Box[Async^] => Unit = useBoxedAsync(_) // error + val _: Box[Async^] => Unit = useBoxedAsync // error + val _ = useBoxedAsync(_) // error + val _ = useBoxedAsync // error + + def boom(x: Async^): () ->{f} Unit = + () => f(Box(x)) + + val leaked = usingAsync[() ->{f} Unit](boom) + + leaked() // scope violation \ No newline at end of file diff --git a/tests/pos-custom-args/captures/dep-reach.scala b/tests/pos-custom-args/captures/dep-reach.scala index 56343fbf8e53..177422565736 100644 --- a/tests/pos-custom-args/captures/dep-reach.scala +++ b/tests/pos-custom-args/captures/dep-reach.scala @@ -1,9 +1,10 @@ +import caps.unboxed object Test: class C type Proc = () => Unit def f(c: C^, d: C^): () ->{c, d} Unit = - def foo(xs: Proc*): () ->{xs*} Unit = + def foo(@unboxed xs: Proc*): () ->{xs*} Unit = xs.head val a: () ->{c} Unit = () => () val b: () ->{d} Unit = () => () @@ -12,7 +13,7 @@ object Test: def g(c: C^, d: C^): () ->{c, d} Unit = - def foo(xs: Seq[() => Unit]): () ->{xs*} Unit = + def foo(@unboxed xs: Seq[() => Unit]): () ->{xs*} Unit = xs.head val a: () ->{c} Unit = () => () diff --git a/tests/pos-custom-args/captures/reaches.scala b/tests/pos-custom-args/captures/reaches.scala index b1045e3c999a..976fadc4b649 100644 --- a/tests/pos-custom-args/captures/reaches.scala +++ b/tests/pos-custom-args/captures/reaches.scala @@ -1,3 +1,5 @@ +import caps.unboxed + class C def f(xs: List[C^]) = val y = xs @@ -20,7 +22,7 @@ extension [A](x: A) def :: (xs: List[A]): List[A] = ??? object Nil extends List[Nothing] -def runAll(xs: List[Proc]): Unit = +def runAll(@unboxed xs: List[Proc]): Unit = var cur: List[() ->{xs*} Unit] = xs // OK, by revised VAR while cur.nonEmpty do val next: () ->{xs*} Unit = cur.head diff --git a/tests/pos/cc-poly-source-capability.scala b/tests/pos/cc-poly-source-capability.scala index 9a21b2d5b802..0f61e98e5068 100644 --- a/tests/pos/cc-poly-source-capability.scala +++ b/tests/pos/cc-poly-source-capability.scala @@ -1,6 +1,7 @@ import language.experimental.captureChecking import annotation.experimental import caps.{CapSet, Capability} +import caps.unboxed @experimental object Test: @@ -17,7 +18,7 @@ import caps.{CapSet, Capability} def allListeners: Set[Listener^{X^}] = listeners - def test1(async1: Async, others: List[Async]) = + def test1(async1: Async, @unboxed others: List[Async]) = val src = Source[CapSet^{async1, others*}] val lst1 = listener(async1) val lsts = others.map(listener) diff --git a/tests/pos/cc-poly-source.scala b/tests/pos/cc-poly-source.scala index 939f1f682dc8..09b4a3024e3c 100644 --- a/tests/pos/cc-poly-source.scala +++ b/tests/pos/cc-poly-source.scala @@ -1,6 +1,7 @@ import language.experimental.captureChecking import annotation.experimental import caps.{CapSet, Capability} +import caps.unboxed @experimental object Test: @@ -24,7 +25,7 @@ import caps.{CapSet, Capability} val ls = src.allListeners val _: Set[Listener^{lbl1, lbl2}] = ls - def test2(lbls: List[Label^]) = + def test2(@unboxed lbls: List[Label^]) = def makeListener(lbl: Label^): Listener^{lbl} = ??? val listeners = lbls.map(makeListener) val src = Source[CapSet^{lbls*}] diff --git a/tests/pos/gears-probem-1.scala b/tests/pos/gears-probem-1.scala index f5c7fdfd0a3c..c683db9ce01d 100644 --- a/tests/pos/gears-probem-1.scala +++ b/tests/pos/gears-probem-1.scala @@ -1,4 +1,5 @@ import language.experimental.captureChecking +import caps.unboxed trait Future[+T]: def await: T @@ -16,7 +17,7 @@ class Result[+T, +E]: case class Err[+E](e: E) extends Result[Nothing, E] case class Ok[+T](x: T) extends Result[T, Nothing] -extension [T](fs: Seq[Future[T]^]) +extension [T](@unboxed fs: Seq[Future[T]^]) def awaitAll = val collector//: Collector[T]{val futures: Seq[Future[T]^{fs*}]} = Collector(fs) diff --git a/tests/pos/i18699.scala b/tests/pos/i18699.scala index 4bd3fbaad890..54390f6bdd71 100644 --- a/tests/pos/i18699.scala +++ b/tests/pos/i18699.scala @@ -1,7 +1,9 @@ import language.experimental.captureChecking +import caps.unboxed + trait Cap: def use: Int = 42 -def test2(cs: List[Cap^]): Unit = +def test2(@unboxed cs: List[Cap^]): Unit = val t0: Cap^{cs*} = cs.head // error - var t1: Cap^{cs*} = cs.head // error \ No newline at end of file + var t1: Cap^{cs*} = cs.head // error diff --git a/tests/pos/reach-capability.scala b/tests/pos/reach-capability.scala index d551113eb05b..5ad7534061b1 100644 --- a/tests/pos/reach-capability.scala +++ b/tests/pos/reach-capability.scala @@ -1,6 +1,7 @@ import language.experimental.captureChecking import annotation.experimental -import caps.{Capability} +import caps.Capability +import caps.unboxed @experimental object Test2: @@ -11,7 +12,7 @@ import caps.{Capability} class Listener - def test2(lbls: List[Label]) = + def test2(@unboxed lbls: List[Label]) = def makeListener(lbl: Label): Listener^{lbl} = ??? val listeners = lbls.map(makeListener) // should work diff --git a/tests/pos/reach-problem.scala b/tests/pos/reach-problem.scala index 60dd1d4667a7..29d46687a219 100644 --- a/tests/pos/reach-problem.scala +++ b/tests/pos/reach-problem.scala @@ -1,9 +1,22 @@ import language.experimental.captureChecking +import caps.unboxed class Box[T](items: Seq[T^]): def getOne: T^{items*} = ??? object Box: - def getOne[T](items: Seq[T^]): T^{items*} = + def getOne[T](@unboxed items: Seq[T^]): T^{items*} = val bx = Box(items) - bx.getOne \ No newline at end of file + bx.getOne +/* + def head[T](items: Seq[T^]): Unit = + val is = items + val x = is.head + () + + def head2[X^, T](items: Seq[T^{X^}]): T^{X^} = + items.head + + def head3[T](items: Seq[T^]): Unit = + head2[caps.CapSet^{items*}, T](items) +*/ \ No newline at end of file