In order to handle variable patterns inside logical-or patterns, flow
analysis will need to model the implicit temporary variables that
represent the variables before they are joined to form the final
variable value. This CL adds the necessary logic to model this:
- `declaredVariablePattern` is now responsible for initializing the
temporary variable (the client no longer needs to call `initialize`
when analyzing a variable pattern). It returns an integer
representing the implicit temporary variable.
- A new API call, `assignMatchedPatternVariable` can be used by the
client to transfer the variable from the implicit temporary variable
to a user-accessible variable. For now, the shared analysis logic
always calls this from `analyzeDeclaredVariablePattern`, after
calling `declaredVariablePattern`. However, in the future, it will
postpone the call until after these temporary variables are
implicitly joined by logical-or patterns.
Bug: https://github.com/dart-lang/sdk/issues/50419
Change-Id: I2b78a46c11d0d46c8e0a8691c2a2ce49dceb2a24
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279078
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This adds support for checking the stack content against a stack base,
which enables testing for an empty stack, relative to the stack base.
Test added for the stack checker.
Change-Id: I4937a4c77c0c2d8d6673e7848ccedfd8b1491ab6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279340
Reviewed-by: Jens Johansen <jensj@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
In a future CL I will need the ability for flow analysis to initialize
promotion keys that aren't associated with any particular variable.
In anticipation of that, this CL refactors `FlowAnalysis.initialize`
so that it immediately looks up the unpromoted type of the variable
(which is the only information it needs), and thereafter just uses the
variable's promotion key.
From the point of view of flow analysis clients, there is no
functional change.
Change-Id: I54794bde49c7af745b43a09914f70c9c4e6d48da
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279074
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This moves the ValueKind and NullValue interface to the util package
and add the NullValue as an interface to the NullValue enum, which has
now been renamed to NullValues.
This prepares for reusing the stack checking mechanism from the
StackListener in the inference visitor of the CFE. The adding of
NullValue as an interface, allows for using ValueKind and NullValue
that are specific to the types used in the stack.
Change-Id: I7d6b3d3932753898d87ef774a95460832efdc969
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279084
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
Generative synthetic constructors of @staticInterop classes are already
disallowed, but this adds errors for tear-offs of such members as well.
This also disallows tear-offs of @anonymous @staticInterop factory
tear-offs. This aligns with what we want to do with object literal
constructors going forward, as tear-offs will implicitly have different
semantics than direct invocations. To avoid that inconsistency, we
disallow tear-offs here.
Change-Id: Ifc9e4a9251743613ee1ea2eca6e42e36c3b20461
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/278645
Reviewed-by: Riley Porter <rileyporter@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Previously, if a switch case was unreachable, we would not insert a
synthetic break at the end of it. Technically this is not a problem
(since a synthetic break is only required to prevent one case from
falling through to another at runtime, and unreachable code is by
definition never reached at runtime). However, it makes it confusing
for CFE and back-end developers, and there's little harm in adding the
synthetic break anyway.
So with this change, we determine whether to add the synthetic break
by checking whether the bottom of the case block is reachable *from
its top* (rather than globally reachable).
Fixes#50994.
Bug: https://github.com/dart-lang/sdk/issues/50994
Change-Id: I17757b182c29da782457adc057b1b8a6fc91e55e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/278897
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This will help facilitate a follow-up CL in which I plan to modify
flow analysis data structures to track the static (unpromoted) type of
each variable, rather than querying that information from the client.
That in turn will help address some subtle bugs in the flow analysis
of patterns wherein an invalid promotion chain is getting created
because the wrong variable is being queried.
As part of this change, I've cleaned up the logic that calls
`FlowAnalysis.declare` for the synthetic variables associated with
or-patterns and switch cases that share a body; previously this logic
was making redundant calls to `declare`; now it is only calling
`declare` once per synthetic variable.
Bug: https://github.com/dart-lang/sdk/issues/50419
Change-Id: I8e8e4aa71afe3ac7ecdc474f023ec513dc7286b1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/278693
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
In a follow-up CL I'll be making changes to what code is responsible
for calling `FlowAnalysis.declare`. This assertion will help make
sure I make those changes correctly.
Change-Id: I8b390e339b45faa8ee46f2db856d9aaa1f9a16c7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/278649
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
A switch statement like this one:
switch (E) {
case P1 when G1:
S1;
case P2 when G2:
S2;
case P3 when G3:
}
Is equivalent to an if/else chain like this:
var tmp = E;
if (tmp case P1 when G1) {
S1;
} else if (tmp case P2 when G2) {
S2;
} else if (tmp case P3 when G3) {
S3;
}
Therefore, if the failure of a particular pattern/guard combination to
match implies a type promotion, it makes sense for that promotion to
be carried into later cases. For example:
int? x = ...;
switch (E) {
case _ when x == null:
break;
default:
x.isEven; // OK because `x` known to be non-null.
}
This enabled some more thorough testing of type promotion in switches,
which then caught a bug introduced in a previous CL: when the switch
scrutinee is a variable reference, and we are trying to determine
whether it is safe for a pattern to promote the scrutinee variable, we
were checking the wrong SSA node to determine whether the variable had
been reassigned. For example:
Object x;
switch (x) {
case _ when f(x = ...);
break;
case int _:
// `x` is not promoted to `int` because it is no longer the
// same as the cached scrutinee.
break;
}
Bug: https://github.com/dart-lang/sdk/issues/50419
Change-Id: Ie8d6cf0fc662aa5ef0ac81eb2343952028dd2abb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/278533
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
The previous name, `node`, was confusing, because the type is
`Variable`, not `Node`. Although the concrete type used by the CFE
for `Variable` (`VariableDeclaration`) happens to be a type of `Node`,
the same is not true for the analyzer (which uses the type
`PromotableElement`).
Change-Id: Idf3a7c3101786f7d36fa9075053544307ba18519
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/278648
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Adds getters `_debugFields` and `_debugType` to the `_FlowContext`
base class, and adds a single implementation of
`_FlowContext.toString` that builds a representation of the context
based on them. This replaces the implementations of `toString` in all
the classes derived from `_FlowContext`, which were more difficult to
get right and keep synchronized with code changes.
Also changes `_TryFinallyContext` from a `late final` variable to a
nullable variable. This sacrifices a tiny bit of safety, but has the
advantage of allowing `toString` to show the value if it's been
initialized, and avoid crashing if it hasn't.
Change-Id: I0ca3ddfed21934c54eaba912c84edda79c8eadfc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/276202
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>