dart-lang / dart-lang/language
What guarantees do we have around the runtimeType of primitive types?
- Dominant language
- TeX
- Stars
- 2.9k
- Forks
- 239
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 14
Description
The co19 tests contain a [test for the runtime type semantics](https://github.com/dart-lang/co19/blob/master/LanguageFeatures/Records/runtime_type_A01_t01.dart) of records that looks like (simplified):
```dart
Type typeOf() => X;
main() {
(num, Object) r = (1, 2.3);
Expect.equals(typeOf<(int, double)>(), r.runtimeType);
}
```
This test implicitly relies on an assumption that `int` and `double` are effectively final types. By that, I mean that the runtime type of any int or double literal (or, I guess, at least `1` and `2.3`) will be `int` and `double` *exactly* and not some subtype of them (like `JSInt`, `_Smi`, etc.).
The test could alternately be written like:
```dart
Type typeOf() => X;
class A {}
class B {}
main() {
(Object, Object) r = (A(), B());
Expect.equals(typeOf<(A, B)>(), r.runtimeType);
}
```
It would still be testing the same functionality of record runtime types, but wouldn't rely on any policy of the built-in core library types. I believe we'll suggest that the test should be changed to that just to minimize the scope of what the test presumes.
However, that does still raise the question of what policy we allow users to rely on for the built-in core library types. Is it the case that all calls to `.runtimeType` on instances of `int` will return the same (equal? identical?) `Type` object? Likewise for the other built-in types?
It's obviously *not* the case for many other core types. For example:
```dart
var whereIterable = [1, 2, 3].where((n) => true);
var mapIterable = [1, 2, 3].map((n) => n);
(Iterable, Iterable) whereMap = (whereIterable, mapIterable);
(Iterable, Iterable) mapWhere = (mapIterable, whereIterable);
print(whereMap.runtimeType == mapWhere.runtimeType);
```
Here, the core libraries make absolutely no guarantee that this will print "true". And, in practice, it prints "false".
For the built-in primitive types, our implementations *do* in practice have subtypes of the declared core library type, sometimes multiple. But they try to obscure that fact and make them appear to have runtime types like `int`. It does sort of leak out sometimes in error messages. So it seems like we *try* to offer this policy.
But from talking to @rakudrama and @fishythefish, there is a code size and performance cost to fully guaranteeing that you can rely on every instance of `int` having the same runtime type.
Do we guarantee this? Should we? How much cost are we willing to push onto the implementers to do so?
Contributor guide
Research direction
Start with the co19 test in LanguageFeatures/Records/runtime_type_A01_t01.dart and read the issue discussion about primitive runtime types. Determine whether the language specification should state a guarantee and whether the record test should use user-defined classes; done means the policy and any required test changes are clearly agreed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100