Skip to content

Commit

Permalink
Add @unboxed to failing tests and standard library file Buffer.scala
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Jul 9, 2024
1 parent 8308b33 commit 8a504aa
Show file tree
Hide file tree
Showing 15 changed files with 105 additions and 44 deletions.
4 changes: 2 additions & 2 deletions scala2-library-cc/src/scala/collection/mutable/Buffer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion tests/neg-custom-args/captures/i15749a.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import caps.cap
import caps.unboxed

class Unit
object u extends Unit

Expand All @@ -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
36 changes: 22 additions & 14 deletions tests/neg-custom-args/captures/reaches.check
Original file line number Diff line number Diff line change
@@ -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]^?
Expand All @@ -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
7 changes: 4 additions & 3 deletions tests/neg-custom-args/captures/reaches.scala
Original file line number Diff line number Diff line change
@@ -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 = ???

Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
14 changes: 4 additions & 10 deletions tests/neg-custom-args/captures/widen-reach.check
Original file line number Diff line number Diff line change
@@ -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
| ^
Expand Down
6 changes: 4 additions & 2 deletions tests/neg/i20503.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import language.experimental.captureChecking
import caps.unboxed

class List[+A]:
def head: A = ???
Expand All @@ -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
32 changes: 32 additions & 0 deletions tests/neg/leak-problem-unboxed.scala
Original file line number Diff line number Diff line change
@@ -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
5 changes: 3 additions & 2 deletions tests/pos-custom-args/captures/dep-reach.scala
Original file line number Diff line number Diff line change
@@ -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 = () => ()
Expand All @@ -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 = () => ()
Expand Down
4 changes: 3 additions & 1 deletion tests/pos-custom-args/captures/reaches.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import caps.unboxed

class C
def f(xs: List[C^]) =
val y = xs
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion tests/pos/cc-poly-source-capability.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import language.experimental.captureChecking
import annotation.experimental
import caps.{CapSet, Capability}
import caps.unboxed

@experimental object Test:

Expand All @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion tests/pos/cc-poly-source.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import language.experimental.captureChecking
import annotation.experimental
import caps.{CapSet, Capability}
import caps.unboxed

@experimental object Test:

Expand All @@ -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*}]
Expand Down
3 changes: 2 additions & 1 deletion tests/pos/gears-probem-1.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import language.experimental.captureChecking
import caps.unboxed

trait Future[+T]:
def await: T
Expand All @@ -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)
Expand Down
6 changes: 4 additions & 2 deletions tests/pos/i18699.scala
Original file line number Diff line number Diff line change
@@ -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
var t1: Cap^{cs*} = cs.head // error
5 changes: 3 additions & 2 deletions tests/pos/reach-capability.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import language.experimental.captureChecking
import annotation.experimental
import caps.{Capability}
import caps.Capability
import caps.unboxed

@experimental object Test2:

Expand All @@ -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

17 changes: 15 additions & 2 deletions tests/pos/reach-problem.scala
Original file line number Diff line number Diff line change
@@ -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
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)
*/

0 comments on commit 8a504aa

Please sign in to comment.