8000 [Patterns] Flow analysis · Issue #50419 · dart-lang/sdk · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[Patterns] Flow analysis #50419

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

Open
stereotype441 opened this issue Nov 9, 2022 · 6 comments
Open

[Patterns] Flow analysis #50419

stereotype441 opened this issue Nov 9, 2022 · 6 comments
Assignees
Labels
area-dart-model For issues related to conformance to the language spec in the parser, compilers or the CLI analyzer. model-flow Implementation of flow analysis in analyzer/cfe P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug

Comments

@stereotype441
Copy link
Member
stereotype441 commented Nov 9, 2022

Tracking issue for the implementation of flow analysis for patterns.

This includes implementing the flow analysis engine for patterns and adding calls to the engine from the shared logic in pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer.dart. I'm assuming that the analyzer and CFE will use that shared logic for all their patterns analysis, so there shouldn't be any need to add calls from the analyzer and CFE directly to flow analysis for pattern support.

@stereotype441 stereotype441 added legacy-area-analyzer Use area-devexp instead. legacy-area-front-end Legacy: Use area-dart-model instead. labels Nov 9, 2022
@stereotype441
Copy link
Member Author

I'm assuming I'll be doing a lot of this work, so I'm assigning to myself. So far I've written up an implementation plan but no actual code.

If anyone would like to collaborate on this, please let me know.

@stereotype441 stereotype441 self-assigned this Nov 9, 2022
copybara-service bot pushed a commit that referenced this issue Nov 9, 2022
It turns out the only information stored in `_BranchContext` that was
actually used was the part of `_conditionInfo` that tells what the
flow model should be if the branch is taken.  So we just store that
information directly.

This should make it easier to implement flow analysis for the new
"if-case" construct, which doesn't have a particular expression that's
determining whether the branch is taken (and hence won't have an
easily derivable `_conditionInfo`).

Change-Id: I73ffad36cda4f863335f571542244d58f4bddceb
Bug: #50419
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/268741
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
@bwilkerson bwilkerson added the P1 A high priority bug; for example, a single project is unusable or has many test failures label Nov 10, 2022
@bwilkerson
Copy link
Member

I marked this P1 because some priority is part of the analyzer issue triage process and it's related to a language feature.

@srawlins srawlins modified the milestones: Dart 3 stable, Dart 3 beta 2 Nov 30, 2022
copybara-service bot pushed a commit that referenced this issue Dec 7, 2022
This change makes the API for the `ifCase` method the same as that of
`if_`, which should make it easier to write flow analysis unit tests
for if-case statements.

Bug: #50419
Change-Id: Iea7e0fabba9966eae52c0d28b52d2f8fcea25e46
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/273820
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Dec 7, 2022
This change makes the API for the `ifCaseElement` method similar to
that of `ifCase`, which should make it easier to write flow analysis
unit tests for if-case elements.

Bug: #50419
Change-Id: I64c160f3b6df38b0d337f9b8f9c6c9189e9a13ed
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274161
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
copybara-service bot pushed a commit that referenced this issue Dec 9, 2022
This CL adds support for flow analysis with variable patterns and
guards, and integrates it with if-case elements, if-case statements,
pattern variable declarations, switch expressions, and switch
statements.  It includes support for guards.

No other types of patterns are handled yet.

Bug: #50419
Change-Id: Iacad82b472cba0e2e670981847258e4046017576
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274162
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Dec 9, 2022
…ement cases.

Bug: #50419
Change-Id: Ie9b0dd3319dd4e28f53082794aa18ed422b0894b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274605
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Dec 12, 2022
When patterns support is disabled, flow analysis considers a switch
statement to be exhaustive if it has a `default` clause or if the
scrutinee type was an enum and all enum cases were covered (this
matches the behaviour of previous releases of Dart).

When patterns support is enabled, flow analysis considers a switch
statement to be exhaustive if it has a `default` clause or if the
scrutinee type is an "exhaustive type" (as defined in the patterns
spec).  A later stage of analysis will check that such switch
statements truly are exhaustive, and issue a compile-time error if
they aren't.

