Skip to content

Commit 86088da

Browse files
committed
Tests for capture checking
1 parent ca561df commit 86088da

24 files changed

+536
-0
lines changed

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class CompilationTests {
3939
compileFilesInDir("tests/pos-special/isInstanceOf", allowDeepSubtypes.and("-Xfatal-warnings")),
4040
compileFilesInDir("tests/new", defaultOptions),
4141
compileFilesInDir("tests/pos-scala2", scala2CompatMode),
42+
compileFilesInDir("tests/pos-custom-args/captures", defaultOptions.and("-Ycc")),
4243
compileFilesInDir("tests/pos-custom-args/erased", defaultOptions.and("-language:experimental.erasedDefinitions")),
4344
compileFilesInDir("tests/pos", defaultOptions.and("-Ysafe-init")),
4445
compileFilesInDir("tests/pos-deep-subtype", allowDeepSubtypes),
@@ -133,6 +134,7 @@ class CompilationTests {
133134
compileFilesInDir("tests/neg-custom-args/allow-deep-subtypes", allowDeepSubtypes),
134135
compileFilesInDir("tests/neg-custom-args/explicit-nulls", defaultOptions.and("-Yexplicit-nulls")),
135136
compileFilesInDir("tests/neg-custom-args/no-experimental", defaultOptions.and("-Yno-experimental")),
137+
compileFilesInDir("tests/neg-custom-args/captures", defaultOptions.and("-Ycc")),
136138
compileDir("tests/neg-custom-args/impl-conv", defaultOptions.and("-Xfatal-warnings", "-feature")),
137139
compileFile("tests/neg-custom-args/implicit-conversions.scala", defaultOptions.and("-Xfatal-warnings", "-feature")),
138140
compileFile("tests/neg-custom-args/implicit-conversions-old.scala", defaultOptions.and("-Xfatal-warnings", "-feature")),
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/boxmap.scala:15:2 ----------------------------------------
2+
15 | () => b[Box[B]]((x: A) => box(f(x))) // error
3+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
| Found: (() => Box[B]) retains b retains f
5+
| Required: () => Box[B]
6+
|
7+
| where: B is a type in method lazymap with bounds <: Top
8+
9+
longer explanation available when compiling with `-explain`
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
type Top = Any retains *
2+
class Cap extends Retains[*]
3+
4+
infix type ==> [A, B] = (A => B) retains *
5+
6+
type Box[+T <: Top] = ([K <: Top] => (T ==> K) => K) retains T
7+
8+
def box[T <: Top](x: T): Box[T] =
9+
[K <: Top] => (k: T ==> K) => k(x)
10+
11+
def map[A <: Top, B <: Top](b: Box[A])(f: A ==> B): Box[B] =
12+
b[Box[B]]((x: A) => box(f(x)))
13+
14+
def lazymap[A <: Top, B <: Top](b: Box[A])(f: A ==> B): () => Box[B] =
15+
() => b[Box[B]]((x: A) => box(f(x))) // error
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class C
2+
type Cap = C retains *
3+
type Top = Any retains *
4+
5+
type T = (x: Cap) => List[String retains x.type] => Unit // error
6+
val x: (x: Cap) => Array[String retains x.type] = ??? // error
7+
val y = x
8+
9+
def test: Unit =
10+
def f(x: Cap) = // ok
11+
val g = (xs: List[String retains x.type]) => ()
12+
g
13+
def f2(x: Cap)(xs: List[String retains x.type]) = ()
14+
val x = f // error
15+
val x2 = f2 // error
16+
val y = f(C()) // ok
17+
val y2 = f2(C()) // ok
18+
()
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:3:2 ------------------------------------------
2+
3 | () => if x == null then y else y // error
3+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
| Found: (() => C) retains x
5+
| Required: () => C
6+
7+
longer explanation available when compiling with `-explain`
8+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:6:2 ------------------------------------------
9+
6 | () => if x == null then y else y // error
10+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11+
| Found: (() => C) retains x
12+
| Required: Any
13+
14+
longer explanation available when compiling with `-explain`
15+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:14:2 -----------------------------------------
16+
14 | f // error
17+
| ^
18+
| Found: (Int => Int) retains x
19+
| Required: Any
20+
21+
longer explanation available when compiling with `-explain`
22+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:23:3 -----------------------------------------
23+
23 | F(22) // error
24+
| ^^^^^
25+
| Found: F retains x
26+
| Required: A
27+
28+
longer explanation available when compiling with `-explain`
29+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:27:40 ----------------------------------------
30+
27 | def m() = if x == null then y else y // error
31+
| ^
32+
| Found: A {...} retains x
33+
| Required: A
34+
35+
longer explanation available when compiling with `-explain`
36+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:32:24 ----------------------------------------
37+
32 | val z2 = h[() => Cap](() => x)(() => C()) // error
38+
| ^^^^^^^
39+
| Found: (() => Cap) retains x
40+
| Required: () => Cap
41+
42+
longer explanation available when compiling with `-explain`
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
class C
2+
def f(x: C retains *, y: C): () => C =
3+
() => if x == null then y else y // error
4+
5+
def g(x: C retains *, y: C): Any =
6+
() => if x == null then y else y // error
7+
8+
def h1(x: C retains *, y: C): Any retains x.type =
9+
def f() = if x == null then y else y
10+
() => f() // ok
11+
12+
def h2(x: C retains *): Any =
13+
def f(y: Int) = if x == null then y else y
14+
f // error
15+
16+
class A
17+
type Cap = C retains *
18+
type Top = Any retains *
19+
20+
def h3(x: Cap): A =
21+
class F(y: Int) extends A:
22+
def m() = if x == null then y else y
23+
F(22) // error
24+
25+
def h4(x: Cap, y: Int): A =
26+
new A:
27+
def m() = if x == null then y else y // error
28+
29+
def foo() =
30+
val x: C retains * = ???
31+
def h[X <:Top](a: X)(b: X) = a
32+
val z2 = h[() => Cap](() => x)(() => C()) // error
33+
val z3 = h[(() => Cap) retains x.type](() => x)(() => C()) // ok
34+
val z4 = h[(() => Cap) retains x.type](() => x)(() => C()) // what was inferred for z3
35+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class C
2+
type Cap = C retains *
3+
4+
def f1(c: Cap): (() => C retains c.type) = () => c // ok
5+
def f2(c: Cap): (() => C) retains c.type = () => c // error
6+
7+
def h5(x: Cap): () => C =
8+
f1(x) // error
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
object Test:
2+
3+
def f[A <: Any retains *](x: A): Any = x // error
4+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
sealed trait IO:
2+
def puts(msg: Any): Unit = println(msg)
3+
4+
def test1 =
5+
val IO : IO retains * = new IO {}
6+
def foo = IO.puts("hello")
7+
val x : () => Unit = () => foo // error: Found: (() => Unit) retains IO; Required: () => Unit
8+
9+
def test2 =
10+
val IO : IO retains * = new IO {}
11+
def puts(msg: Any, io: IO retains *) = println(msg)
12+
def foo() = puts("hello", IO)
13+
val x : () => Unit = () => foo() // error: Found: (() => Unit) retains IO; Required: () => Unit
14+
15+
type Capability[T] = T retains *
16+
17+
def test3 =
18+
val IO : Capability[IO] = new IO {}
19+
def puts(msg: Any, io: Capability[IO]) = println(msg)
20+
def foo() = puts("hello", IO)
21+
val x : () => Unit = () => foo() // error: Found: (() => Unit) retains IO; Required: () => Unit
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:29:32 ------------------------------------------
2+
29 | (x: CanThrow[Exception]) => () => raise(new Exception)(using x) // error
3+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
| Found: (() => Nothing) retains x
5+
| Required: () => Nothing
6+
7+
longer explanation available when compiling with `-explain`
8+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:43:2 -------------------------------------------
9+
43 | yy // error
10+
| ^^
11+
| Found: (yy : List[(xx : (() => Int) retains *)])
12+
| Required: List[() => Int]
13+
14+
longer explanation available when compiling with `-explain`
15+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:50:2 -------------------------------------------
16+
45 |val global = handle {
17+
46 | (x: CanThrow[Exception]) =>
18+
47 | () =>
19+
48 | raise(new Exception)(using x)
20+
49 | 22
21+
50 |} { // error
22+
| ^
23+
| Found: (() => Int) retains *
24+
| Required: () => Int
25+
51 | (ex: Exception) => () => 22
26+
52 |}
27+
28+
longer explanation available when compiling with `-explain`
29+
-- Error: tests/neg-custom-args/captures/try.scala:22:28 ---------------------------------------------------------------
30+
22 | val a = handle[Exception, CanThrow[Exception]] { // error
31+
| ^^^^^^^^^^^^^^^^^^^
32+
| type argument is not allowed to capture the universal capability *
33+
-- Error: tests/neg-custom-args/captures/try.scala:34:11 ---------------------------------------------------------------
34+
34 | val xx = handle { // error
35+
| ^^^^^^
36+
| inferred type argument ((() => Int) retains *) is not allowed to capture the universal capability *
37+
|
38+
| The inferred arguments are: [Exception, ((() => Int) retains *)]
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import language.experimental.erasedDefinitions
2+
3+
class CT[E <: Exception]
4+
type CanThrow[E <: Exception] = CT[E] retains *
5+
type Top = Any retains *
6+
7+
infix type throws[R, E <: Exception] = (erased CanThrow[E]) ?=> R
8+
9+
class Fail extends Exception
10+
11+
def raise[E <: Exception](e: E): Nothing throws E = throw e
12+
13+
def foo(x: Boolean): Int throws Fail =
14+
if x then 1 else raise(Fail())
15+
16+
def handle[E <: Exception, R <: Top](op: CanThrow[E] => R)(handler: E => R): R =
17+
val x: CanThrow[E] = ???
18+
try op(x)
19+
catch case ex: E => handler(ex)
20+
21+
def test: List[() => Int] =
22+
val a = handle[Exception, CanThrow[Exception]] { // error
23+
(x: CanThrow[Exception]) => x
24+
}{
25+
(ex: Exception) => ???
26+
}
27+
28+
val b = handle[Exception, () => Nothing] {
29+
(x: CanThrow[Exception]) => () => raise(new Exception)(using x) // error
30+
} {
31+
(ex: Exception) => ???
32+
}
33+
34+
val xx = handle { // error
35+
(x: CanThrow[Exception]) =>
36+
() =>
37+
raise(new Exception)(using x)
38+
22
39+
} {
40+
(ex: Exception) => () => 22
41+
}
42+
val yy = xx :: Nil
43+
yy // error
44+
45+
val global = handle {
46+
(x: CanThrow[Exception]) =>
47+
() =>
48+
raise(new Exception)(using x)
49+
22
50+
} { // error
51+
(ex: Exception) => () => 22
52+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try2.scala:31:32 -----------------------------------------
2+
31 | (x: CanThrow[Exception]) => () => raise(new Exception)(using x) // error
3+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
| Found: (() => Nothing) retains x
5+
| Required: () => Nothing
6+
7+
longer explanation available when compiling with `-explain`
8+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try2.scala:45:2 ------------------------------------------
9+
45 | yy // error
10+
| ^^
11+
| Found: (yy : List[(xx : (() => Int) retains canThrow)])
12+
| Required: List[() => Int]
13+
14+
longer explanation available when compiling with `-explain`
15+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try2.scala:52:2 ------------------------------------------
16+
47 |val global = handle {
17+
48 | (x: CanThrow[Exception]) =>
18+
49 | () =>
19+
50 | raise(new Exception)(using x)
20+
51 | 22
21+
52 |} { // error
22+
| ^
23+
| Found: (() => Int) retains canThrow
24+
| Required: () => Int
25+
53 | (ex: Exception) => () => 22
26+
54 |}
27+
28+
longer explanation available when compiling with `-explain`
29+
-- Error: tests/neg-custom-args/captures/try2.scala:24:28 --------------------------------------------------------------
30+
24 | val a = handle[Exception, CanThrow[Exception]] { // error
31+
| ^^^^^^^^^^^^^^^^^^^
32+
| type argument is not allowed to capture the global capability (canThrow : *)
33+
-- Error: tests/neg-custom-args/captures/try2.scala:36:11 --------------------------------------------------------------
34+
36 | val xx = handle { // error
35+
| ^^^^^^
36+
|inferred type argument ((() => Int) retains canThrow) is not allowed to capture the global capability (canThrow : *)
37+
|
38+
|The inferred arguments are: [Exception, ((() => Int) retains canThrow)]
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import language.experimental.erasedDefinitions
2+
import annotation.ability
3+
4+
@ability erased val canThrow: * = ???
5+
6+
class CanThrow[E <: Exception] extends Retains[canThrow.type]
7+
type Top = Any retains *
8+
9+
infix type throws[R, E <: Exception] = (erased CanThrow[E]) ?=> R
10+
11+
class Fail extends Exception
12+
13+
def raise[E <: Exception](e: E): Nothing throws E = throw e
14+
15+
def foo(x: Boolean): Int throws Fail =
16+
if x then 1 else raise(Fail())
17+
18+
def handle[E <: Exception, R <: Top](op: CanThrow[E] => R)(handler: E => R): R =
19+
val x: CanThrow[E] = ???
20+
try op(x)
21+
catch case ex: E => handler(ex)
22+
23+
def test: List[() => Int] =
24+
val a = handle[Exception, CanThrow[Exception]] { // error
25+
(x: CanThrow[Exception]) => x
26+
}{
27+
(ex: Exception) => ???
28+
}
29+
30+
val b = handle[Exception, () => Nothing] {
31+
(x: CanThrow[Exception]) => () => raise(new Exception)(using x) // error
32+
} {
33+
(ex: Exception) => ???
34+
}
35+
36+
val xx = handle { // error
37+
(x: CanThrow[Exception]) =>
38+
() =>
39+
raise(new Exception)(using x)
40+
22
41+
} {
42+
(ex: Exception) => () => 22
43+
}
44+
val yy = xx :: Nil
45+
yy // error
46+
47+
val global = handle {
48+
(x: CanThrow[Exception]) =>
49+
() =>
50+
raise(new Exception)(using x)
51+
22
52+
} { // error
53+
(ex: Exception) => () => 22
54+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import java.io.IOException
2+
3+
class CanThrow[E] extends Retains[*]
4+
type Top = Any retains *
5+
6+
def handle[E <: Exception, T <: Top](op: CanThrow[E] ?=> T)(handler: E => T): T =
7+
val x: CanThrow[E] = ???
8+
try op(using x)
9+
catch case ex: E => handler(ex)
10+
11+
def raise[E <: Exception](ex: E)(using CanThrow[E]): Nothing =
12+
throw ex
13+
14+
@main def Test: Int =
15+
def f(a: Boolean) =
16+
handle { // error
17+
if !a then raise(IOException())
18+
(b: Boolean) =>
19+
if !b then raise(IOException())
20+
0
21+
} {
22+
ex => (b: Boolean) => -1
23+
}
24+
val g = f(true)
25+
g(false) // would raise an uncaught exception
26+
f(true)(false) // would raise an uncaught exception
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- [E007] Type Mismatch Error: tests/neg/polymorphic-functions1.scala:1:53 ---------------------------------------------
2+
1 |val f: [T] => (x: T) => x.type = [T] => (x: Int) => x // error
3+
| ^
4+
| Found: [T] => (Int) => Int
5+
| Required: [T] => (x: T) => x.type
6+
7+
longer explanation available when compiling with `-explain`
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
val f: [T] => (x: T) => x.type = [T] => (x: Int) => x // error

0 commit comments

Comments
 (0)