Skip to content

Commit 35a00cd

Browse files
committed
More documentation for safe initialization
1 parent 7e20b81 commit 35a00cd

File tree

1 file changed

+110
-10
lines changed

1 file changed

+110
-10
lines changed

docs/_docs/reference/other-new-features/safe-initialization.md

Lines changed: 110 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/safe-
66

77
Scala 3 implements experimental safe initialization check, which can be enabled by the compiler option `-Ysafe-init`.
88

9+
The design and implementation of the initialization checker is described in the
10+
paper _Safe object initialization, abstractly_ [3].
11+
912
## A Quick Glance
1013

1114
To get a feel of how it works, we first show several examples below.
@@ -105,8 +108,8 @@ By _reasonable usage_, we include the following use cases (but not restricted to
105108

106109
## Principles
107110

108-
To achieve the goals, we uphold three fundamental principles:
109-
_stackability_, _monotonicity_ and _scopability_.
111+
To achieve the goals, we uphold following fundamental principles:
112+
_stackability_, _monotonicity_, _scopability_ and _authority_.
110113

111114
Stackability means that all fields of a class are initialized at the end of the
112115
class body. Scala enforces this property in syntax by demanding that all fields
@@ -159,41 +162,137 @@ Monotonicity is based on a well-known technique called _heap monotonic
159162
typestate_ to ensure soundness in the presence of aliasing
160163
[1]. Roughly speaking, it means initialization state should not go backwards.
161164

162-
Scopability means that there are no side channels to access to partially constructed objects. Control effects like coroutines, delimited
165+
Scopability means that there are no side channels to access to partially
166+
constructed objects. Control effects like coroutines, delimited
163167
control, resumable exceptions may break the property, as they can transport a
164168
value upper in the stack (not in scope) to be reachable from the current scope.
165169
Static fields can also serve as a teleport thus breaks this property. In the
166170
implementation, we need to enforce that teleported values are transitively
167171
initialized.
168172

169-
The principles enable _local reasoning_ of initialization, which means:
173+
The three principles above contribute to _local reasoning about initialization_,
174+
which means:
170175

171176
> An initialized environment can only produce initialized values.
172177
173178
For example, if the arguments to an `new`-expression are transitively
174179
initialized, so is the result. If the receiver and arguments in a method call
175180
are transitively initialized, so is the result.
176181

182+
Local reasoning about initialization is the key to give rise to a fast
183+
initialization checker, as it avoids whole-program analysis.
184+
185+
The principle of authority goes hand-in-hand with monotonicity: the principle
186+
of monotonicity stipulates that initialization states cannot go backwards, while
187+
the principle of authority stipulates that the initialization states may not
188+
go forward at arbitrary locations due to aliasing. In Scala, we may only
189+
advance initialization states of objects in the class body when a field is
190+
defined with a mandatory initializer or at local reasoning points when the object
191+
becomes transitively initialized.
192+
193+
## Abstract Values
194+
195+
There are three fundamental abstractions for initialization states of objects:
196+
197+
- __Cold__: A cold object may have uninitialized fields.
198+
- __Warm__: A warm object has all its fields initialized but may reach _cold_ objects.
199+
- __Hot__: A hot object is transitively initialized, i.e., it only reaches warm objects.
200+
201+
In the initialization checker, the abstraction `Warm` is refined to handle inner
202+
classes and multiple constructors:
203+
204+
- __Warm[C] { outer = V, ctor, args = Vs }__: A warm object of class `C`, where the immediate outer of `C` is `V`, the constructor is `ctor` and constructor arguments are `Vs`.
205+
206+
The initialization checker checks each class separately. The abstraction `ThisRef`
207+
represents the current object under initialization:
208+
209+
- __ThisRef[C]__: The current object of class `C` under initialization.
210+
211+
The initialization state of the current object is stored in the heap as an
212+
abstract object. The abstract heap also serves as a cache for the field values
213+
of warm objects.
214+
215+
Two more abstractions are introduced to support functions and conditional
216+
expressions:
217+
218+
- __Fun(e, V, C)__: A abstract function value where `e` is the code, `V` is the
219+
abstract value for `this` inside the function body and the function is located
220+
inside the class `C`.
221+
222+
- __Refset(Vs)__: A set of abstract values `Vs`.
223+
177224
## Rules
178225

179226
With the established principles and design goals, following rules are imposed:
180227

181-
1. In an assignment `o.x = e`, the expression `e` may only point to transitively initialized objects.
228+
1. The field access `e.f` or method call `e.m()` is illegal if `e` is _cold_.
229+
230+
A cold value should not be used.
231+
232+
2. The field access `e.f` is invalid if `e` has the value `ThisRef` and `f` is not initialized.
233+
234+
3. In an assignment `o.x = e`, the expression `e` must be _effectively hot_.
182235

183236
This is how monotonicity is enforced in the system. Note that in an
184-
initialization `val f: T = e`, the expression `e` may point to an object
185-
under initialization. This requires a distinction between mutation and
186-
initialization in order to enforce different rules. Scala
187-
has different syntax for them, it thus is not an issue.
237+
initialization `val f: T = e`, the expression `e` may point to an non-hot
238+
value.
188239

189-
2. Objects under initialization may not be passed as arguments to method calls.
240+
4. Arguments to method calls must be _effectively hot_.
190241

191242
Escape of `this` in the constructor is commonly regarded as an anti-pattern.
243+
192244
However, escape of `this` as constructor arguments are allowed, to support
193245
creation of cyclic data structures. The checker will ensure that the escaped
194246
non-initialized object is not used, i.e. calling methods or accessing fields
195247
on the escaped object is not allowed.
196248

249+
An exception is for calling synthetic `apply`s of case classes. For example,
250+
the method call `Some.apply(e)` will be interpreted as `new Some(e)`, thus
251+
is valid even if `e` is not hot.
252+
253+
Another exception too this rule is parametric method calls. For example, in
254+
`List.apply(e)`, the argument `e` may be non-hot. If that is the case, the
255+
result value of the parametric method call is taken as _cold_.
256+
257+
5. Method calls on hot values with effectively hot arguments product hot results.
258+
259+
This rule is assured by local reasoning about initialization.
260+
261+
6. Method calls on `ThisRef` and warm values will be resolved statically and the
262+
method bodies will be checked.
263+
264+
7. In a new expression `new p.C(args)`, if the values of `p` and `args` are
265+
effectively hot, then the result value is also hot.
266+
267+
This is assured by local reasoning about initialization.
268+
269+
8. In a new expression `new p.C(args)`, if any value of `p` and `args` is not
270+
effectively hot, then the result value takes the form `Warm[C] { outer = Vp, args = Vargs }`.
271+
272+
In the above, `Vp` is the widened value of `p` --- the widening happens if `p`
273+
is a warm value `Warm[D] { outer = V, args }` and we widen it to
274+
`Warm[D] { outer = Cold, args }`.
275+
276+
The variable `Vargs` represents values of `args` with non-hot values widened
277+
to `Cold`.
278+
279+
The widening is motivated to finitize the abstract domain and ensure
280+
terimination of the initialization check.
281+
282+
9. The scrutinee in pattern match, the values in return and throw statements must be _effectively hot_.
283+
284+
A value `v` is _effectively hot_ if any of the following is true:
285+
286+
- `v` is `Hot`.
287+
- `v` is `ThisRef` and all fields of the underlying object are initialized.
288+
- `v` is `Warm[C] { outer = V, ctor, args = Vs }` and
289+
1. `C` does not contain inner classes;
290+
2. Calling any method on `v` encounters no errors and the method return value is _effectively hot_;
291+
3. Each field of `v` is _effectively hot_.
292+
- `v` is `Fun(e, V, C)` and calling the function encounters no errors and the
293+
function return value is _effectively hot_.
294+
- `ThisRef` is _effectively hot_.
295+
197296
## Modularity
198297

199298
The analysis takes the primary constructor of concrete classes as entry points.
@@ -234,3 +333,4 @@ mark some fields as lazy.
234333

235334
1. Fähndrich, M. and Leino, K.R.M., 2003, July. [_Heap monotonic typestates_](https://www.microsoft.com/en-us/research/publication/heap-monotonic-typestate/). In International Workshop on Aliasing, Confinement and Ownership in object-oriented programming (IWACO).
236335
2. Fengyun Liu, Ondřej Lhoták, Aggelos Biboudis, Paolo G. Giarrusso, and Martin Odersky. 2020. [_A type-and-effect system for object initialization_](https://dl.acm.org/doi/10.1145/3428243). OOPSLA, 2020.
336+
3. Fengyun Liu, Ondřej Lhoták, Enze Xing, Nguyen Cao Pham. 2021 [_Safe object initialization, abstractly_](https://dl.acm.org/doi/10.1145/3486610.3486895)

0 commit comments

Comments
 (0)