Note that as part of this change I've modified the analyzer so that it
only attempts to track whether all enum cases are covered if patterns
support is disabled.  I didn't make a corresponding change to the CFE
because the CFE stores exhaustiveness information in the kernel output
(`SwitchStatement.isExplicitlyExhaustive`) and I didn't want to break
that.

Bug: #50419
Change-Id: Ib2e51971a1b814c003401b3d54d41f7a9ef9f59a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274720
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Dec 12, 2022
As I'm beginning to work on flow analysis for logical and/or patterns
I'm realizing that the shared analysis methods for handling
logical-and and logical-or patterns are going to need to have
different parameters, so it makes sense to separate them.

This makes the code clearer anyhow, since they weren't sharing any
functionality.

Bug: #50419
Change-Id: I17b0ad53f916f48ee02ccb1c9c23488261a6a1b6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274920
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Dec 12, 2022
This will allow me to push additional pattern contexts to represent
subpatterns, while still accumulating the "unmatched" flows for the
pattern as a whole.

Bug: #50419
Change-Id: I8a7b1255abeda1cd07d82828057c81f45954d173
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274941
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Dec 13, 2022
This simplifies the shared TypeAnalyzer code by avoiding a lot of null
checks.

This required modifying a few analyzer code paths that didn't
previously initialize FlowAnalysis so that they now do.

Change-Id: Ie306d3ac94c4ca00d211e9cd038fb0b001996747
Bug: #50419
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274940
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Dec 13, 2022
…ments.

I previously thought that the only possible situation where a switch
statement over a valid type could be exhaustive without containing any
cases was if the scrutinee type was the empty record type (`()`).  But
that isn't true at all:

- The empty record type is inhabited by empty record objects, so such
  a switch statement would not be exhaustive after all.

- It is possible for the user to create a type that can be
  exhaustively switched over with zero cases: by creating an abstract
  sealed class without any subclasses.

In the latter case, I think it makes the most sense to treat the code
after the switch as unreachable, because that's the normal behaviour
of a switch over an exhaustive type with no `break`s.  It is sound to
do so because the type is uninhabited, therefore the body of the
switch statement itself will never be reached.

Bug: #50419
Change-Id: Ibc0a21226f25ae155db994343874354d5b8e4f7d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274621
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Dec 14, 2022
A desired feature for flow analysis of patterns is that the matched
value type of one subpattern may depend on promotions from previous
patterns.  For example, we want this to work:

    int? x = ...;
    if (x case null || < 0) ...;

This means that although the type of the scrutinee is `int?`, after
the subpattern `null` has been processed, type promotion should
promote the matched value type to `int`, so that the subpattern `< 0`
doesn't report an error.

This requires changing the API of the shared patterns analysis logic,
so that the client is no longer responsible for passing around the
matched value type.  Instead, flow analysis keeps track of it and
provides it to the client when requested.

In follow-up CLs I'll add the necessary logic to flow analysis to
actually promote the matched value type in situations like the example
above.

Change-Id: I6ce66962af32f9bd93bfe4082ba256de627d3cc6
Bug: #50419
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274942
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
copybara-service bot pushed a commit that referenced this issue Dec 14, 2022
…t clauses.

This enables the front end to skip the old-style (enum-based)
exhaustiveness checking algorithm when pattern support is enabled,
because with pattern support enabled, switches on enums are required
to be exhaustive (this will be checked by the new exhaustiveness
checking algorithm).

That in turn means that in the future, when we remove support for
language versions that lack patterns support, we will be able to
remove the old-style exhaustiveness checking algorithm.

This change has a small effect on code generated by the WASM back-end
(the only back-end that uses `isExplicitlyExhaustive`): for a switch
statement that is exhaustive *and* has an unreachable `default`
clause, after testing all the cases, the WASM back-end will generate a
branch to the `default` case.  Previously it would instead generate an
`unreachable` instruction.  There should be no behavioural difference
because the instruction in question is unreachable in both cases.
Also, there should be negligible code size difference because the body
of the `default` case is being emitted either way.

