dart-lang / dart-lang/language
Current "covariance by default" paradigm is not respected by the compiler
- Dominant language
- TeX
- Stars
- 2.9k
- Forks
- 239
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 14
Description
After some digging around old issues, I can see there has been some discussion in the past about providing a more robust type system to Dart by including specific covariant/contravariant type definitions as provided by other languages (java, kotlin, etc).
That being said, it is my understanding that, currently, the language design is to consider everything to be "covariant by default":
> As of Dart 2.4 or earlier, every type variable declared for a generic class is considered covariant
(from [this great issue from 2019](https://github.com/dart-lang/language/issues/524)).
Which is not, as discussed, sound (nor safe) -- and I would love to see that addressed. But I am not sure the status of these proposals, as there are many open, old issues from the community about it. And while I would much prefer that to be the answer to this, I believe that even when considering the current rule, there is an issue with the compiler implementation (if I am not misinterpreting these statements).
Assuming the "covariant by default" rule, a `List` should _be_ a `List`, for all intents and purposes:
```dart
void take(List ns) {}
void main() {
take([]); // ok
take([]); // ok
}
```
However, this does not work when the generics are a bit more involved. See an example below: ([dartpad](https://dartpad.dev/?id=5742674b550cfd66beee51b67772e444)):
```dart
void take>(List list) {}
void main() {
take([]); // ok -- T = num
take([]); // not ok -- does no compile, it is trying to resolve T=double instead of the also valid T=num
take([] as List); // ok, teach the compiler. but warns and wants to "auto-fix"
}
```
Here, my function generics satisfy `List`. But for some reason, it does not compile for `List`. Of course `double` itself does not satisfy `T extends Comparable`, but `num` does. I believe the _current_ "covariance by default" rule is being violated here, as my `List` should be treated as a `List` to satisfy the type constraint.
To make it even more weird, see how I can cast the `List` to `List` and then it totally works! But then the compiler correctly points out that since dart is "covariant by default" the cast is unnecessary, and the IDE will auto-fix to remove the cast. This removal actually causes the code to break.
The way I am understanding this, this is a violation of the current rule, but as pointed out in many issues, the current rule isn't good to begin with. So my question/proposal is:
1. am I missing something on my analysis? is there current any way to accomplish this?
2. is there any ongoing plan to add something akin to Java's wildcard + `T super U` syntax, or explicit covariant/contravariant modifiers like kotlin's `in`, `out`, `inout`?
Because if not, I believe this should be treated as a bug as `take([])` should work with the current ruling, as far as I understand it. And that might be a much quicker fix than to get the actual spec sorted out. Also, having the compiler warning/auto-fix generate non-compiling code is odd.
The reason I am asking this is that this not an abstract, far-off, edge case that I machinated; having a function that takes a list whose elements can be compared seems to be like a very common use case (e.g. sorting, finding min, max, etc). And not being able to pass a list of `double` or `int` because of how they implement `Comaprable` limits our design choices.
If there are approved plans to solve this by the root cause though -- and make the type system more sound--, I am happy to wait for that solution instead, as that would allow for even more cool & safe code :)
Contributor guide
Research direction
Reproduce the supplied DartPad example first and compare its generic type inference and covariance behavior with the current Dart language rules. No repository file or test is named; done requires deciding whether the compiler and auto-fix violate the existing rules or whether a language-design change is needed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100