Skip to content

Analyzer: no implicit cast errors for most downcasts to iterable #36267

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

Closed
MichaelRFairhurst opened this issue Mar 19, 2019 · 4 comments
Closed
Assignees
Labels
dart-model-analyzer-spec Issues with the analyzer's implementation of the language spec legacy-area-analyzer Use area-devexp instead. P4 type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)

Comments

@MichaelRFairhurst
Copy link
Contributor

MichaelRFairhurst commented Mar 19, 2019

  Object o;                                                                      
  dynamic d;                                                                     
  for (var x in o) {} // no error                                                
  for (int x in d) {} // no error                                                
  for (int x in o) {} // error                                                   
  for (var x in d) {} // no error

The same behavior occurs for spread collections.

I would expect that all of these would be flagged based on the fact that all four of these assignments are flagged:

  Iterable<dynamic> idd = d;                                                     
  Iterable<int> iid = d;                                                         
  Iterable<dynamic> ido = o;                                                     
  Iterable<int> iio = o;
@MichaelRFairhurst MichaelRFairhurst added legacy-area-analyzer Use area-devexp instead. type-bug Incorrect behavior (everything from a crash to more subtle misbehavior) labels Mar 19, 2019
@eernstg
Copy link
Member

eernstg commented Mar 20, 2019

@lrhn and I just discussed the extent to which we can insist that a desugaring specification can be used to determine compile-time errors. I would like to say that the rewritten code shows whether there are any errors, but the original code should (of course) be the sole reference that error messages can refer to.

But for-in desugars to a while statement, and in this case we would get the following outcome:

  Object o;
  dynamic d;
  for (var x in o) {} // An error exists, because `o.iterator` is an error.
  for (int x in d) {} // OK; dynamic lookups `d.iterator`, `n0.current` and cast to `int`.
  for (int x in o) {} // Error exists, `o.iterator` again.
  for (var x in d) {} // OK, has `d.iterator`, `n0.current`, `x` has type `dynamic`.

The error messages would not be able to refer to o.iterator, but we don't specify exactly how to weasel around that. ;-)

PS: The title seems to imply that --no-implicit-casts is used, in which case the cast to int will of course be flagged as well.

@MichaelRFairhurst
Copy link
Contributor Author

PS: The title seems to imply that --no-implicit-casts is used, in which case the cast to int will of course be flagged as well.

Yes, that's the issue here, is that turning that flag on doesn't do anything here.

I'm not so sure about

  for (var x in d) {} // OK, has `d.iterator`, `n0.current`, `x` has type `dynamic`.

I suppose I can see this being OK because it's simply pure dynamic code. But I don't like it! :)

I would expect the desugared code to do:

Iteratable i = d;

after all, this code aborts because NotIterable is not an Iterable.

void main() {                                                                    
  dynamic notIterable = NotIterable();                                           
  for (var i in notIterable) {                                                   
    print(i);                                                                    
  }                                                                              
}                                                                                
                                                                                 
class NotIterable {                                                              
  List<int> innerList = [1, 2, 3];                                               
  Iterator<int> get iterator => innerList.iterator;                              
}

@eernstg
Copy link
Member

eernstg commented Mar 29, 2019

I would expect the desugared code to do: .. Iterable i = d;

We discussed that approach in the language team, but for-in is actually specified to desugar to a snippet of code where the type of the iterable is not required to be Iterable<T> for any T, it just has to support an invocation of iterator, and the returned object must support moveNext() and current. So if that iterator has type dynamic and those methods/getters exist then we're good to go. It's been a while, but I believe it wasn't done because of worries about potential breakage, or maybe it just wasn't pushed hard enough to get to a conclusion.

But I can see that both dart2js and dart generate code where it is actually required that the iterable is an Iterable, so there wouldn't be any breakage to worry about in practice.

Created a pull request in order to make the spec say that it must be an Iterable.

The discussion about --no-implicit-casts is separate, but with this change we would get an error for for (var x in d) {} because of the downcast from dynamic to Iterable<dynamic> at the beginning of the desugared code.

@srawlins srawlins added dart-model-analyzer-spec Issues with the analyzer's implementation of the language spec P4 labels Jan 11, 2021
@srawlins srawlins self-assigned this Nov 9, 2021
copybara-service bot pushed a commit that referenced this issue Nov 10, 2021
The implementation is extremely simple: wire strict-casts right
alongside implicit-casts in AnalysisOptionsImpl, AnalysisSession, and
TypeSystem. The flag is separate from implicit-casts because it is
_slightly_ stricter. The implicit-casts flag has some gaps in coverage
(like for-each iterables, and yield-each expressions). Filling these
gaps in implicit-casts would be expensive (requiring cleanup), and the
short-term goal is to deprecate the flag, so it is not important to
tidy it up.

A few extra bits are then added which are specific to strict-casts,
which improve the coverage.

Most of this change is the tests: tests are added for (theoretically)
each error code which could be reported as a result of strict-casts. The
test model using WithStrictCasts follows the model of WithStrictCasts,
which couples two tests into one assertion, ensuring the state of each
example code before and after strict-casts.

Bug: #36267
Change-Id: I52b2fc831fac3a5467ec7164df8fc1a1109e42ef
#33749
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/219847
Commit-Queue: Samuel Rawlins <[email protected]>
Reviewed-by: Konstantin Shcheglov <[email protected]>
Reviewed-by: Brian Wilkerson <[email protected]>
@srawlins
Copy link
Member

I'm closing this as WAI. The spec is now pretty clear, and analyzer's new strict-casts: true reports these cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dart-model-analyzer-spec Issues with the analyzer's implementation of the language spec legacy-area-analyzer Use area-devexp instead. P4 type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)
Projects
None yet
Development

No branches or pull requests

3 participants