You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- We separate encapsulation from boxing. Instead, similar to reachability types,
5
-
we don't allow widening to `cap` in subcapturing. The subcapturing rule (sc-var)
6
-
is revised as follows:
7
-
8
-
```
9
-
x: S^C in E E |- C <: C' cap notin C
10
-
-------------------------------------------
11
-
E |- {x} <: C'
12
-
13
-
```
14
-
The aim is to have a system where we detect all leaks on formation
15
-
and not some just on access. In the TOPLAS version, we cannot box {cap}
16
-
but we can box with some capability {x} and then widen under the box
17
-
into {cap}. This was not a problem because we cannot unbox cap so the
18
-
data could not be accessed. But now we want to be stricter and already
19
-
prevent the formation. This strictness is necessary to ensure soundness
20
-
of the modified (Var) rule described below.
21
-
22
-
NOTE: This restrictions might make things difficult. For instance,
23
-
we want to have:
24
-
25
-
A^ -> B <: A^{x} -> B
26
-
27
-
but with the rule above this no longer holds.
28
-
29
-
- To compensate, and allow capture polymorphism in e.g. function arguments, we allow
30
-
some subcapture slack to covariant cap when comparing the types of actual and expected types
31
-
after rechecking. This has to be done selectively, so that the following are still guaranteed:
32
-
33
-
- No widening of function results.
34
-
That way, library-defined encapsulators like `usingFile` still prevent leaks
35
-
on formation.
36
-
- No widening in assignments to vars.
37
-
That way, we cannot assign local capabilities to global vars.
38
-
- No widening in results of trys.
39
-
That way, local throws capabilities cannot escape.
40
-
41
-
- The way to achieve this is to tweak the expected type. Interesting
42
-
expected types are created by
43
-
44
-
1. function parameter types for their arguments,
45
-
2. declared types of vals for their right hand sides,
46
-
3. declared result types of defs for their right hand sides,
47
-
2. declared types of vars for their initializers,
48
-
4. declared types of vars for the right hand sides of assignments to them,
49
-
5. declared types of seq literals for the elements in that seq literal,
50
-
51
-
In cases (1) and (2) above we map all covariant occurrences of cap
52
-
in the original expected type to a wildcard (i.e. a fluid capture set). This still treats
53
-
`try` blocks correctly, since even if the expected type of a try block contains wildcards,
54
-
we still need to eliminate capabilities defined in the try through avoidance, and that
55
-
will not be possible since we cannot widen to cap via subtyping.
56
-
57
-
- Technicalities for type comparers and type maps:
58
-
59
-
1. Subcapturing: When applying rule (sc-var), we need to make sure that the
60
-
capture set of the info of a ref does not contain `cap`. In the case where
61
-
this capture set is a variable, we can use `disallowRootCapability`.
62
-
63
-
2. Lubs of capture sets can now contain at the same time `cap` and other
64
-
references.
65
-
66
-
3. Avoidance maps can have undefined results. We need to tweak the part
67
-
of AvoidMap that widens from a TermRef `ref` to `ref.info`, so that
68
-
this is forbidden if the `ref.info` contains `cap`. This is similar
69
-
to the restriction of subcapturing. It could be achieved by disallowing
70
-
the root capability in a capeture set that arises from mapping a capture
71
-
set through avoidance, using a handler that issues an appropriate error message.
72
-
73
-
- There is no longer a need for mutable variables and results of try's to be boxed.
74
-
75
-
- The resulting system is significantly less expressive than the TOPLAS version since
76
-
we no longer support return types or variables capturing `cap`. But we make
77
-
up for it through the introduction of reach capabilities (see following items).
78
-
79
-
- For any immutable variable `x`, introduce a capability `x*` which stands for
80
-
"all capabilities reachable through `x`". We have `{x} <: {x*} <: {cap}`.
4
+
- Our starting point is the currently implemented system, where encapsulation is achieved by disallowing root capabilities in
5
+
the types of sealed type variables. By contrast no restrictions apply
6
+
to boxing or unboxing.
7
+
8
+
- We now treat all type variables as sealed (so no special `sealed` modifier is necessary anymore). A type variable cannot be instantiated to a type that contains a covariant occurrence of `cap`. The same restriction applies to the types of mutable variables and try expressions.
81
9
82
10
- We modify the VAR rule as follows:
83
11
@@ -89,9 +17,8 @@ A capture checking variant
89
17
(2) all covariant occurrences of cap replaced by `x*`. (1) is standard,
90
18
whereas (2) is new.
91
19
92
-
- Why is this sound? Covariant occurrences of cap must represent capabilities
93
-
that are reachable from `x`, so they are included in the meaning of `{x*}`.
94
-
20
+
- Why is this sound? Covariant occurrences of cap must represent capabilities that are reachable from `x`, so they are included in the meaning of `{x*}`. At the same time, encapsulation is still maintained since no covariant occurrences of cap are allowed in instance types of
21
+
type variables.
95
22
96
23
Examples:
97
24
@@ -106,27 +33,27 @@ Note that type parameters no longer need (or can) be annotated with `sealed`.
106
33
The following example does not work.
107
34
```scala
108
35
defrunAll(xs: List[Proc]):Unit=
109
-
varcur:List[Proc] = xs // error: xs: List[() ->{xs} Unit], can't be widened to List[Proc]
36
+
varcur:List[Proc] = xs // error: Illegal type for var
110
37
while cur.nonEmpty do
111
38
valnext: () =>Unit= cur.head
112
39
next()
113
40
cur = cur.tail
114
41
115
42
usingFile: f =>
116
-
cur = ((() => f.write()): (() ->{f*} Unit)) ::Nil// error since we cannot widen {f*} to {cap} under box
43
+
cur = ((() => f.write()): (() ->{f*} Unit)) ::Nil
117
44
```
118
45
Same with refs:
119
46
```scala
120
47
defrunAll(xs: List[Proc]):Unit=
121
-
valcur=Ref[List[Proc]](xs: List[() ->{xs*} Unit]) // error, since we cannot widen {xs*} to {cap}
48
+
valcur=Ref[List[Proc]](xs: List[() ->{xs*} Unit]) // error, illegal type for type argument to Ref
122
49
while cur.get.nonEmpty do
123
50
valnext: () =>Unit= cur.get.head
124
51
next()
125
52
cur.set(cur.get.tail:List[Proc])
126
53
127
54
usingFile: f =>
128
55
cur.set:
129
-
(() => f.write(): () ->{f*} Unit) ::Nil// error since we cannot widen {f*} to {cap}
56
+
(() => f.write(): () ->{f*} Unit) ::Nil
130
57
```
131
58
132
59
The following variant makes the loop typecheck, but
@@ -153,31 +80,23 @@ def runAll(xs: List[Proc]): Unit =
153
80
cur.set(cur.get.tail:List[() ->{xs*} Unit])
154
81
155
82
usingFile: f =>
156
-
cur = (() => f.write(): () ->{f*} Unit) ::Nil// error since {f*} !<: {xs*}
0 commit comments