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
Copy file name to clipboardExpand all lines: docs/_docs/contributing/scaladoc.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -74,8 +74,8 @@ You can also run specific signature tests with `testOnly`,
74
74
for example `scaladoc/testOnly *scaladoc.signatures.MarkdownCode`.
75
75
76
76
Most tests rely on comparing signatures (of classes, methods, objects etc.) extracted from the generated documentation
77
-
to signatures found in source files (extracted using Scalameta). Such tests are defined using [SignatureTest](test/dotty/tools/scaladoc/signatures/SignatureTest.scala) class
78
-
and its subtypes (such as [TranslatableSignaturesTestCases](test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala)). In this style of test, it's not necessary for expected output to be included, because the test is its own specification.
77
+
to signatures found in source files (extracted using Scalameta). Such tests are defined using [SignatureTest](https://github.com/scala/scala3/blob/main/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala) class
78
+
and its subtypes (such as [TranslatableSignaturesTestCases](https://github.com/scala/scala3/blob/main/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala)). In this style of test, it's not necessary for expected output to be included, because the test is its own specification.
79
79
80
80
WARNING: As the classes mentioned above are likely to evolve, the description below might easily get out of date.
81
81
In case of any discrepancies rely on the source files instead.
@@ -84,7 +84,7 @@ In case of any discrepancies rely on the source files instead.
84
84
the names of directories containing corresponding TASTY files
85
85
and the kinds of signatures from source files (corresponding to keywords used to declare them like `def`, `class`, `object` etc.)
86
86
whose presence in the generated documentation will be checked (other signatures, when missing, will be ignored).
87
-
The mentioned source files should be located directly inside [](../scaladoc-testcases/src/tests) directory
87
+
The mentioned source files should be located directly inside the [scaladoc-testcases](https://github.com/scala/scala3/tree/main/scaladoc-testcases) directory
88
88
but the file names passed as parameters should contain neither this path prefix nor `.scala` suffix.
89
89
90
90
By default it's expected that all signatures from the source files will be present in the documentation
Copy file name to clipboardExpand all lines: docs/_docs/reference/experimental/capture-checking/basics.md
+4-1Lines changed: 4 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -66,7 +66,8 @@ val xs = usingLogFile { f =>
66
66
}
67
67
```
68
68
An error would be issued in the second case, but not the first one (this assumes a capture-aware
69
-
formulation `LzyList` of lazily evaluated lists, which we will present later in this page).
69
+
formulation `LzyList` of lazily evaluated lists, which we will present later in the chapter
70
+
on [capture checking classes](classes.md)).
70
71
71
72
It turns out that capture checking has very broad applications. Besides the various
72
73
try-with-resources patterns, it can also be a key part to the solutions of many other long standing problems in programming languages. Among them:
@@ -346,6 +347,8 @@ loophole()
346
347
But this will not compile either, since the capture set of the mutable variable `loophole` cannot refer to variable `f`, which is not visible
347
348
where `loophole` is defined.
348
349
350
+
### Monotonicity Rule
351
+
349
352
Looking at object graphs, we observe a monotonicity property: The capture set of an object `x` covers the capture sets of all objects reachable through `x`. This property is reflected in the type system by the following _monotonicity rule_:
350
353
351
354
- In a class `C` with a field `f`, the capture set `{this}` covers the capture set `{this.f}` as well as the capture set of any application of `this.f` to pure arguments.
Copy file name to clipboardExpand all lines: docs/_docs/reference/experimental/capture-checking/classes.md
+70-6Lines changed: 70 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -45,7 +45,9 @@ Here class `Super` has local capability `a`, which gets inherited by class
45
45
`Sub` and is combined with `Sub`'s own local capability `b`. Class `Sub` also has an argument capability corresponding to its parameter `x`. This capability gets instantiated to `c` in the final constructor call `Sub(c)`. Hence,
46
46
the capture set of that call is `{a, b, c}`.
47
47
48
-
The capture set of the type of `this` of a class is inferred by the capture checker, unless the type is explicitly declared with a self type annotation like this one:
48
+
## Capture Set of This
49
+
50
+
The capture set of the type of `this` of a class is inferred by the capture checker, unless the type is explicitly declared with a self-type annotation like this one:
49
51
```scala
50
52
classC:
51
53
self: D^{a, b} => ...
@@ -73,7 +75,69 @@ we know that the type of `this` must be pure, since `this` is the right hand sid
73
75
| of the enclosing class A
74
76
```
75
77
76
-
## Capture Tunnelling
78
+
### Traits and Open Classes
79
+
80
+
The self-type inference behaves differently depending on whether all subclasses of a class are known. For a regular (non-open, non-abstract) class, all subclasses are known at compile time,¹ so the capture checker can precisely infer the self-type. However, for traits, abstract classes, and [`open`](../../other-new-features/open-classes.md) classes, arbitrary subclasses may exist, so the capture checker conservatively assumes that `this` may capture arbitrary capabilities
81
+
(i.e., it infers the universal capture set `cap`).
82
+
83
+
¹We ignore here the possibility that non-open classes have subclasses in other compilation units (e.g. for testing) and assume that these subclasses do not change the inferred self type.
84
+
85
+
For example (assuming all definitions are in the same file):
86
+
```scala
87
+
classA:
88
+
deffn:A=this// ok
89
+
90
+
traitB:
91
+
deffn:B=this// error
92
+
deffn2:B^=this// ok
93
+
94
+
abstractclassC:
95
+
deffn:C=this// error
96
+
deffn2:C^=this// ok
97
+
98
+
sealedabstractclassD:
99
+
deffn:D=this// ok
100
+
101
+
objectD0extendsD
102
+
103
+
openclassE:
104
+
deffn:E=this// error
105
+
deffn2:E^=this// ok
106
+
```
107
+
108
+
### Inheritance
109
+
110
+
The capture set of `this` of a class or trait also serves as an upper bound of the possible capture
This might seem surprising. The `Pair(x, y)` value does capture capabilities `ct` and `fs`. Why don't they show up in its type at the outside?
95
159
96
-
The answer is capture tunnelling. Once a type variable is instantiated to a capturing type, the
160
+
The answer is capture tunneling. Once a type variable is instantiated to a capturing type, the
97
161
capture is not propagated beyond this point. On the other hand, if the type variable is instantiated
98
162
again on access, the capture information "pops out" again. For instance, even though `p` is technically pure because its capture set is empty, writing `p.fst` would record a reference to the captured capability `ct`. So if this access was put in a closure, the capability would again form part of the outer capture set. E.g.
99
163
```scala
@@ -144,7 +208,7 @@ end LzyCons
144
208
The `LzyCons` class takes two parameters: A head `hd` and a tail `tl`, which is a function
145
209
returning a `LzyList`. Both the function and its result can capture arbitrary capabilities.
146
210
The result of applying the function is memoized after the first dereference of `tail` in
147
-
the private mutable field `cache`. Note that the typing of the assignment `cache = tl()` relies on the monotonicity rule for `{this}` capture sets.
211
+
the private mutable field `cache`. Note that the typing of the assignment `cache = tl()` relies on the [monotonicity rule](basics.md#monotonicity-rule) for `{this}` capture sets.
148
212
149
213
Here is an extension method to define an infix cons operator `#:` for lazy lists. It is analogous
150
214
to `::` but instead of a strict list it produces a lazy list without evaluating its right operand.
@@ -204,7 +268,7 @@ Their capture annotations are all as one would expect:
204
268
- Filtering a lazy list produces a lazy list that captures the original list as well
205
269
as the (possibly impure) filtering predicate.
206
270
- Concatenating two lazy lists produces a lazy list that captures both arguments.
207
-
- Dropping elements from a lazy list gives a safe approximation where the original list is captured in the result. In fact, it's only some suffix of the list that is retained at run time, but our modelling identifies lazy lists and their suffixes, so this additional knowledge would not be useful.
271
+
- Dropping elements from a lazy list gives a safe approximation where the original list is captured in the result. In fact, it's only some suffix of the list that is retained at run time, but our modeling identifies lazy lists and their suffixes, so this additional knowledge would not be useful.
208
272
209
273
Of course the function passed to `map` or `filter` could also be pure. After all, `A -> B` is a subtype of `(A -> B)^{cap}` which is the same as `A => B`. In that case, the pure function
210
274
argument will _not_ show up in the result type of `map` or `filter`. For instance:
@@ -218,7 +282,7 @@ argument does not show up since it is pure. Likewise, if the lazy list
218
282
This demonstrates that capability-based
219
283
effect systems with capture checking are naturally _effect polymorphic_.
220
284
221
-
This concludes our example. It's worth mentioning that an equivalent program defining and using standard, strict lists would require no capture annotations whatsoever. It would compile exactly as written now in standard Scala 3, yet one gets the capture checking for free. Essentially, `=>` already means "can capture anything" and since in a strict list side effecting operations are not retained in the result, there are no additional captures to record. A strict list could of course capture side-effecting closures in its elements but then tunnelling applies, since
285
+
This concludes our example. It's worth mentioning that an equivalent program defining and using standard, strict lists would require no capture annotations whatsoever. It would compile exactly as written now in standard Scala 3, yet one gets the capture checking for free. Essentially, `=>` already means "can capture anything" and since in a strict list side effecting operations are not retained in the result, there are no additional captures to record. A strict list could of course capture side-effecting closures in its elements but then tunneling applies, since
222
286
these elements are represented by a type variable. This means we don't need to annotate anything there either.
223
287
224
288
Another possibility would be a variant of lazy lists that requires all functions passed to `map`, `filter` and other operations like it to be pure. E.g. `map` on such a list would be defined like this:
Copy file name to clipboardExpand all lines: docs/_docs/reference/experimental/capture-checking/internals.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -34,7 +34,7 @@ that gets propagated and resolved further out.
34
34
35
35
When a mapping `m` is performed on a capture set variable `C`, a new variable `Cm` is created that contains the mapped elements and that is linked with `C`. If `C` subsequently acquires further elements through propagation, these are also propagated to `Cm` after being transformed by the `m` mapping. `Cm` also gets the same supersets as `C`, mapped again using `m`.
36
36
37
-
One interesting aspect of the capture checker concerns the implementation of capture tunnelling. The [foundational theory](https://infoscience.epfl.ch/record/290885) on which capture checking is based makes tunnelling explicit through so-called _box_ and
37
+
One interesting aspect of the capture checker concerns the implementation of capture tunneling. The [foundational theory](https://infoscience.epfl.ch/record/290885) on which capture checking is based makes tunneling explicit through so-called _box_ and
38
38
_unbox_ operations. Boxing hides a capture set and unboxing recovers it. The capture checker inserts virtual box and unbox operations based on actual and expected types similar to the way the type checker inserts implicit conversions. When capture set variables are first introduced, any capture set in a capturing type that is an instance of a type parameter instance is marked as "boxed". A boxing operation is
39
39
inserted if the expected type of an expression is a capturing type with
40
40
a boxed capture set variable. The effect of the insertion is that any references
0 commit comments