-
Notifications
You must be signed in to change notification settings - Fork 228
Description
For a very long time, I thought that the weakening of soundness in mixed mode was limited to nullability. That is, I thought that in mixed mode:
- within an opted out library, the result of evaluating an expression having static type T would always be a value whose runtime type is a subtype of T (because T contains the
*s necessary to permit nulls in the appropriate places) - within an opted in library, the result of evaluating an expression having static type T would always be a value whose runtime type is a subtype of T', where T' is formed from T by adding/removing
*s to T in appropriate positions.
But that turns out not to be the case. The following program breaks soundness by allowing a String to be assigned to a variable of type int. Both the analyzer and CFE accept it without issuing any errors:
test.dart (opted out):
// @dart=2.6
import 'opted_in.dart';
T violateSoundness<T>(dynamic x) => accomplice<T>(x, null);
main() {
String s = 'soudness, my old nemesis, we meet again!';
int i = violateSoundness<int>(s);
print(i.abs());
}opted_in.dart:
// @dart=2.10
T accomplice<T>(Object? x, int i) {
if (x is! T) {
if (i is int) throw 0;
}
return x;
}What's happening is that flow analysis considers the implicit "else" branch of if (i is int) to promote the type of i to Never, hence it considers it unreachable. So it considers the flow state after if (i is int) throw 0; to be unreachable, and hence it considers if (x is! T) { if (i is int) throw 0; } to promote the type of x to T. So it believes that the return x; at the end of accomplice is sound.