Previously, when the migration engine found a type that was implicitly
or explicitly dynamic, it assumed the type was meant to be nullable.
This seemed reasonable (since, after migration, `dynamic` indeed
allows null), but it caused unnecessary nullabilities to be
propagated. With this change, the migration engine tracks whether
dynamic types can be nullable or not, just as it does for all other
types. This cleans up a number of tricky corner cases in the
migration engine that were getting in the way of fixing up uses of
`always` and `never` nodes.
Note that if a `dynamic` type is determined to be non-nullable, it
will still be `dynamic` after migration; this change merely prevents
nullabilities from being needlessly propagated from that dynamic type
to other types.
Note that we still assume `dynamic` is nullable when it occurs in an
already-migrated library or as the result of a dynamic dispatch.
Change-Id: I380d0907f3489d0b84f3fdd7164113daae24274e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/125084
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
A core assumption of the migration tool is that the code being
migrated may have some dependencies that have already been migrated
(e.g. the SDK itself). Of course these dependencies will need to be
analyzed as "opted in" libraries so that they can have nullability
annotations.
However, when the migration tool was first being developed, we didn't
yet have a migrated SDK, so the SDK was opted out and we just had to
pretend that it was opted in.
This CL prepares to switch over to a truly opted-in SDK by updating
the AlreadyMigratedCodeDecorator to handle opted-in code.
Change-Id: I797752401f8c7bf7cdf8d26d03d00d3d213a5127
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/124466
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Previously, when the code being migrated referred to a nullable or a
non-nullable type in an already-migrated library, we would use the
`always` or `never` graph node to represent the nullability of the
type. This was problematic, because it made it difficult for
instrumentation to pinpoint precisely which element in an
already-migrated library was the cause of an expresion being
null-checked, or a type being made non-nullable.
We now create a fresh nullability node for each non-nullable type
coming from already-migrated code, with an edge to `always` or `never`
to ensure that the fresh node has the correct nullability, so that
instrumentation can see precisely which element it came from.
Since the migration tool doesn't traverse the ASTs of already-migrated
code, we can't report an AST node that caused such an edge, so an
`element` getter has been added EdgeOriginInfo to allow us to report
the element that caused the edge.
As before, types associated with already-migrated code are reported to
instrumentation via InstrumentationListener.externalDecoratedType. As
a new enhancement, bounds of generic parameters in already-migrated
code are reported to instrumentation via
InstrumentationListener.externalDecoratedTypeParameterBound.
Change-Id: Ided4e96e2920f8d9062688f5a6fda29b9b71dd12
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/124000
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
Previously such shared code was in the front_end package, but that
created problems because there is a lot of code in front_end that
isn't intended to be shared with the analyzer (including, notably, the
dependency on kernel).
This CL just moves over the flow_analysis logic to the new shared
package. Follow-up CLs will move over other shared logic and tests.
The end goal is that the analyzer package will no longer have a
dependency on front_end.
Change-Id: I5642d6565204422d79808ca47648462db85e442a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/123624
Reviewed-by: Jens Johansen <jensj@google.com>
Flow analysis requies the client to desugar a try/catch/finally
statement into a try/catch statement nested inside a try/finally
statement (or, in the analyzer's case, to make calls to flow analysis
as though such a desugaring has been done). The analyzer was doing
this incorrectly:
- It was always calling the flow analysis methods for try/catch, even
if there were no catch clauses present.
- When calling tryFinallyStatement_finallyBegin for a
try/catch/finally statement, it was passing in the node associated
with the original try block (prior to "desugaring"). It needs to
pass in the node associated with the desugared try block (which
includes the catches).
Includes a similar fix in the migration engine.
Fixes#39178.
Change-Id: I16440e71d4964b9905fc2316380c8c98f14b13f8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/123506
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This allows so-called "ensure guarded" promotions, e.g.
- `if (x is! int) x = 0;`
- `if (x == null) x = 0;`
- `x ??= 0;`
The latter two are particularly important to prepare for NNBD.
Change-Id: Ib53ca916e6d485945326b19e86f8cfb5b3ee2160
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/123280
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
If the RHS of an assignment is a subtype of the current promoted type
(or one of the previously promoted types), any relevant promotions are
kept.
So for example:
if (x is int) {
x = x + 1; // Still an int!
print(x.isEVen); // ok
}
This required two significant changes:
- Clients must now tell the flow analysis engine the type of the RHS
of the assignment. This required some refactoring in the analyzer,
because this information wasn't always easily available at the point
where we were calling flow analysis.
- The flow analysis engine now has to keep track of a "chain" of the
currently active promotions, so that it can potentially un-do some
promotions but not others. This meant that the algorithms for
"join" and "restrict" had to be reworked.
In a future CL I'll add the ability for assignments to promote to
types that have been previously checked against ("types of interest"),
e.g.:
if (x is! int) {
x = 0; // x is now an int!
}
// x is now known to be an int because it's promoted in both branches
Change-Id: I63d6c9a2021b045d391b2d9c674e9a2e5f770e62
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/123003
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This should make it easier to verify that the CFE implementation is
correct.
Note that this effort encountered a few bugs in the analyzer
implementation, which are marked with TODOs. I will fix them in
follow-up CLs.
Change-Id: Ic164a1cf214b5730f848b17db2838c29d25b00d0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/122850
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Previously, the client was responsible for querying AssignedVariables
to find the variables assigned and captured during constructs like
loops, and passing that information into FlowAnalysis methods. With
this CL, the client simply passes AssignedVariables into the
FlowAnalysis constructor, and the lookup happens automatically.
Change-Id: Ifbbf8ba8ea0d0f8a31d41244892c8c816eea56dd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/122731
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
When promoting a type parameter, we need to create an intersection
type (e.g. "T & int"). Such a type is represented in the analyzer and
the front end as a type parameter type pointing to the same type
variable, but with a different bound.
Change-Id: I1655f9242d913ca958c279cc80c3f6329f6b396d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/122581
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Previously, any attempt to promote type A to type B produced type B if
B was a subtype of A, and failed otherwise. But in order to support
promotion of type parameters, we need the ability to produce a fresh
"intersection type" (e.g. `T & int`). The new
`TypeOperations.tryPromoteToType method` makes this possible by giving
the client the opportunity to synthesize the new type when necessary.
Change-Id: If671d5d865f38469a878329180c3a1c94e25a42c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/122582
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Previously, it was the caller's responsibility to determine if the
subexpression of an "is" expression was a read of a promotable
variable. Now, the caller calls isExpression_end regardless of what
the subexpression is, and flow analysis determines whether promotion
should happen or not.
Change-Id: Ibba689935580d4e9c5ab79e5afb534c173386232
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/122411
Reviewed-by: Johnni Winther <johnniwinther@google.com>
It would have been very useful in helping to track down
https://github.com/dart-lang/sdk/issues/38341#issuecomment-542892773
to have this information surfaced through the instrumentation output.
Specifically, it would be nice if the user could hover over a type
annotation that was *not* made nullable due to a propagationStep whose
`reason` was `StateChangeReason.upstream`, and see information about
all the edges for which the `sourceNode` corresponds to that type and
`isUpstreamTriggered` returns `true`. These are, in effect, the edges
that prevented the type from being marked nullable.
(Note that there's another possible reason a type might not be marked
nullable. It might be that there are no edges for which the
`destinationNode` corresponds to that type and `isTriggered` returns
`true`. In this case, the node is indeterminate, so the migration
engine left it as non-nullable because there was no need to make it
nullable).
Change-Id: I9b969201b813496d41f3a373454c2659f683178d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/121941
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>