-
Notifications
You must be signed in to change notification settings - Fork 219
Description
During the implementation of patterns, we decided that irrefutable patterns shouldn't cause type promotions to happen. For example, this promotes d
from type dynamic
to int
:
main() {
dynamic d = 0;
if (d case int x) {
d; // Static type: `int`
}
}
but this doesn't:
main() {
dynamic d = 0;
var (int x) = d;
d; // Static type: `dynamic`
}
The reason we decided this was primarily so that pattern assignments and pattern variable declarations would behave consistently with ordinary assignments and variable declarations (see past discussion).
Unfortunately, the way I implemented this essentially caused all promotion related to the scrutinee of the irrefutable pattern match to be disabled, including field promotions. So, for example, if a field is promoted before the irrefutable pattern match, that promotion is "forgotten" during the irrefutable pattern match:
class A {
final int? _i;
A(this._i);
}
main() {
var a = A(0);
a._i!; // Promotes `a._i` to `int`
var A(_i: i) = a; // Promotion is forgotten, therefore:
int j = i; // ERROR: `int?` not assignable to `int`
}
But it all works works fine if the pattern match is refutable:
// Same definition of `class A`
main() {
var a = A(0);
a._i!; // Promotes `a._i` to `int`
if (a case A(_i: var i)) {
int j = i; // OK: `i` has type `int`.
}
}
Similarly, a field promotion performed by !
or as
on the inside of the left hand side of an irrefutable pattern match doesn't have any effect on the code that follows:
// Same definition of `class A`
main() {
var a1 = A(0);
var A(_i: i1!) = a1;
int j = a1._i; // ERROR: `int?` not assignable to `int`
var a2 = A(0);
var A(_i: i2 as int) = a2;
int k = a2._i; // ERROR: `int?` not assignable to `int`
}
Whereas these same promotions do have an effect after a refutable pattern match:
// Same definition of `class A`
main() {
var a1 = A(0);
if (a1 case A(_i: _!)) {}
int j = a1._i; // OK: `a1._i` has been promoted to `int`
var a2 = A(0);
if (a2 case A(_i: _ as int)) {}
int k = a2._i; // OK: `a2._i` has been promoted to `int`
}
This feels weird and surprising to me, and it feels like it goes well beyond the original intent.
I'm tempted to change the rules so that irrefutable pattern matches handle field promotion in the same way as refutable pattern matches, and the only difference in promotion between irrefutable and refutable patterns is that an irrefutable pattern match never promotes the type of the scrutinee itself. I think this would be much more in keeping with the original intent, and less confusing to users.
(See #4344 (comment) for further context.)
@dart-lang/language-team What do you think?