-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Type inference should consider asserts #36883
Comments
I think this is just outdated docs, the I'm also under the impression I think the suggestion should probably be to do something like this: void example1(Foo foo) {
// I know this is always a SuperFoo
var sfoo = foo as SuperFoo;
// ...
}
void example2(Foo foo) {
// This might be a Bar, so I'll need an upcast to do type promotion
Object maybeBar = foo;
if (maybeBar is Bar) {
// ...
}
} |
I agree that I hope that we may get a simple syntax for guaranteed run-time checks in the future which will promote, say |
Why must |
Analyzer and the compiler must guarantee the same thing. This is Dart 2 not Dart 1: void example(Foo foo) {
assert(foo is Bar);
var items = [foo];
print(items.runtimeType); // Should this print List<Foo> or List<Bar>?
} |
I was using the analyzer as an example. Why couldn't the compiler do the same thing? Maybe let's set aside the As a bonus it'd be nice if debug builds could throw This doesn't have to use |
Because of all the reasons already listed.
The last I heard the VM/Language team is firmly against this because of the easy ability to seg-fault. If you are just looking for a more convenient syntax to do type promotion, @eernstg has some ideas. |
This seems like a great idea. My only suggestion would be to tweak the syntax a little so it looks like this:
|
Type promotion only happens when the promoted static type is guaranteed to be a safe approximation of the run-time type. The way to guarantee this is that the compiler can statically detect that the promoted code will only be executed when the type check succeeds. Example: Object x = ...;
if (x is Foo) {
// Won't get here unless x *is* Foo.
} We want to make type promotion smarter so it also recognizes control flow, so that: {
Object x = ...;
if (x is! Foo) throw "badness"; // or return, or even other control flow.
// Won't get here unless x is Foo.
} The problem with assertions is that they are not always executed, so: Object x = ...;
assert(x is Foo);
// *Can* get here when x is not Foo, if running in production mode. Because of that, the type promotion isn't sound. It's not true that the value of Object x = ...;
runtimeAssert(x is Foo);
// Won't get here unless x is Foo. We don't have those, you'll have to write them as |
|
I'm suggesting that |
Or alternatively, I suggest that |
If we make If the only syntax which turns on "always executed" for an assert is an expression of the form Or we could say that if I still fear that users will be confused as to which asserts are always executed and which are only executed in debug mode. For example, if a variable is assigned or captured in the guarded branch, then we do not promote. That would, in turn, make the assert into debug-only, so: void foo(List otherList, dynamic o) {
if (o is String) {
otherList.remove(o);
} else if (o is int) {
otherList.removeAt(o);
} else {
// Must be the third kind.
assert(o is Set<String>);
otherList.removeWhere((x) => o.contains(x));
}
} Here the closure will capture With NNBD, a check like By using an existing syntax, and conditionally giving it a different meaning, we make it harder for the user to express their intent. It means that we can't tell them when they make a mistake because it might be intentional. I think adding a separate syntax for guaranteed check will be better for users. Then they can say when they want the check to be always made, so we can tell them when it doesn't work anyway, instead of just going back to not checking. |
I agree that we shouldn't actually execute asserts. I'm saying we should use asserts as barriers that can imply contracts. The exception thrown by a contract violation would be on the next access, just like today, not on the assert. The assert itself isn't executed, but it's implications are taken into account by the compiler. In your specific example in the last comment, assuming |
This sounds like you don't want to execute the assert, but you want to insert the same test later anyway, if the variable is ever used at the promoted type. We will need to do that check because the VM may crash if it assumes a static type which isn't true. Then we might as well just execute the assert, it is at least predictable and visible, and the reported failure can be directly understood by the user (and you can even have a custom message). (To take the opposite view for a moment, I have actually suggested that we should do unsafe promotion in more places. Any code of the form |
I've noticed that with NNBD, casts now will promote the variable type in the current scope: localVariable as Foo;
localVariable.someFooMethod(); and someLocalNullable!;
someLocalNullable.someMethod(); |
Yes, those casts promote because the check is an implicit branch where one branch throws. localVariable is Foo || throw ...
// and
someLocalNullable != null || throw ... which both promote on the non-throwing branch. |
As I mentioned earlier in #35019, given
assert(variable is Type)
, I would likevariable
to be inferred asType
.@Hixie also mentioned this in #32360 (comment).
(I noticed #4410 also requested this, and it was apparently fixed, but I'm not sure if it regressed or if that was specific to DartPad or something.)
Additionally, the documentation for the
avoid_as
lint states:Maybe "use an assertion" there doesn't mean a literal
assert
, but it's misleading at best. Furthermore, one of the reasons for the lint is to avoid an unnecessary runtime type check when the type is already known. However, I don't think there's a way to do that to the analyzer's satisfaction if I also have implicit-casts disabled: I can't useassert
, I can't implicitly narrow the type, and I can't explicitly narrow the type withas
.The text was updated successfully, but these errors were encountered: