Promote local variables based on matching patterns when in record literals, too
Consider the following example:
void main() {
Object x = 1;
var y = true;
final Object? z = 'Hello!';
switch ((x, y, z)) {
case (int(), true, String()):
print('${x.isEven}, ${y && y}, ${z.length}');
// Further cases, can be seen as a "matrix" of conditions.
// case (..., ..., ...): ...
// case (..., ..., ...): ...
// case (..., ..., ...): ...
}
}
This example has type errors (with the analyzer as well as the CFE, SDK 3.11.0-16.0.dev) because no promotion occurs for x even though the record pattern gives rise to a type query (using the object pattern int()). Similarly for z and String().
I think it would be useful if a promotion could be performed for the corresponding pair of a record component (like x) and the corresponding record component pattern (like int()), because this lets developers ask questions about several objects simultaneously, yielding a pattern match which is kind of matrix shaped.
The support for promoting a promotable local variable using this kind of correspondence is already available when it occurs alone:
void main() {
Object x = 1;
var y = true;
final Object? z = 'Hello!';
switch (x) {
case int():
switch (y) {
case true:
switch (z) {
case String():
print('${x.isEven}, ${y && y}, ${z.length}'); // OK!
}
case false: // No-op.
}
}
}
The nested approach does allow the variables to be promoted, but it is much more verbose than the "matrix" approach using record literals and record patterns.
The promotion of variables that occur as components of a record literal is a very narrow special case. A generalization that could work is nested records (so if ((1, (2, y))) case (_, (_, String())) {...} would promote y to String in the body). On the other hand, promoting variables that occur as elements in other non-atomic collection patterns (lists, maps) would probably not be very useful.
We have discussed this topic earlier, or at least some similar ideas, but apparently there is no issue on it.
One worry would be that this is special to the switch expression, which might be confusing.
Doing switch ((x, y, z)) { .... } works, but var triple = (x, y, z); switch (triple) { ... } doesn't. (Or if it does, it requires a smarter flow analysis to preserve that triple.x aliases x, which it might not unless x is final.