Skip to content

Commit fd0d6ff

Browse files
authored
Add section on records being constant. (#2413)
Adds section on constant behavior (records can be constant and potentially constant). Adds canonicalization rules for non-records that contain records. Currently a non-record is canonicalized based on the `identical` comparison of its fields against previously created constant values of the same type. Since a record field does not have a predictably or useful `identical` comparison result, we instead define a comparison structurally on the record, and use that instead.
1 parent 4c4c0ba commit fd0d6ff

File tree

1 file changed

+71
-0
lines changed

1 file changed

+71
-0
lines changed

working/0546-patterns/records-feature-specification.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,77 @@ fields are) and collection literals.
283283

284284
**TODO: Specify this more precisely.**
285285

286+
### Constants
287+
288+
_Record expressions can be constant and potentially constant expressions._
289+
290+
A record expression is a compile-time constant expression
291+
if and only if all its record field expressions are compile-time constant expressions.
292+
293+
_This is true whether the expression occurs in a constant context or not,
294+
which means that a record expression can be used directly as a parameter default value
295+
if its record field expressions are constant expressions.
296+
Example: `f({(int, int) x = (1, 2)}) => ...`._
297+
298+
A record expression is a potentially constant expression
299+
if and only iff all its record field expressions are potentially constant or constant expressions.
300+
301+
_This means that a record expression can be used in the initializer list
302+
of a constant non-redirecting generative constructor,
303+
and can depend on constructor parameters._
304+
305+
_Constant *object* instantiations create deeply immutable and canonicalied objects.
306+
Records are always unmodifiable, and if their field values are deeply immutable,
307+
like constants values, the records are also deeply immutable.
308+
It's meaningless to consider whether record constants are canonicalized,
309+
since records do not have a persistent identity._
310+
311+
_Because of that, there is no need for a `const (1, 2)` syntax to force a record
312+
to be a constant, like there is for object creation expressions.
313+
A record expression with field values that are constant-created values,
314+
will be indistinguishable from a similar expression created in a constant
315+
context, since identity cannot be used as a distinguishing trait._
316+
317+
_(We could choose to promise that a compile-time constant `identical(c1, c2)`,
318+
where the expression occurs in a constant context and `c1` and `c2` are records,
319+
will evaluate to `true` iff a runtime evaluation of `identical`
320+
*can* return `true` for the same values.
321+
That is, records would be canonicalized during compile-time constant evealuation,
322+
but may lose their identity at runtime. We will not make such a promise.)_
323+
324+
For canonoicalization purposes, we update the definition of when to canonicalize
325+
the result of a constant object creation expression to not be dependent on
326+
the `identical` function, since it does not behave predictably (or usefully)
327+
for records.
328+
329+
We define two Dart values, *a* and *b*, to be _structurally equivalent_ as follows:
330+
* If *a* and *b* are both records, and they have the same shape,
331+
and for each field *f* of that shape, the records' values of that field,
332+
*a*<sub>*f*</sub> and *b*<sub>*f*</sub> are structurally equivalent,
333+
then *a* and *b* are structurally equivalent.
334+
* If *a* and *b* are non-record object references,
335+
and they refer to the same object, then *a* and *b* are structurally equivalent.
336+
_So structural equivalence agrees with `identical` for non-records._
337+
* Otherwise *a* and *b* are not structurally equivalent.
338+
339+
With that definition, the rules for object and collection canonicalization is changed
340+
from requiring that instance variable, list/set element and map key/value values are
341+
`identical` between the instances, to them being _structurally equivalent_.
342+
343+
_This change allows a class like_
344+
```dart
345+
class C {
346+
final (int, int) pair;
347+
const C(int x, int y) : pair = (x, y);
348+
}
349+
```
350+
_to be properly canonicalized for objects with the same effective state,
351+
independentlty of whether `identical` returns `true` or `false` on the `pair` value._
352+
353+
_Notice that if the `identical`returns `true` on two records, they must be structurally equivalent,
354+
but unlike for non-records, the `identical` function can also return `false`
355+
for structurally equivalent records._
356+
286357
## Runtime semantics
287358

288359
### Records

0 commit comments

Comments
 (0)