I am encountering a non-exhaustive switch error when using a sealed hierarchy with two generic parameters. While I understand this is a known limitation, I wanted to highlight that enabling the experimental variance feature and marking the primary parameter as inout "invariant" does not resolve the issue. The compiler still fails to prove that the subclasses cover the full type space for a specific generic instance.
# analysis_options.yaml
analyzer:
enable-experiment:
- variance
abstract class Table {}
sealed class Criterion<inout T extends Table> {}
sealed class Constraint<inout T extends Table, V> extends Criterion<T> {}
final class BoolConstraint<inout T extends Table> extends Constraint<T, bool> {}
final class StringConstraint<inout T extends Table> extends Constraint<T, String> {}
void evaluate<T extends Table>(Criterion<T> criterion) {
// ERROR: The type 'Criterion<T>' is not exhaustively matched by the switch.
// It suggests that 'Constraint<Table, Object>' is not covered,
// even though T is invariant.
final result = switch (criterion) {
BoolConstraint<T>() => 'bool',
StringConstraint<T>() => 'string',
};
}
The compiler expects the switch to cover Constraint<Table, Object> (the top-level bounds) rather than recognizing that for a specific Criterion<T>, the only valid subclasses in this sealed hierarchy are those bound to that same T.
With inout variance, the compiler should be able to guarantee that Constraint<T, V> is the only branch to follow, and since all subclasses of Constraint are accounted for, the switch should be exhaustive.
I am encountering a non-exhaustive switch error when using a sealed hierarchy with two generic parameters. While I understand this is a known limitation, I wanted to highlight that enabling the experimental variance feature and marking the primary parameter as
inout"invariant" does not resolve the issue. The compiler still fails to prove that the subclasses cover the full type space for a specific generic instance.The compiler expects the switch to cover
Constraint<Table, Object>(the top-level bounds) rather than recognizing that for a specificCriterion<T>, the only valid subclasses in this sealed hierarchy are those bound to that same T.With
inoutvariance, the compiler should be able to guarantee thatConstraint<T, V>is the only branch to follow, and since all subclasses of Constraint are accounted for, the switch should be exhaustive.