Skip to content

Commit 8d7cb43

Browse files
committed
Add initial draft of static immutability
1 parent 0d094e4 commit 8d7cb43

File tree

1 file changed

+167
-0
lines changed

1 file changed

+167
-0
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# Shared immutable objects
2+
3+
4+
5+
Status: Draft
6+
7+
This describes a possible solution for:
8+
- [Communication between isolates](https://github.com/dart-lang/language/issues/124)
9+
- [Building immutable collections](https://github.com/dart-lang/language/issues/117)
10+
- [Unwanted mutation of lists in Flutter](https://github.com/dart-lang/sdk/issues/27755)
11+
12+
## Summary
13+
14+
This describes a way to declare classes that produce deeply immutable object
15+
graphs that are shared across isolates.
16+
17+
## Syntax
18+
19+
We add a section to class headers for expressing class and generic constraints,
20+
along with an "immutable" constraint.
21+
22+
```dart
23+
class Value<T> extends Scalar<T> implements Constant is immutable
24+
where T is immutable {
25+
}
26+
```
27+
28+
Mixin declarations may also be marked `immutable`.
29+
30+
Generic method headers may also express generic constraints.
31+
32+
```
33+
foo<S, T, where T is immutable>(Value<T> v) {
34+
35+
}
36+
```
37+
38+
### Alternative syntax
39+
40+
Instead of adding constraints, a simpler approach is to add a marker interface
41+
`Immutable`. The property expressed by the constraint `T is immutable` then
42+
becomes expressed by `implements Immutable` in the case of a class, or `T
43+
extends Immutable` in the case of a type variable `T`.
44+
45+
## Static checking
46+
A class marked with `immutable` is subject to the following additional static
47+
checks.
48+
49+
- Every field in an immutable class (including any superclass fields) must be
50+
final.
51+
- Every field in an immutable class (including any superclass fields) must have
52+
a static type which is immutable.
53+
- Every other class which implements the interface of an immutable class
54+
(including via extension or mixing in) must also be immutable.
55+
56+
The types `int`, `double`, `bool`, `String`, `Type`, and `Symbol` are considered
57+
immutable.
58+
59+
## Allocation of immutable objects
60+
61+
Immutable objects are allocated as usual in an isolate local
62+
nursery. (Alternatively, it might be preferable to maintain a separate isolate
63+
local shared object nursery for allocating only shared objects). However, when
64+
they are tenured, they are tenured to a global heap which is shared by all
65+
isolates in the process, and which is inhabited solely by immutable shared
66+
objects.
67+
68+
The shared object heap cannot have pointers into the isolate local heaps, and so
69+
garbage collection of an isolate local heap does not require coordination with
70+
other isolates.
71+
72+
The isolate local heap can have pointers into the shared global heap, and so
73+
either these must be tracked via write barriers and treated as roots when
74+
collecting the shared global heap, or else collection of the shared global heap
75+
might require cross-isolate coordination.
76+
77+
Tenuring objects into the shared global heap requires locking or pausing
78+
isolates. Bulk reservation of allocation regions could potentially be used to
79+
mitigate this.
80+
81+
Issue: It is possible that a large object may need to be tenured before it has
82+
been fully initialized. This would allow writes into the shared heap. This
83+
should not be problematic semantically since the object cannot be visible in
84+
other isolates prior to initialization, but it may complicate the GC model.
85+
This does not seem deeply problematic - a number of solutions seem plausible.
86+
87+
## Sharing of immutable objects
88+
89+
The SendPort class is extended with a new method `void share<T, where T is
90+
immutable>(T message)` which given a reference to an immutable object graph,
91+
shares that reference with all receivers of the SentPort. Note that the object
92+
is not copied since it and all sub-components of it are in the shared heap.
93+
94+
An object which is shared before it has been tenured will likely need to be
95+
tenured when it is shared.
96+
97+
It should be the case that every object is fully initialized before it can be
98+
shared. The intent of the static checks specified above are to guarantee this.
99+
100+
It should be the case that no object that has been shared can be mutated. The
101+
intent of the static checks specified above are to guarantee this.
102+
103+
## Immutable collections
104+
105+
The following additional immutable classes are added to the core libraries:
106+
`ImmutableList` which implements `List`, `ImmutableMap` which implements `Map`,
107+
and `ImmutableSet` which implements `Set`.
108+
109+
### Collection initialization
110+
Instances of these collections may be allocated and assigned to local variables
111+
in a modifiable state. Mutation operations may be performed on such an instance
112+
up until the first point at which the instance escapes (that is, is captured by
113+
a closure, is assigned to another variable or setter, or is passed as a
114+
parameter). It is a static error if a mutation operation is performed on an
115+
instance of one of these classes:
116+
- at any point not intra-procedurally dominated by the allocation point of the
117+
instance
118+
- at any point where the instance escapes along any path from the allocation
119+
point to the mutation operation.
120+
121+
Instances that are allocated to initialize fields or top level variables are
122+
always initialized in an umodifiable state.
123+
124+
### Runtime immutability
125+
As with the result of the current `List.unmodifiable` constructor, mutation
126+
operations on an instance of an immutable collection shall throw (except in the
127+
limited cases described in the initialization section above). Note that the
128+
static checks described above prevent mutation operations from being accessed on
129+
an instance of immutable type. However, the immutable collections implement
130+
their mutable interfaces, and hence the mutation operations may be reached by
131+
subsuming into the mutable type.
132+
133+
### Literals
134+
135+
A collection literal which appears in a context where the static type required
136+
by the context is an immutable collection type shall be allocated as an
137+
immutable collection.
138+
139+
```
140+
ImmutableList<int> l = [ 3 ];
141+
```
142+
Question: Do we need additional syntax for the case where a static type context
143+
is not required?
144+
145+
```
146+
var l = ^[3];
147+
```
148+
149+
## Immutable functions
150+
151+
There is no way to describe the type of an immutable function. If important, we
152+
could add a type for immutable closures. A function is immutable if every free
153+
variable of the function is immutable.
154+
155+
## Immutable top type
156+
157+
There is no top type for immutable types. It might be useful to have a type
158+
`Immutable`, to express the type of fields of immutable objects which are
159+
intended to hold instances of multiple types which do not otherwise share a
160+
common super-interface.
161+
162+
## Javascript
163+
164+
Currently, isolates are not supported in Javascript. If we revisit that, we are
165+
unlikely to be able to support this in full on the web. It is possible that we
166+
may be able to define a subset of immutable objects which can be implemented as
167+
a layer over shared typed data buffers.

0 commit comments

Comments
 (0)