Bug: #50419
Change-Id: Id6bd7d9a540cb1b4d9c3624db8ff494438276bea
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274924
Reviewed-by: Aske Simon Christensen <askesc@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
copybara-service bot pushed a commit that referenced this issue Dec 16, 2022
Implement the necessary flow analysis bookkeeping so that in a pattern
of the form `P1 && P2`, any type checks performed by `P1` will be
reflected in the matched value type used by `P2`.  To accomplish this,
we allocate a promotion key to represent the "cached value" matched by
every pattern and subpattern (so that `P1 && P2` promotes properly
even when in a subpattern of another pattern).  This promotion key is
stored (in the form of a `ReferenceWithType<Type>`) in the
`_PatternContext` class.

Also, move `_scrutineeReference` and `_scrutineeType` into
`_FlowAnalysisImpl`; this preserves the existing behaviour of
promoting the scrutinee expression, but with less bookkeeping (since
we don't need to pass it along from one context to the next).  And add
`_FlowAnalysisImpl._scrutineeSsaReference` so that if the scrutinee is
modified during execution of the switch statement, it won't be
erroneously promoted by patterns that follow.

Finally, implement some preliminary logic for `||` patterns to avoid
breaking existing tests.  I will fill this out (and test it
thoroughly) in a follow-up CL.

Bug: #50419
Change-Id: I0d934e706ad5b19cda46a198afb2fb7a4309829b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/275788
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
copybara-service bot pushed a commit that referenced this issue Jan 9, 2023
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: #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>
copybara-service bot pushed a commit that referenced this issue Jan 11, 2023
Thanks to Konstantin for noticing this issue.

Bug: #50419
Change-Id: I0380b846839c5afa1b20a52d55d980ddb6d09fec
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/278810
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Jan 11, 2023
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: #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>
copybara-service bot pushed a commit that referenced this issue Jan 19, 2023
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: #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>
copybara-service bot pushed a commit that referenced this issue Jan 25, 2023
Bug: #50419
Change-Id: I7fd845b0e465b63a2a9fd5e17bf254b9073a7d1f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279459
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
copybara-service bot pushed a commit that referenced this issue Jan 26, 2023
This method is used for declared variable patterns, list patterns, map
patterns, record patterns, object patterns, and wildcard patterns to
cause the value being matched to be promoted.

As a temporary measure it's also used for cast patterns; this will be
fixed in a later CL.

Change-Id: Iccc9ce4a53e2a10753847f9ecade091f1b230111
Bug: #50419
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279653
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Feb 1, 2023
There are three ways type promotion can occur in an or-pattern:

(1) the scrutinee variable (if any) might be promoted, e.g.:

    f(Object x) {
      if (x case int _ && < 0 || int _ && > 10) {
        // `x` is promoted to `int` because both sides of the `||`
        // promote the scrutinee variable to `int`.
      }
    }

(2) the implicit temporary variable that holds the matched value might
    be promoted, e.g.:

    f(Object Function() g) {
      if (g() case (int _ && < 0 || int _ && > 10) && (var x)) {
        // `x` has type `int` because both sides of the `||` promote
        // the matched value to `int`.
      }
    }

    For this sort of promotion to work, we need to

(3) explicitly matched variables might be promoted at the time of the
    match, e.g.:

    f<T>(T t) {
      if (t is int) {
        if (t case var x && < 0 || var x && > 10) {
          // `x` has type `T` but is promoted to `T&int`, because both
          // declarations of `x` are in a context where the matched
          // value has type `T&int`.
        }
      }
    }

The existing flow analysis logic handles cases (1) and (2) without any
extra work, because those promotions are joined as a natural
consequence of the flow control join at the end of matching the
logical-or pattern.

However, flow analysis has to do some extra work for case (3), because
the two copies of variable `x` are associated with different variable
declarations (and hence have different promotion keys).  To ensure
that the promotions are joined in this case, we need to copy the flow
model for the two copies of `x` into a common promotion key prior to
doing the flow control join.

The bookkeeping necessary to figure out a common promotion key is
similar to the bookkeeping necessary to track the association between
the individual declared variable patterns and the joined pattern
variable (and this is bookkeeping that flow analysis is already
doing).  So as part of this change I went ahead and removed the
`getJoinedVariableComponents` method (which was previously used by
flow analysis to query this association).  This reduces the
constraints on the analyzer and CFE implementations by not requiring
them to do this bookkeeping themselves.

In the process I've made two additional small changes:

- I modified the logic for assigning types and finality to joined
  variables so that if there is a finality conflict but no type
  conflict, the common type is used; conversely, if there is a type
  conflict but no finality conflict, the common finality is used.
  This should help reduce follow-on errors.

- I added logic to ensure that if a variable is only declared on one
  side or the other of a logical-or, flow analysis still considers
  that variable to be definitely assigned.  This should help reduce
  follow-on errors.

Change-Id: I62f17adb6a51a583707c216ed48d941d1c621eea
Bug: #50419
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279756
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
copybara-service bot pushed a commit that referenced thi 8000 s issue Feb 1, 2023
…a body.

There are two ways type promotion can occur when switch cases share a body:

(1) the scrutinee variable (if any) might be promoted, e.g.:

    f(Object x) {
      switch (x) {
        case int _ && < 0:
        case int _ && > 10:
          // `x` is promoted to `int` because both cases promote the
          // scrutinee variable to `int`.
      }
    }

(2) explicitly matched variables might be promoted at the time of the
    match, e.g.:

    f<T>(T t) {
      if (t is int) {
        switch (t) {
	  case var x && < 0:
	  case var x && > 10:
            // `x` has type `T` but is promoted to `T&int`, because
            // both declarations of `x` are in a context where the
            // matched value has type `T&int`.
        }
      }
    }

The existing flow analysis logic handles case (1) without any extra
work, because those promotions are joined as a natural consequence of
the flow control join at the end of matching the cases.

However, flow analysis has to do some extra work for case (2), because
the two copies of variable `x` are associated with different variable
declarations (and hence have different promotion keys).  To ensure
that the promotions are joined in this case, we need to copy the flow
model for the two copies of `x` into a common promotion key prior to
doing the flow control join.

The bookkeeping necessary to figure out a common promotion key is
similar to the bookkeeping for logical-or patterns.

Bug: #50419
Change-Id: I9ee4ec5d797dae28099aafbaf34fbbeeee5cd626
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/280201
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
copybara-service bot pushed a commit that referenced this issue Feb 7, 2023
Since matching a constant pattern implicitly performs an equality
check, we do the same flow analysis for constant patterns that we
would have done for an explicit equality test.  There are only two
user-visible behaviours:

- If the constant pattern is a `null` literal, then in the code path
  where the pattern fails to match, the scrutinee is promoted to a
  non-nullable.

- If the constant pattern and the scrutinee are both `null` literals,
  the pattern match is known to always succeed (so the code path where
  the pattern fails to match is marked as unreachable).

The first of these two behaviours is genuine useful to the user, since
it allows things like:

    switch (expr) {
      case null:
        ...
      ...
      default:
        // expr is known to be non-`null`.
    }

The second behaviour is not so useful, but seemed worth doing to
maximize code sharing and to keep the behaviour consistent between
pattern matching and explicit null checks.

Making this work required extracting some of the logic that was
formerly in `_FlowAnalysisImpl.equalityOperation_end` into a method
`_equalityCheck`, which understands how to analyze an equality check
regardless of whether it's an explicit equality expression or an
implicit part of a pattern.  It returns an `_EqualityCheckResult`,
which `equalityOperation_end` examines to determine exactly what needs
to be done to for an equality test in an expression context;
similarly, `_FlowAnalysisImpl.constantPattern_end` now calls
`_equalityCheck` and then does the right thing for patterns.

It was also necessary to extract the logic from
`nullCheckOrAssertPattern_begin` for handling null checks in patterns;
that logic is now in `_nullCheckPattern`, which is also called by
`constantPattern_end`.

In the process of testing this change, I discovered (and fixed) a
minor bug: when analyzing a switch statement or switch expression, we
were re-capturing the value of the scrutinee before visiting each
pattern.  This meant, for example, that we would analyze the following
code incorrectly:

    int? i = ...;
    switch (i) {
      case _ when f(i = ...):
        ...
      case null:
        ...
      default:
        // `i` should *not* be promoted to non-null here, because it's
        // not guaranteed to be the same as the value matched by `case
        // null` above.
    }

Bug: #50419
Change-Id: I4d30d6bc2673d9968e69f3384a37e1b2b9cc4a8f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/280861
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Feb 7, 2023
There are two new API methods: `equalityRelationalPattern_end` (for
relational patterns using `==` or `!=`, where some degree of flow
analysis is warranted) and `nonEqualityRelationalPattern_end` (for all
other relational patterns, where all we care about is making sure that
both the "matched" and "unmatched" branches are reachable).

I've moved most of the logic for constant patterns into
`equalityRelationalPattern_end`, since it's essentially the same logic
(except that `equalityRelationalPattern_end` also has support for
`!=`).  So now, `constantPattern_end` simply calls
`equalityRelationalPattern_end` in the case where pattern support is
enabled.

I also had to make a change to `RelationalOperatorResolution` to make
it possible to distinguish `==` from `!=`.

Bug: #50419
Change-Id: Idc5dbcfb02e3f8b57cfcccb3f46364fe34268ded
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/280900
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Feb 9, 2023
If a cast pattern fails to match, it throws an exception, so the
"match failure" code path should be unreachable.

To fix this, I added an optional argument `updateUnmatched` to
`patternRequiredType` to allow the type analyzer to tell flow analysis
whether a required type is enforced via a match failure or an
exception.  I also renamed `patternRequiredType` to
`promoteForPattern`; this is in anticipation of an upcoming CL which
will use this method to promote to a type which isn't necessarily the
same as the required type.

I also discovered a few testing gaps while double-checking that every
call to `promoteForPattern` makes the proper choice about what to pass
for `updateMatched`; I've added test cases to cover these gaps.

Bug: #50419
Change-Id: Id6f63138d01b481e7c9442469ce45e2aaa507a5a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/281740
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Feb 10, 2023
These fields are no longer being used.

Bug: #50419
Change-Id: I6305785d32c5ca2ff55aee297a5977dbd6ea4189
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/282300
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Feb 10, 2023
Previously, we stored an EqualityInfo object representing the
scrutinee at the time of entry to the top level of the pattern; then
when handling, for example, a relational pattern using `==`, we used
that EqualityInfo object to decide how to handle the equality check,
*even if we were no longer at the top level of the pattern*.  As a
result, we would sometimes come to incorrect conclusions about
relational patterns inside subpatterns.  For example, we would fail to
see that `if ((null,) case (== null,))` is a guaranteed match, because
when processing the `== null` pattern, we would erroneously use the
EqualityInfo for `(null,)` (which is *not* `null`).

To solve this, we only store the scrutinee *reference* at the top
level of the pattern (this is sufficient to allow us to decide whether
or not to promote the scrutinee).  When we encounter a pattern like
`== null`, we compute the appropriate EqualityInfo directly based on
the ExpressionInfo, type, and reference that are appropriate to the
current pattern level.

Bug: #50419
Change-Id: I4d1bc34fcb2d238e7b69e1b88df50b389410963c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/282162
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Feb 13, 2023
Bug: #50419
Change-Id: If38ad38cefbdca97144dd853c05aeca3ddb61cf2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/282320
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Feb 13, 2023
Most pattern variable assignments don't need to promote, because
pattern variable assignments must be irrefutable, and thus the pattern
match cannot fail.  There's one exception, though: when the assigned
value has type `dynamic`, an implicit downcast is performed.

Bug: #50419
Change-Id: Ic8f572303d60c354e9e342f79af03e669fa248c3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/282381
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
copybara-service bot pushed a commit that referenced this issue Feb 13, 2023
Previously, we kept track of the promotion information for a matched
value using a `ReferenceWithType` stored in the `_PatternContext`.
This information did not get updated in the event of a promotion, so
that meant that if a pattern tried to promote the same value twice
(e.g. `int _ && num _`), the second promotion attempt would not see
the effects of the first attempt, so it might wind up demoting the
matched value.

The solution is to just track the promotion key in `_PatternContext`,
and resynthesize the `ReferenceWithType` object (with the proper type)
whenever we need it.

Bug: #50419
Change-Id: I026a4d2f42875a003ce8840dfc88d65484bb33ae
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/282800
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Feb 14, 2023
For each subpattern of a record pattern, we define its "demonstrated
type" to be the type that the matched value has been promoted to if
the subpattern match succeeds.  At the conclusion of visiting a record
pattern, we promote the whole record pattern's matched value to a
record type formed by combining together the demonstrated types of the
subpatterns.  So, for example, the pattern `(int _, String _)`
promotes the matched value to `(int, String)`.

This change contains a lot of specific tests, to make sure the
demonstrated type of each kind of pattern behaves as expected; but the
actual machinery is general, since it just takes advantage of the type
promotion performed by the subpatterns.

Bug: #50419
Change-Id: Ie4da81964b5ade657c23a598ee18982b793fb4ac
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/282806
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
@itsjustkevin
Copy link
Contributor

@stereotype441 are we still working toward completion of flow analysis? What is left to close this issue out?

copybara-service bot pushed a commit that referenced this issue Mar 20, 2023
This field is no longer used; the SSA node for the scrutinee is now
looked up using the scrutinee's promotion key.

Bug: #50419
Change-Id: I67768fa1311cecf62508844d3074758fbf55b426
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/287941
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
copybara-service bot pushed a commit that referenced this issue Mar 21, 2023
As discussed in
dart-lang/language#2857 (comment),
we only want a pattern match to promote the scrutinee in refutable
contexts (if-case and switch).

Bug: dart-lang/language#2857, #50419
Change-Id: I187ef632e5da95b931e5cda34db06491a5228c98
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/288000
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
@vsmenon
Copy link
Member
vsmenon commented Apr 3, 2023

@stereotype441 - is there still open work here? Or can we close?

@stereotype441
Copy link
Member Author

@stereotype441 - is there still open work here? Or can we close?

It's feature complete as far as I know. But I'm still planning on writing language tests (probably later this week or early next week) to help verify that there are no issues.

@vsmenon vsmenon added P2 A bug or feature request we're likely to work on and removed P1 A high priority bug; for example, a single project is unusable or has many test failures labels Apr 3, 2023
@vsmenon vsmenon modified the milestones: Dart 3 beta 3, Dart 3 stable Apr 3, 2023
@vsmenon
Copy link
Member
vsmenon commented Apr 3, 2023

Thanks, I'll drop the pri and bump to the stable milestone.

@stereotype441 stereotype441 added area-fe_analyzer_shared and removed legacy-area-analyzer Use area-devexp instead. legacy-area-front-end Legacy: Use area-dart-model instead. labels Apr 13, 2023
copybara-service bot pushed a commit that referenced this issue May 19, 2023
These tests are based on the unit tests I wrote during development of
the flow analysis support for patterns; as such, they are not
especially uniform in their coverage, but they should be a good
start. I intend to expand coverage in future CLs.

Bug: #50419
Change-Id: I23bd96621a0269c1e642a7fa3b65c851357505c3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/304320
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Bob Nystrom <rnystrom@google.com>
@johnniwinther johnniwinther added area-dart-model For issues related to conformance to the language spec in the parser, compilers or the CLI analyzer. model-flow Implementation of flow analysis in analyzer/cfe and removed legacy-area-fe-analyzer-shared labels Apr 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-dart-model For issues related to conformance to the language spec in the parser, compilers or the CLI analyzer. model-flow Implementation of flow analysis in analyzer/cfe P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

No branches or pull requests

7 participants
0