Skip to content

Commit eae9c07

Browse files
committed
Expand aliases when mapping explicit types in Setup
This is necessary because the compiler is free in previous phases to dealias or not. Therefore, capture checking should not depend on aliasing. The main difference is that now arguments to type aliases are not necessarily boxed. They are boxed only if they need boxing in the dealiased type.
1 parent 8049a9d commit eae9c07

16 files changed

+68
-53
lines changed

compiler/src/dotty/tools/dotc/cc/CaptureOps.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ object ccConfig:
5656
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.6`)
5757

5858
def followAliases(using Context): Boolean =
59-
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.7`)
59+
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.6`)
6060
end ccConfig
6161

6262
/** Are we at checkCaptures phase? */

tests/neg-custom-args/captures/boundschecks2.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ object test {
88

99
val foo: C[Tree^] = ??? // error
1010
type T = C[Tree^] // error
11-
val bar: T -> T = ???
11+
//val bar: T -> T = ??? // --> boundschecks3.scala for what happens if we uncomment
1212
val baz: C[Tree^] -> Unit = ??? // error
1313
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- Error: tests/neg-custom-args/captures/boundschecks3.scala:11:13 -----------------------------------------------------
2+
11 | val bar: T -> T = ??? // error, since `T` is expanded here. But the msg is not very good.
3+
| ^^^^^^
4+
| test.C[box test.Tree^] captures the root capability `cap` in invariant position
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
object test {
2+
3+
class Tree
4+
5+
def f[X <: Tree](x: X): Unit = ()
6+
7+
class C[X <: Tree](x: X)
8+
9+
val foo: C[Tree^] = ??? // hidden error
10+
type T = C[Tree^] // hidden error
11+
val bar: T -> T = ??? // error, since `T` is expanded here. But the msg is not very good.
12+
val baz: C[Tree^] -> Unit = ??? // hidden error
13+
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
trait Cap
22

33
def test1(io: Cap^) = {
4-
type Op[X] = [T] -> Unit -> X
4+
class Op[+X](val value: [T] -> Unit -> X)
55
val f: Op[Cap^{io}] = ???
6-
val x: [T] -> Unit -> Cap^{io} = f // error
6+
val x: [T] -> Unit -> Cap^{io} = f.value // error
77
}
88

99
def test2(io: Cap^) = {
10-
type Op[X] = [T] -> Unit -> X^{io}
10+
class Op[+X](val value: [T] -> Unit -> X^{io})
1111
val f: Op[Cap^{io}] = ???
12-
val x: Unit -> Cap^{io} = f[Unit] // error
13-
val x1: Unit ->{io} Cap^{io} = f[Unit] // ok
12+
val x: Unit -> Cap^{io} = f.value[Unit] // error
13+
val x1: Unit ->{io} Cap^{io} = f.value[Unit] // ok
1414
}
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
trait Cap { def use(): Int }
22

33
def test1(io: Cap^): Unit = {
4-
type Id[X] = [T] -> (op: X ->{io} T) -> T
4+
class Id[X](val value: [T] -> (op: X ->{io} T) -> T)
55

66
val x: Id[Cap]^{io} = ???
7-
x(cap => cap.use()) // ok
7+
x.value(cap => cap.use()) // ok
88
}
99

1010
def test2(io: Cap^): Unit = {
11-
type Id[X] = [T] -> (op: (x: X) ->{io} T) -> T
11+
class Id[X](val value: [T] -> (op: (x: X) ->{io} T) -> T)
1212

1313
val x: Id[Cap^{io}] = ???
14-
x(cap => cap.use())
14+
x.value(cap => cap.use())
1515
// should work when the expected type is a dependent function
1616
}
1717

1818
def test3(io: Cap^): Unit = {
19-
type Id[X] = [T] -> (op: (x: X) ->{} T) -> T
19+
class Id[X](val value: [T] -> (op: (x: X) ->{} T) -> T)
2020

2121
val x: Id[Cap^{io}] = ???
22-
x(cap => cap.use()) // error
22+
x.value(cap => cap.use()) // error
2323
}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
trait Cap { def use(): Int }
22

33
def test1(io: Cap^): Unit = {
4-
type Op[X] = [T] -> X -> Unit
4+
class Op[X](val value: [T] -> X -> Unit)
55
val f: [T] -> (Cap^{io}) -> Unit = ???
6-
val op: Op[Cap^{io}] = f // error
6+
val op: Op[Cap^{io}] = Op(f) // was error, now ok
77
}
88

99
def test2(io: Cap^): Unit = {
10-
type Lazy[X] = [T] -> Unit -> X
10+
class Lazy[X](val value: [T] -> Unit -> X)
1111
val f: Lazy[Cap^{io}] = ???
12-
val test: [T] -> Unit -> (Cap^{io}) = f // error
12+
val test: [T] -> Unit -> (Cap^{io}) = f.value // error
1313
}

tests/neg-custom-args/captures/capt1.check

+6-6
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,15 @@
3636
-- Error: tests/neg-custom-args/captures/capt1.scala:34:16 -------------------------------------------------------------
3737
34 | val z2 = h[() -> Cap](() => x) // error // error
3838
| ^^^^^^^^^
39-
| Type variable X of method h cannot be instantiated to () -> box C^ since
40-
| the part box C^ of that type captures the root capability `cap`.
39+
| Type variable X of method h cannot be instantiated to () -> (ex$15: caps.Exists) -> C^{ex$15} since
40+
| the part C^{ex$15} of that type captures the root capability `cap`.
4141
-- Error: tests/neg-custom-args/captures/capt1.scala:34:30 -------------------------------------------------------------
4242
34 | val z2 = h[() -> Cap](() => x) // error // error
4343
| ^
44-
| reference (x : C^) is not included in the allowed capture set {}
45-
| of an enclosing function literal with expected type () -> box C^
44+
| reference (x : C^) is not included in the allowed capture set {}
45+
| of an enclosing function literal with expected type () -> (ex$15: caps.Exists) -> C^{ex$15}
4646
-- Error: tests/neg-custom-args/captures/capt1.scala:36:13 -------------------------------------------------------------
4747
36 | val z3 = h[(() -> Cap) @retains(x)](() => x)(() => C()) // error
4848
| ^^^^^^^^^^^^^^^^^^^^^^^
49-
| Type variable X of method h cannot be instantiated to box () ->{x} Cap since
50-
| the part Cap of that type captures the root capability `cap`.
49+
| Type variable X of method h cannot be instantiated to box () ->{x} (ex$20: caps.Exists) -> C^{ex$20} since
50+
| the part C^{ex$20} of that type captures the root capability `cap`.

tests/neg-custom-args/captures/explain-under-approx.check

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
| Found: Future[Int]{val a: (async : Async)}^{async}
55
| Required: Future[Int]^{col.futs*}
66
|
7-
| Note that reference ex$25.type
7+
| Note that reference ex$22.type
88
| cannot be included in outer capture set {cap}
99
|
1010
| longer explanation available when compiling with `-explain`

tests/neg-custom-args/captures/i15922.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11

22

33
trait Cap { def use(): Int }
4-
type Id[X] = [T] -> (op: X => T) -> T
5-
def mkId[X](x: X): Id[X] = [T] => (op: X => T) => op(x)
4+
class Id[+X](val value: [T] -> (op: X => T) -> T)
5+
def mkId[X](x: X): Id[X] = Id([T] => (op: X => T) => op(x))
66

77
def withCap[X](op: (Cap^) => X): X = {
88
val cap: Cap^ = new Cap { def use() = { println("cap is used"); 0 } }

tests/neg-custom-args/captures/i16725.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ class IO extends caps.Capability:
33
def brewCoffee(): Unit = ???
44
def usingIO[T](op: IO => T): T = ???
55

6-
type Wrapper[T] = [R] -> (f: T => R) -> R
7-
def mk[T](x: T): Wrapper[T] = [R] => f => f(x)
6+
class Wrapper[T](val value: [R] -> (f: T => R) -> R)
7+
def mk[T](x: T): Wrapper[T] = Wrapper([R] => f => f(x))
88
def useWrappedIO(wrapper: Wrapper[IO]): () -> Unit =
99
() =>
10-
wrapper: io => // error
10+
wrapper.value: io => // error
1111
io.brewCoffee()
1212
def main(): Unit =
1313
val escaped = usingIO(io => useWrappedIO(mk(io))) // error

tests/neg-custom-args/captures/i19330.check

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
22 | val bad: bar.T = foo(bar) // error
88
| ^^^^^^^^
99
| Found: () => Logger^
10-
| Required: () ->{fresh} Logger^{fresh}
10+
| Required: () ->{fresh} (ex$9: caps.Exists) -> Logger^{ex$9}
1111
|
1212
| longer explanation available when compiling with `-explain`

tests/neg-custom-args/captures/i21401.check

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
-- Error: tests/neg-custom-args/captures/i21401.scala:16:66 ------------------------------------------------------------
1212
16 | val leaked: [R, X <: Boxed[IO^] -> R] -> (op: X) -> R = usingIO[Res](mkRes) // error
1313
| ^^^
14-
| Type variable R of method usingIO cannot be instantiated to Res since
15-
| the part box IO^ of that type captures the root capability `cap`.
14+
| Type variable R of method usingIO cannot be instantiated to [R, X <: Boxed[box IO^] -> R] => (op: X) -> R since
15+
| the part box IO^ of that type captures the root capability `cap`.
1616
-- Error: tests/neg-custom-args/captures/i21401.scala:17:29 ------------------------------------------------------------
1717
17 | val x: Boxed[IO^] = leaked[Boxed[IO^], Boxed[IO^] -> Boxed[IO^]](x => x) // error // error
1818
| ^^^^^^^^^^
@@ -21,8 +21,8 @@
2121
-- Error: tests/neg-custom-args/captures/i21401.scala:17:52 ------------------------------------------------------------
2222
17 | val x: Boxed[IO^] = leaked[Boxed[IO^], Boxed[IO^] -> Boxed[IO^]](x => x) // error // error
2323
| ^^^^^^^^^^^^^^^^^^^^^^^^
24-
|Type variable X of value leaked cannot be instantiated to Boxed[box IO^] -> (ex$18: caps.Exists) -> Boxed[box IO^{ex$18}] since
25-
|the part box IO^{ex$18} of that type captures the root capability `cap`.
24+
|Type variable X of value leaked cannot be instantiated to Boxed[box IO^] -> (ex$20: caps.Exists) -> Boxed[box IO^{ex$20}] since
25+
|the part box IO^{ex$20} of that type captures the root capability `cap`.
2626
-- Error: tests/neg-custom-args/captures/i21401.scala:18:21 ------------------------------------------------------------
2727
18 | val y: IO^{x*} = x.unbox // error
2828
| ^^^^^^^

tests/neg/cc-ex-conformance.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ def Test =
2121
val ex3: EX3 = ???
2222
val ex4: EX4 = ???
2323
val _: EX4 = ex3 // ok
24-
val _: EX4 = ex4
24+
val _: EX4 = ex4 // error (???) Probably since we also introduce existentials on expansion
2525
val _: EX3 = ex4 // error

tests/neg/existential-mapping.check

+12-12
Original file line numberDiff line numberDiff line change
@@ -33,56 +33,56 @@
3333
-- [E007] Type Mismatch Error: tests/neg/existential-mapping.scala:21:30 -----------------------------------------------
3434
21 | val _: A^ -> (x: C^) -> C = x5 // error
3535
| ^^
36-
| Found: (x5 : A^ -> (ex$27: caps.Exists) -> Fun[C^{ex$27}])
36+
| Found: (x5 : A^ -> (x: C^) -> (ex$27: caps.Exists) -> C^{ex$27})
3737
| Required: A^ -> (x: C^) -> C
3838
|
3939
| longer explanation available when compiling with `-explain`
4040
-- [E007] Type Mismatch Error: tests/neg/existential-mapping.scala:24:30 -----------------------------------------------
4141
24 | val _: A^ -> (x: C^) => C = x6 // error
4242
| ^^
43-
| Found: (x6 : A^ -> (ex$33: caps.Exists) -> (x: C^) ->{fresh} C^{ex$33})
44-
| Required: A^ -> (ex$36: caps.Exists) -> (x: C^) ->{ex$36} C
43+
| Found: (x6 : A^ -> (ex$36: caps.Exists) -> (x: C^) ->{ex$36} (ex$35: caps.Exists) -> C^{ex$35})
44+
| Required: A^ -> (ex$39: caps.Exists) -> (x: C^) ->{ex$39} C
4545
|
4646
| longer explanation available when compiling with `-explain`
4747
-- [E007] Type Mismatch Error: tests/neg/existential-mapping.scala:27:25 -----------------------------------------------
4848
27 | val _: (x: C^) => C = y1 // error
4949
| ^^
50-
| Found: (y1 : (x: C^) ->{fresh} (ex$38: caps.Exists) -> C^{ex$38})
50+
| Found: (y1 : (x: C^) ->{fresh} (ex$41: caps.Exists) -> C^{ex$41})
5151
| Required: (x: C^) ->{fresh} C
5252
|
5353
| longer explanation available when compiling with `-explain`
5454
-- [E007] Type Mismatch Error: tests/neg/existential-mapping.scala:30:20 -----------------------------------------------
5555
30 | val _: C^ => C = y2 // error
5656
| ^^
57-
| Found: (y2 : C^ ->{fresh} (ex$42: caps.Exists) -> C^{ex$42})
57+
| Found: (y2 : C^ ->{fresh} (ex$45: caps.Exists) -> C^{ex$45})
5858
| Required: C^ ->{fresh} C
5959
|
6060
| longer explanation available when compiling with `-explain`
6161
-- [E007] Type Mismatch Error: tests/neg/existential-mapping.scala:33:30 -----------------------------------------------
6262
33 | val _: A^ => (x: C^) => C = y3 // error
6363
| ^^
64-
| Found: (y3 : A^ ->{fresh} (ex$47: caps.Exists) -> (x: C^) ->{ex$47} (ex$46: caps.Exists) -> C^{ex$46})
65-
| Required: A^ ->{fresh} (ex$50: caps.Exists) -> (x: C^) ->{ex$50} C
64+
| Found: (y3 : A^ ->{fresh} (ex$50: caps.Exists) -> (x: C^) ->{ex$50} (ex$49: caps.Exists) -> C^{ex$49})
65+
| Required: A^ ->{fresh} (ex$53: caps.Exists) -> (x: C^) ->{ex$53} C
6666
|
6767
| longer explanation available when compiling with `-explain`
6868
-- [E007] Type Mismatch Error: tests/neg/existential-mapping.scala:36:25 -----------------------------------------------
6969
36 | val _: A^ => C^ => C = y4 // error
7070
| ^^
71-
| Found: (y4 : A^ ->{fresh} (ex$53: caps.Exists) -> C^ ->{ex$53} (ex$52: caps.Exists) -> C^{ex$52})
72-
| Required: A^ ->{fresh} (ex$56: caps.Exists) -> C^ ->{ex$56} C
71+
| Found: (y4 : A^ ->{fresh} (ex$56: caps.Exists) -> C^ ->{ex$56} (ex$55: caps.Exists) -> C^{ex$55})
72+
| Required: A^ ->{fresh} (ex$59: caps.Exists) -> C^ ->{ex$59} C
7373
|
7474
| longer explanation available when compiling with `-explain`
7575
-- [E007] Type Mismatch Error: tests/neg/existential-mapping.scala:39:30 -----------------------------------------------
7676
39 | val _: A^ => (x: C^) -> C = y5 // error
7777
| ^^
78-
| Found: (y5 : A^ ->{fresh} (ex$58: caps.Exists) -> Fun[C^{ex$58}])
78+
| Found: (y5 : A^ ->{fresh} (x: C^) -> (ex$61: caps.Exists) -> C^{ex$61})
7979
| Required: A^ ->{fresh} (x: C^) -> C
8080
|
8181
| longer explanation available when compiling with `-explain`
8282
-- [E007] Type Mismatch Error: tests/neg/existential-mapping.scala:42:30 -----------------------------------------------
8383
42 | val _: A^ => (x: C^) => C = y6 // error
8484
| ^^
85-
| Found: (y6 : A^ ->{fresh} (ex$64: caps.Exists) -> (x: C^) ->{fresh} C^{ex$64})
86-
| Required: A^ ->{fresh} (ex$67: caps.Exists) -> (x: C^) ->{ex$67} C
85+
| Found: (y6 : A^ ->{fresh} (ex$70: caps.Exists) -> (x: C^) ->{ex$70} (ex$69: caps.Exists) -> C^{ex$69})
86+
| Required: A^ ->{fresh} (ex$73: caps.Exists) -> (x: C^) ->{ex$73} C
8787
|
8888
| longer explanation available when compiling with `-explain`

tests/neg-custom-args/captures/i15749a.scala renamed to tests/pos-custom-args/captures/i15749a.scala

+4-6
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,17 @@ object u extends Unit
66

77
type Top = Any^
88

9-
type Wrapper[+T] = [X] -> (op: T ->{cap} X) -> X
9+
class Wrapper[+T](val value: [X] -> (op: T ->{cap} X) -> X)
1010

1111
def test =
1212

13-
def wrapper[T](x: T): Wrapper[T] =
13+
def wrapper[T](x: T): Wrapper[T] = Wrapper:
1414
[X] => (op: T ->{cap} X) => op(x)
1515

1616
def strictMap[A <: Top, B <: Top](mx: Wrapper[A])(f: A ->{cap} B): Wrapper[B] =
17-
mx((x: A) => wrapper(f(x)))
17+
mx.value((x: A) => wrapper(f(x)))
1818

1919
def force[A](thunk: Unit ->{cap} A): A = thunk(u)
2020

2121
def forceWrapper[A](@use mx: Wrapper[Unit ->{cap} A]): Wrapper[A] =
22-
// Γ ⊢ mx: Wrapper[□ {cap} Unit => A]
23-
// `force` should be typed as ∀(□ {cap} Unit -> A) A, but it can not
24-
strictMap[Unit ->{mx*} A, A](mx)(t => force[A](t)) // error // should work
22+
strictMap[Unit ->{mx*} A, A](mx)(t => force[A](t)) // was error when Wrapper was an alias type

0 commit comments

Comments
 (0)