Commit Graph

296 Commits

Author SHA1 Message Date
Paul Berry 6cea1790bd [flow analysis] Fix invalid type of interest promotion.
This change closes a loophole whereby it was possible for "type of
interest" promotion to promote to a type that was not a subtype of the
declared type.

I've gone ahead and included more extensive tests (both in unit test
and language test form) of demotion and type of interest promotion, to
try to make sure there aren't other loopholes.

Fixes https://github.com/dart-lang/sdk/issues/60620.

Bug: https://github.com/dart-lang/sdk/issues/60620
Change-Id: Ifef04cde6fda2aee80ec002c7ca04f2be6cff987
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/427920
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
2025-05-13 06:10:25 -07:00
Paul Berry 7436b97ba4 [flow analysis] Allow non-cascaded field accesses to participate in field promotion.
The way this is accomplished is that in
`_FlowAnalysisImpl.nullAwareAccess_rightBegin`, any expression
reference associated with the target of the null-aware access is
restored, and the corresponding SSA node is associated with the guard
variable (if any). These changes ensure that if the null-aware access
is a property get, the subsequent call to `propertyGet` will pick up
the appropriate SSA node, so it will be able to locate the promotion
key for the property.

This functionality is only enabled when the language feature
`sound-flow-analysis` is enabled.

To prevent test regressions, a few related changes need to be made at
the same time:

- `_FlowAnalysisImpl.nullAwareAccess_end` is changed so that it clears
  any expression info or expression reference that was associated with
  the null-aware access expression. This prevents flow analysis
  information from being erroneously propagated out of a null-aware
  expression, which would have led to assertion failures when
  analyzing null-aware expressions inside of conditional
  expressions. This wasn't previously a problem because the expression
  reference used to be consumed by
  `_FlowAnalysisImpl.nullAwareAccess_rightBegin`, preventing further
  expression references and expression infos from being recorded
  further along in the null-aware access.

- The test framework in `mini_ast.dart` is fixed so that `!` is
  considered to participate in null shorting. This was a bug in the
  test framework that wasn't previously caught because it happened not
  to produce any test failures.

- The analyzer's method `PostfixExpressionResolver._resolveNullCheck`
  is changed so that it calls `nonNullAssert_end` before terminating
  null-aware access. Previously, the order was swapped, causing
  `nullAwareAccess_end` to be called before `nonNullAssert_end` when
  analyzing expressions like `a?.b!`. This used to be benign, but now
  that non-cascaded field accesses participate in field promotion,
  flow analysis needs the methods to be called in the correct order.

Fixes https://github.com/dart-lang/language/issues/4344.

Bug: https://github.com/dart-lang/language/issues/4344
Change-Id: I523be1b4be1af3f68654a745187a546728c878fe
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/427820
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
2025-05-12 08:25:20 -07:00
Paul Berry d127486b2a Fix up comments in reachability test data.
Turning on the sound-flow-analysis language feature caused a lot of
code to be classified as unreachable. This change updates comments in
the reachability tests to reflect the new behavior.

Bug: https://github.com/dart-lang/sdk/issues/60438
Change-Id: Iec3fd10e4a5d7f890bbe14e0eadd845eeb7a3225
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/427584
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Auto-Submit: Paul Berry <paulberry@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
2025-05-12 00:14:01 -07:00
Paul Berry 0fcd1a6fa1 Bump _fe_analyzer_shared to SDK 3.7 and reformat
Change-Id: I70157caf90c0955c2eb8426f5721ba4ed527eb76
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/427900
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Auto-Submit: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
2025-05-10 15:32:50 -07:00
Paul Berry a757d2af41 Enable sound-flow-analysis for Dart 3.9.
Bug: https://github.com/dart-lang/sdk/issues/60438
Change-Id: I908d4e4a9143142281d8198870f40eba6cf6f67f
Tested: trybots
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/427500
Reviewed-by: Ivan Inozemtsev <iinozemtsev@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Morgan :) <davidmorgan@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
2025-05-09 14:50:26 -07:00
Paul Berry 8b4ce0113b [sound flow analysis] Additional unit tests for patterns.
These unit tests exercise flow analysis behaviors for patterns that
one might plausibly assume are part of the `sound-flow-analysis`
feature, but actually have been present ever since the `patterns`
feature was introduced.

Adding these tests helps me be confident that the behavior of flow
analysis after `sound-flow-analysis` has all of the soundness
behaviors I expect; even though some of those behaviors aren't new.

Bug: https://github.com/dart-lang/sdk/issues/60438
Change-Id: I77a269907c67d643d0ef23d4f76545e36761bbfc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/421960
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2025-04-11 08:03:42 -07:00
Paul Berry 12a32f4359 [sound flow analysis] Implement promoteForPattern behaviors.
This change updates the logic in the flow analysis method
`promoteForPattern`, so that when the language feature
`sound-flow-analysis` is enabled, the following additional behaviors
are added:

- If the matched value type is non-nullable, and the pattern
  implicitly performs an `is Null` test, then the pattern is known not
  to match.

- If the matched value type is `Null`, and the pattern implicitly
  performs an `is T` test, where `T` is a non-nullable type, then the
  pattern is known not to match. Note that this reasoning step is
  sound regardless of whether the program is running with sound null
  safety enabled, but since it is a new reasoning step, it only takes
  place if the `sound-flow-analysis` feature is enabled.

There is no behavioral change if the feature `sound-flow-analysis` is
disabled.

Bug: https://github.com/dart-lang/sdk/issues/60438
Change-Id: I5a6e8def050c95b6c1ad01d37584d17a0cd590c8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/421900
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
2025-04-10 12:48:20 -07:00
Paul Berry 9334e8b2db [sound flow analysis] Implement behaviors for map patterns.
This change updates the flow analysis logic for map patterns, so that
when the language feature `sound-flow-analysis` is enabled, an empty
map pattern is considered to match a non-nullable map.

There is no behavioral change if the feature `sound-flow-analysis` is
disabled.

Bug: https://github.com/dart-lang/sdk/issues/60438
Change-Id: I3f5a79c00cfe91d37528a3790b5cedb9a8f010fc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/421584
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2025-04-10 09:50:40 -07:00
Paul Berry de58cf72e0 [sound flow analysis] Implement behaviors for null check patterns.
This change updates the flow analysis logic for null check patterns,
so that when the language feature `sound-flow-analysis` is enabled,
the matched value type is checked for nullability. If it's
non-nullable, then the null check pattern is known to succeed.

There is no behavioral change if the feature `sound-flow-analysis` is
disabled.

Bug: https://github.com/dart-lang/sdk/issues/60438
Change-Id: Ie68d98d95e30053f992a3f8db6ccdd3978960eb7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/421583
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
2025-04-10 09:27:42 -07:00
Paul Berry 1540a33f74 [sound flow analysis] Implement behaviors for null-aware map entries.
It turns out that the flow analysis logic for null-aware map entries
has always presumed sound null safety. That is, given a map entry with
a null-aware key (`{?x: y}`), flow analysis assumed that if the key
was non-nullable, then the value was guaranteed to execute.

However, another piece of logic that could have been implemented, and
wasn't, was that if the key had static type `Null`, then the value was
guaranteed _not_ to execute. This logic would have been sound even
without assuming sound null safety (because even in unsound null
safety mode, the type `Null` was only inhabited by the value `null`).

This change implements the missing logic. Even though it doesn't
strictly depend on the assumption sound null safety, it still makes
sense to guard it by the `sound-flow-analysis` flag, because (a) it's
a potentially breaking change, and (b) it brings the flow analysis
behavior of null-aware map entries into alignment with the other
behaviors that are being implemented as part of the
`sound-flow-analysis` feature.

There is no behavioral change if the feature `sound-flow-analysis` is
disabled.

Bug: https://github.com/dart-lang/sdk/issues/60438
Change-Id: Ie711f582660a31a411be0dc339995df140feb04f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420800
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2025-04-09 12:02:23 -07:00
Paul Berry 4ce0cf3f49 [sound flow analysis] Implement behaviors for null-aware accesses.
This change updates the flow analysis logic for `??` and `??=`
expressions, so that when the language feature `sound-flow-analysis`
is enabled, the static type of the left hand side is checked for
nullability. If it's non-nullable, then the right hand side of the
expresison is considered unreachable.

These new behaviors break assumptions made by two pre-existing flow
analysis tests. I changed those tests to run with
`sound-flow-analysis` disabled.

There is no behavioral change if the feature `sound-flow-analysis` is
disabled.

Bug: https://github.com/dart-lang/sdk/issues/60438
Change-Id: I33d6d256bd3c41b764245f50ad34eb5c8b33878e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420740
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
2025-04-09 10:13:28 -07:00
Paul Berry 375152e07f [sound flow analysis] Implement behaviors for null-aware operations.
This change updates the flow analysis logic for `?.` expressions, so
that when the language feature `sound-flow-analysis` is enabled, the
static type of the target is checked for nullability. If it's
non-nullable, then the "shortcut" control flow path (the control flow
path in which the null-aware operation is not executed) is considered
unreachable.

One pre-existing flow analysis test was made redundant by this
change. Another needed a minor tweak to continue passing.

There is no behavioral change if the feature `sound-flow-analysis` is
disabled.

Bug: https://github.com/dart-lang/sdk/issues/60438
Change-Id: I3cd92dd49d4393b40f0ec888b643f0353de5e2d4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420466
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2025-04-09 09:29:32 -07:00
Paul Berry 569c901fef [sound flow analysis] Implement behaviors for equality comparisons.
This change updates the flow analysis logic for `==` and `!=`
expressions, `==` and `!=` patterns, and constant patterns, so that
when the language feature `sound-flow-analysis` is enabled, the static
types of the two expressions being compared are checked for
nullability. If one of the types is non-nullable and the other type is
`Null`, then it is known that the values will be unequal.

Note that these new behaviors break assumptions made by several
pre-existing flow analysis tests. I was able to adjust some of the
tests to preserve their old behavior, either by adjusting expectations
or running the test with `sound-flow-analysis` disabled. Some other
tests became redundant, so I removed them.

There is no behavioral change if the feature `sound-flow-analysis` is
disabled.

Bug: https://github.com/dart-lang/sdk/issues/60438
Change-Id: Ib65477e064bb8dcd761542ebe187843fe265a24b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420463
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
2025-04-08 13:56:14 -07:00
Paul Berry 6c5c9d384f [sound flow analysis] Implement behaviors for is and as.
This change updates the flow analysis logic for `is` and `as`
expressions so that when the language feature `sound-flow-analysis` is
enabled, the static type of the operand is compared to the type to the
right of the `is` or `as` keyword. If one of the types is non-nullable
and the other type is `Null`, then the type test is known to fail. For
an `as` expression, this means that the code path following the
expression will be marked as unreachable. For an `is` expression, this
means that any code paths that assume it evaluates to `true` will be
marked as unreachable.

Note that these new behaviors break assumptions made by three
pre-existing flow analysis tests. I was able to adjust one of the
tests ("equalityOp_end does not set reachability for `this`") to
preserve its old behavior. The other two tests became redundant, so I
removed them.

There is no behavioral change if the feature `sound-flow-analysis` is
disabled.

Bug: https://github.com/dart-lang/sdk/issues/60438
Change-Id: Ib3a9e96bd39cf7df4c6c297568763c0f25bc9e39
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420164
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2025-04-04 12:11:58 -07:00
Paul Berry 08154451d5 [_fe_analyzer_shared] Use TypeAnalyzerOptions to configure flow analysis.
Previously, flow analysis was configured by passing a set of named
booleans to its constructor, each to enable or disable a separate
language feature.

This change merges the flow analysis configuration with the
`TypeAnalyzerOptions` class (which was already being used for
configuring the shared `TypeAnalyzer` class). This should make it
easier to add language features to the shared code base in the future,
since there will be one common place where all features will be
configured.

To avoid duplicating the logic that creates `TypeAnalyzerOptions`,
I've had to do a bit of minor surgery to the clients:

- In the test harness in `_fe_analyzer_shared`, there is a common
  method (`Harness.computeTypeAnalyzerOptions`) that constructs
  `TypeAnalyzerOptions` based on the harness configuration. It is
  called from a few different tests.

- In `pkg/analyzer`, there is a common method
  (`computeTypeAnalyzerOptions`) that constructs `TypeAnalyzerOptions`
  based on a `FeatureSet`. It is used by
  `LibraryAnalyzer.analyzeForCompletion`,
  `LibraryAnalyzer._resolveFile`, and the late variable
  `AstResolver._typeAnalyzerOptions`.

- In `pkg/front_end`, I've moved computation of `TypeAnalyzerOptions`
  from the `InferenceVisitorImpl` constructor to the
  `TypeInferrerImpl` constructor; the options are then passed to the
  `InferenceVisitorImpl` by `_createInferenceVisitor`.

I'm doing this work now as preparation for adding support for sound
flow analysis (https://github.com/dart-lang/sdk/issues/60438), so that
I can add the logic to enable it in a clean way.

Bug: https://github.com/dart-lang/sdk/issues/60438
Change-Id: Ib845194adb404b4c0a3feeff17a14ae641d515eb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/419940
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2025-04-03 11:56:51 -07:00
Paul Berry 5d78579842 [analyzer] Fix dead code reporting for null-aware accesses.
A null-aware access can result in dead code if the target has static
type `Null`. The analyzer wasn't properly accounting for this,
resulting in some confusing ranges reported for the DEAD_CODE warning.

This change causes the following expressions to report dead code for
the code ranges indiced by `^`:

    Null myNullVar = null;
    myNullVar?[index];
    //         ^^^^^^ DEAD_CODE
    myNullVar?[index] = value;
    //         ^^^^^^^^^^^^^^ DEAD_CODE
    myNullVar?.method();
    //         ^^^^^^^^ DEAD_CODE
    myNullVar?.property;
    //         ^^^^^^^^ DEAD_CODE
    myNullVar?.property = value;
    //         ^^^^^^^^^^^^^^^^ DEAD_CODE

Note that the bug was confined solely to the logic that reports the
DEAD_CODE warning; there is no change to the reachability inferred by
flow analysis (and hence, this is a non-breaking change).

Fixes https://github.com/dart-lang/sdk/issues/60364.

Bug: https://github.com/dart-lang/sdk/issues/60364
Change-Id: I068826282fba6b9057e9c27d1d9310c65714e203
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/416723
Auto-Submit: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
2025-03-20 09:08:53 -07:00
Paul Berry c67a80f3f5 Stop using NullabilitySuffix in fe/analyzer shared code.
The getter `SharedType.nullabilitySuffix` is replaced by
`SharedType.isQuestionType`, which returns a boolean.

The method `TypeAnalyzerOperations.withNullabilitySuffixInternal` is
replaced by `SharedType.setNullabilitySuffix`, which accepts a
boolean.

Support for `*` types has been removed from `mini_types.dart`.

A few test cases in `flow_analysis_test.dart` previously used `*`
types as a way of exercising corner cases involving types that were
mutual subtypes of each other. These tests have been changed to take
advantage of the fact that `dynamic` and `Object?` are mutual
subtypes.

Change-Id: Id9904f9570fc738b388192db8536848204af03e9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/414581
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2025-03-19 11:24:21 -07:00
Kallen Tu 5883289bb5 [cfe] Handle equality with dot shorthands.
This CL adds the ability to handle == with dot shorthands in regular equality expressions and then in relational patterns.

Bug: https://github.com/dart-lang/sdk/issues/59758
Change-Id: I958bbaf9e8a63ca576024ef2ee287779064e5967
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/413321
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Kallen Tu <kallentu@google.com>
2025-03-06 11:09:20 -08:00
Paul Berry 7382fe9d0a Remove legacy support from shared type analyzer and flow analysis.
Now that the ability to run in "unsound null safety" mode has been
removed (https://dart-review.googlesource.com/c/sdk/+/412881), it is
safe to start removing the code that implements legacy
(pre-null-safety) analysis.

Change-Id: I7f998a081704030ce630d3c343185d0d41d4349a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/413524
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
2025-03-04 14:17:42 -08:00
Paul Berry 274f5639f7 [Flow analysis] Remove PropertyNotPromoted.staticType.
This field was only used to populate expectation strings in "id"
tests; it did not affect any user-visible behavior of the analyzer or
CFE.

Including information in "id" tests that doesn't affect any
user-visible behavior isn't helpful. Removing this field will enable
some upcoming flow analysis refactoring work (I intend to remove the
`ExpressionInfo._type` field, replacing its remaining usages with a
more reliable mechanism).

Change-Id: Id4c6593fae4ef25b8c21f0625e6c1f9eaa766e17
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/406403
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2025-02-08 16:51:15 -08:00
Chloe Stefantsova dcd410efd4 [analyzer][cfe] Remove TypeStructure variable from shared classes
Part of https://github.com/dart-lang/sdk/issues/54902

Change-Id: Ia70f2afd321e9b4a4762b6ed860611dee1399d87
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/404622
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
2025-01-20 01:58:43 -08:00
Paul Berry 574e54db67 [_fe_analyzer_shared] Add shared logic to support null-shorting.
This change introduces a new mixin, `NullShortingMixin`, with a type
parameter `Guard` for the data structure used by the client to desugar
null-aware accesses. The mixin maintains a stack of these guards, and
provides methods that the client can use to manipulate the stack:

- `startNullShorting` adds an entry to the stack; it should be called
  when the client encounters the `?.` part of a null-aware expression.

It also provides two hooks that the client can override if desired:

- `handleNullShortingStep`, called whenever an entry is removed from
  the stack; this will let the CFE know when it should de-sugar a null
  short using a "let" expression.

- `handleNullShortingFinished`, called whenever a sequence of entries
  is removed from the stack; this will let the analyzer know when it
  should change the static type of an expression as a result of
  null-shorting.

Also, a new optional parameter, `continueNullShorting`, is added to
`TypeAnalyzer.analyzeExpression`. If this parameter is `false` (the
default value), then any null shorting that is started during analysis
of the expression (due to the client calling `startNullShorting`) will
be terminated before returning. If it is `true`, then null shorting
won't be terminated, so it will extend to the containing
expression. For expression types that are able to extend null shorting
that appears in their target subexpression (e.g., method calls and
property accesses), the `visit` or `analyze` method should pass
`false` for this parameter when making a recursive call to analyze the
target.

Finally, the `NullShortingMixin` has a getter `nullShortingDepth`,
that `TypeAnalyzer.analyzeExpression` uses to determine when null
shorting should be terminated, and a method `finishNullShorting`, that
actually does the work of terminating null shorting. In principle,
clients don't need to invoke these parts of the `NullShortingMixin`
API. However, since the CFE doesn't always use
`TypeAnalyzer.analyzeExpression` (favoring its own internal methods
`InferenceVisitorImpl.inferExpression` and
`InferenceVisitorImpl.inferNullAwareExpression`), the CFE will need to
use them.

The "mini_ast" tests of flow analysis formerly used a method called
`nullAwareAccess` to exercise the flow analysis effects of null-aware
constructs. This was hacky and confusing, and is now unnecessary,
since the shared infrastructure now fully supports null-shorting. So
this method has been removed and replaced by the ability to mark a
method invocation or property access as null-aware.

This change only builds the infrastructure for shared analysis of
null-shorting; the analyzer and CFE still handle null shorting on
their own. In follow-up CLs I will change the analyzer and CFE to make
use of the shared mechanism.

Change-Id: Ide25a915c4d06eab751b87c1c2745749d23a8114
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/399480
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2024-12-10 19:07:18 +00:00
Paul Berry 0197beaa8e [flow analysis] Remove _typeContains method.
It wasn't necessary; `List.contains` does the same thing.

Also, remove the plumbing for `typeOperations`, a parameter of
`_typeContains` that was not used.

Change-Id: I688835512e58cb7a24336318b2006c9913c77888
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/398300
Auto-Submit: Paul Berry <paulberry@google.com>
Reviewed-by: Kallen Tu <kallentu@google.com>
Commit-Queue: Kallen Tu <kallentu@google.com>
2024-12-02 18:54:24 +00:00
Kallen Tu 53bad058b1 [flow] Add unit test for postIncDec().
Add Flow Analysis unit tests for FlowAnalysis.postIncDec() which should be used for postfix increment and decrement operations.

Bug: https://github.com/dart-lang/language/issues/3658
Change-Id: If77b6fbb5fc80d5f5d014ec0516d77578446dced
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/393441
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Kallen Tu <kallentu@google.com>
2024-11-05 18:54:02 +00:00
Paul Berry c3ce683cca [Breaking change] Account for field promotion to Null when computing reachability based on equality tests.
Fixes https://github.com/dart-lang/sdk/issues/56893.
Fixes https://github.com/dart-lang/language/issues/4127.

Bug: https://github.com/dart-lang/sdk/issues/56893
Bug: https://github.com/dart-lang/language/issues/4127
Change-Id: If8bb0144ebe7024a7f4f7c1733e24632b4549c7f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/389660
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Kallen Tu <kallentu@google.com>
2024-10-22 22:10:50 +00:00
Kallen Tu 47d550277d [flow] Issue 1721 - Allow better promotions for final variables.
Promotions should happen for final variables because they are assigned and won't be re-assigned by the time they're evaluated. In a conservative join, promotions should not be cancelled for final variables.

Language tests made in https://dart-review.googlesource.com/c/sdk/+/390340.

Bug: https://github.com/dart-lang/language/issues/1721
Change-Id: I7bb577a694ddb5572a28884de70bd8c5b68e3c25
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/390803
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Kallen Tu <kallentu@google.com>
2024-10-22 20:05:50 +00:00
Kallen Tu b85c499b60 [flow] Part 1 Issue 3658 - Store variable information after a write.
Assignments in the condition of an if statements don't store the promoted information (for the write of that variable). This fix stores the promotion of that expression for when we use it to null check or otherwise.

Part 1 because there's still some holes with postfix operators and null asserts that need to be fixed, but this behaviour stands on its own at the moment.

Everything is behind the flag so we'll iterate.

Bug: https://github.com/dart-lang/language/issues/3658
Change-Id: I8663f089a451468651efccadeb3991b34a37d899
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/388903
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Kallen Tu <kallentu@google.com>
2024-10-17 17:51:54 +00:00
Paul Berry 4f01b048dd Add tests reproducing language issue 4127.
These tests exercise the current (unintended) behavior described
https://github.com/dart-lang/language/issues/4127, which was
previously not well tested.

Adding these tests acts as a safeguard to make sure that we don't
change the current behavior by accident.  If/when we decide to fix
https://github.com/dart-lang/language/issues/4127, the test
expectations will need to be updated.

Bug: https://github.com/dart-lang/language/issues/4127
Change-Id: I02fd1d393038a304401d11cf2c19e97755ba90a0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/389584
Reviewed-by: Kallen Tu <kallentu@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2024-10-15 13:11:36 +00:00
Kallen Tu c8ee0e78a5 [flow] Pipe the language feature flag inference-update-4 through flow analysis.
Weaves the flag `inference-update-4` through to `FlowAnalysis` and updating both the analyzer and CFE point-of-entry to include the flag.

This flag will be used in `flow_analysis.dart` to hide upcoming bug fixes to flow analysis.

Change-Id: Ib0004eb4bcf0b6e579116632b5973fe969e51e90
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/388582
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Kallen Tu <kallentu@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
2024-10-08 16:32:24 +00:00
Paul Berry 38c08f919d [flow analysis] Remove ExpressionInfo.after.
Previously, the base `ExpressionInfo` class contained four fields:

- `type`: the type of the expression.

- `ifTrue`: a flow model describing the state of the program after the
  expression is evaluated, assuming the expression evaluates to
  `true`.

- `ifFalse`: a flow model describing the state of the program after
  the expression is evaluated, assuming the expression evaluates to
  `false`.

- `after`: a flow model describing the state of the prorgam after the
  expression is evaluated, making no assumptions about what value the
  expression evaluates to.

The `after` field was largely redundant, since it tracked the same
information as `FlowAnalysisImpl._current`. In fact, flow analysis
contained a substantial amount of code to copy from
`ExpressionInfo.after` to `FlowAnalysisImpl._current`, or vice versa,
in order to keep the two in sync.

The one exception was in `FlowAnalysisImpl.conditional_end`, which is
called at the end of visiting a conditional expression (`e1 ? e2 :
e3`): it joined the `after` flow models from `e2` and `e3` in order to
determine the state of the program after the conditional expression
completes. To preserve this behavior, a small amount of extra
accounting logic had to be added to the handling of conditional
expressions, to keep track of these flow models. (`e2.after` is now
stored in `_ConditionalContext.thenModel`, and `e3.after` comes from
the state of `_current` at the time of entry into
`FlowAnalysisImpl.conditional_end`).

Change-Id: I46e771f8b029550d43a5fe50366177f189a6a91d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/388081
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Kallen Tu <kallentu@google.com>
2024-10-04 13:44:40 +00:00
Paul Berry b452721942 Change "mini types" type parameters to match CFE and analyzer.
Previously, the "mini types" representation used in
`_fe_analyzer_shared` unit tests represented unpromoted type
parameters using the `PrimaryType` class (which was also used for
interface types and special built-in types like `void`) and
represented promoted type parameters using a separate
`PromotedTypeVariableType` class.

This CL changes the "mini types" representation to use a single
`TypeParameterType` class for both unpromoted and promoted type
parameters.  This parallels the representation used by the analyzer
and CFE, so it should help pave the way for sharing type system logic
between the analyzer and CFE.

To allow the `Type` constructor to distinguish whether a given
identifier represents an interface type or a type variable, tests must
register all type names they will need, using either the static method
`TypeRegistry.addTypeParameter` or the static method
`TypeRegistry.addInterfaceTypeName`.

To prevent the type names registered by one unit test from interfering
with those registered by another, tests should call
`TypeRegistry.init` in a `setUp` callback and `TypeRegistry.uninit` in
a `tearDown` callback. Methods in `TypeRegistry` contain error checks
to help make sure these calls aren't forgotten.

Change-Id: I701842ad94899c819f1a059e660510a616d00456
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/387822
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2024-10-02 12:53:10 +00:00
Chloe Stefantsova bc8104f3b2 [analyzer][cfe] Implement flow analysis for null-aware map entries
Closes https://github.com/dart-lang/sdk/issues/56786

Change-Id: I738c98b6f4e632cfbbe51221bbc3547edbc718fe
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/386800
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
2024-09-30 09:48:27 +00:00
Paul Berry 13d4eaf99a Implement matchInferableParameter in MiniAstOperations.
Previously the implementation of this method was a stub.

It turns out that all the necessary infrastructure was in place
already, however the type arguments supplied by MiniAstOperations to
TypeAnalyzerOperationsMixin and TypeAnalyzerOperations needed to be
changed: in the "mini_ast" representation of types, an
InferableParameter is represented by a String, not a
PromotedTypeVariableType. This is because InferableParameter is meant
to represent the declaration of the type parameter
(StructuralParameter for the CFE, TypeParameterElement for the
analyzer), not the type itself. The types used for unit testing in
_fe_analyzer_shared don't have a separate notion of the declaration of
a type parameter, so we just use its name.

Implementing this logic required adding a method
`TypeSystem.matchTypeParameterType`, which checks if a Type is a type
parameter type, and returns the name of the type parameter if so. I
based this on the previously existing `TypeSystem._isTypeVar` method
(which performed the same job but did not return the type parameter
name).

I also took the liberty of fixing a flow analysis test that treated
`T` as a type variable but failed to mark it as a type variable by
calling `addTypeVariable`.

This should help pave the way for unit testing more of the shared
infrastructure for types.

Change-Id: Ia7a9777ec3d90a5886567dcb9f831e388e372f32
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/386607
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2024-09-25 12:23:01 +00:00
Chloe Stefantsova 3acd125e44 [analyzer][cfe] Implement types and type schemas as extension types
This CL removes Type and TypeSchema type variables from the abstract
classes with shared code between the CFE and the analyzer. Extension
types SharedTypeView and SharedTypeSchemaView are declared to replace
the type variables.

The update propagates the discipline of distinguishing between types
and type schemas into the clients of the shared code. Now the code in
the CFE and the Analyzer that uses the shared code needs to statically
specify the interpretation of their type objects as either types or
type schemas.

Another benefit of the update is SharedTypeView and
SharedTypeSchemaView being less opaque than the Type and TypeSchema
type variables, which removes the necessity for some code duplication
in abstract methods for types and type schemas.

Finally, the update enables some further changes in the shared code
between the Analyzer and the CFE.

Change-Id: I88e8cfcd47d4f721974b4f2612521e85bb54c30f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/379302
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
2024-08-23 08:10:09 +00:00
Paul Berry b026068313 Flow analysis: fix handling of list pattern type promotion.
When analyzing the type test implied by a pattern, flow analysis uses
three variables to control promotion behavior:

- `matchFailsIfWrongType`, which indicates whether flow analysis needs
  to account for the possible control flow path resulting from the
  type test failing. (This is `false` for cast patterns, because in
  the case where a cast pattern fails, an exception is thrown).

- `matchMayFailEvenIfCorrectType`, which indicates whether flow
  analysis needs to account for the possible control flow path
  resulting from the type test succeeding, but some other check
  causing the match to fail. (This is `true` for most list patterns,
  because the list pattern will fail to match if the list has the
  wrong length).

  (Note that `matchMayFailEvenIfCorrectType` doesn't account for the
  fact that a pattern match might fail due to failure in a subpattern
  match; this is automatically handled by the fact that flow analysis
  walks through the complete pattern in the order in which it
  executes.)

- `coversMatchedType`, which indicates whether the type test is
  guaranteed to succeed due to a subtype relationship between the
  matched value type and the type being tested (e.g. a `num x` pattern
  is guaranteed to succeed if the matched value type is `int`).

In the case where `matchFailsIfWrongType` is `true`,
`matchMayFailEvenIfCorrectType` is `true`, and `coversMatchedType` is
`false`, flow analysis must account for the fact that there are two
ways that the pattern match might fail: the type test might fail, or
the type test might succeed but then the pattern match might fail for
some other reason.

Before this change, this was done incorrectly, and flow analysis only
accounted for the possibility of the type test failing.

Fixes #55543.

Bug: https://github.com/dart-lang/sdk/issues/55543
Change-Id: I86603ec5f940402313f32177212b7960878db97f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/364942
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2024-05-01 17:12:00 +00:00
Paul Berry 0c7585c907 Start building a shared class hierarchy for types.
This commit introduces the following new classes:

- SharedType, which represents the common interface between the
  DartType classes in the analyzer and the CFE.

- SharedRecordType, which represents the common interface between the
  RecordType classes in the analyzer and the CFE.

- SharedNamedType, which represents the common interface between the
  analyzer and CFE representations of a name/type pair.

- SharedUnknownType, which represents the common interface between the
  analyzer and CFE representations of the unknown type (`_`).

This allowed three methods to be removed from the
`TypeAnalyzerOperations` class:

- `areStructurallyEqual`, which is replaced by
  `SharedType.isStructurallyEqualTo`.

- `asRecordType`, which is no longer needed because `is
  SharedRecordType` can be used instead.

- `isUnknownType`, which is no longer needed because `is
  SharedUnknownType` can be used instead.

And one method to be removed from the `FlowAnalysisTypeOperations`
class:

- `isSameType`, which is replaced by `operator ==`. (Technically this
  could have been done even without introducing a shared class
  hierarchy, since `operator ==` is defined in the shared base class
  `Object`).

The long term goal is to fill out the shared class hierarchy to cover
other kinds of types (interface types, function types, void, etc.),
and to move most of the shared logic from the analyzer and CFE
DartType class hierarchies into shared code. This should reduce the
risk of implementation skew between the analyzer and CFE, and to
streamline the implementation of future features. Additionally, the
hope is to eventually remove, or drastically simplify, classes like
`TypeAnalyzerOperations`, so that the code in `_fe_analyzer_shared`
can be written in simpler and more straightforward way.

Change-Id: I5d3a929057959f77ccff8dbed5671f9bca6259c5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/362481
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2024-05-01 16:41:47 +00:00
Paul Berry 97edad1568 Flow analysis: Use extension type erasure for implicit is reachability.
Whenever a pattern performs an implicit `is` test, flow analysis
attempts to determine whether the `is` test is guaranteed to succeed;
if it is, then flow analysis considers the code path in which the `is`
test fails to be unreachable. This allows flow analysis to recognize
switch statements that are trivially exhaustive (because one of the
cases is guaranteed to match), avoiding spurious errors such as
"variable must be assigned before use" or "missing return statement".

This change upgrades the logic for computing when an `is` test is
guaranteed to succeed, so that it accounts for type erasure of
extension types. This brings flow analysis's treatment of switch
statements into closer alignment with the exhaustiveness checker,
which should reduce the risk of confusing error messages. For more
information see
https://github.com/dart-lang/language/issues/3534#issuecomment-1885839268.

Fixes https://github.com/dart-lang/language/issues/3534.

Bug: https://github.com/dart-lang/language/issues/3534
Change-Id: Ib73d191e04e7fa8c0e6888c2733dae73d8f389da
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/345822
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2024-01-24 15:23:27 +00:00
Paul Berry c9cf5b9916 Add a type parameter to TypeAnalyzer to represent type schemas.
The front end and analyzer use the same representation for types and
type schemas, with the unknown type schema (`_`) treated as a
pseudo-type. This creates the risk of accidentally mixing types and
schemas, resulting in `_` accidentally "leaking" into the type system
and showing up in static analysis results or error messages.

As a step toward reducing this risk, this change adds a type parameter
to `TypeAnalyzer`, preventing the shared type analysis code from being
able to assume that types and schemas are represented the same. This
extra discipline makes it much easier to search through the code and
identify how types are manipulated vs. how type schemas are
manipulated, and makes it impossible for the shared type analysis to
accidentally leak `_` into the type system.

I believe this change will also make it easier to implement some type
inference improvements we've been contemplating, such as improved type
inference of `.map(...).toList()`, as well as
https://github.com/dart-lang/language/issues/3471, because those
improvements may require introducing new kinds of type schemas.

Change-Id: Ifcd7e2c4e1172ee39719ce8c8b10d7f10f6a7b6f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/345353
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
2024-01-18 15:28:25 +00:00
Paul Berry 5510ef63a6 Clean up shared Operations classes.
The classes `Operations`, `TypeOperations`, and `VariableOperations`
are reworked as follows:

- The class `FlowAnalysisOperations<Variable, Type>` contains the API
  used by flow analysis to manipulate the client's representation of
  types and variables. This class (and the classes that support it) is
  located in `flow_analysis_operations.dart`, a sibling of
  `flow_analysis.dart`.

- Note that `FlowAnalysisOperations<Variable, Type>` has a supertype,
  `FlowAnalysisTypeOperations<Type>`, which contains the portion of
  the API that is only concerned with types. This simplifies the
  implementation of flow analysis, by letting some of its lower-level
  operations be agnostic to how the client represents variables.

- The class `TypeAnalyzerOperations<Variable, Type>` contains the API
  used by the shared type analyzer to manipulate the client's
  representation of types and variables. This class (and the classes
  that support it) is located in `type_analyzer_operations.dart`, a
  sibling of `type_analyzer.dart`.

- Several abstract methods and getters that were previously in the
  `TypeAnalyzer` class are moved to `TypeAnalyzerOperations`, for
  consistency with `FlowAnalysisOperations`.

Since the type analyzer calls out to flow analysis, the class
`TypeAnalyzerOperations` is a subtype of
`FlowAnalysisOperations`. This means that clients only need to
implement a single API, `TypeAnalyzerOperations`.

Change-Id: I7f33976d49bc998a7edd3f07f31576fe54886999
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/343280
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
2023-12-22 21:33:32 +00:00
Paul Berry d42390d7c1 Clean up enable/disable logic for flow analysis and type inference tests.
Previously, the logic for enabling and disabling language features in
flow analysis and type inference tests relied on setters in the
`Harness` class that (a) were mostly unmatched with getters, and (b)
were almost exclusively used in just a single one direction
(e.g. `Harness.legacy` defaulted to `true`, so it was only ever set to
`false`).

Cleaned up so that there are explicit `enable` and `disable` methods
in the `Harness` class to cover all the use cases.

Change-Id: I5ccc8585f803fec634cad1472395ea0d135c87c6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/332064
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Lasse Nielsen <lrn@google.com>
2023-10-27 17:51:44 +00:00
Paul Berry c23ac067eb Improve the behavior of "why not promoted" when field promotion disabled.
If the user attempts to promote a property, and their language version
does not permit field promotion, the "why not promoted" logic now
checks whether the language version is the sole reason for the failure
in property promotion. In other words, it checks whether the property
would have been promotable *if* field promotion had been enabled. If
it would, then the context message displayed to the user explains that
the property did not promote because field promotion is not supported
by the current language version.

However, if there is some secondary reason why the property failed to
promote (in other words, if the property would not have been
promotable even if field promotion had been enabled), then the context
message now favors the secondary reason.

Rationale: imagine a user is maintaining a package that doesn't yet
support SDK version 3.2, and that package contains some property
that's non-promotable both because the language version is prior to
3.2 *and* for some other reason (e.g., because the property isn't a
private field). It would be quite frustrating if the user saw a
context message suggesting that the property would be promotable in
SDK 3.2, and then went to a lot of effort to bump their minimum SDK
version, only to discover *after* the bump that the property is still
not promotable.

In the process of making this change, I discovered that the CFE
doesn't support field promotion in patch files. This is because patch
files aren't listed in `SourceLoader.sourceLibraryBuilders`, so the
logic in the `FieldPromotability` is never invoked for those
files. Since patch files are an artifact of SDK development, and will
never be used by end users, it doesn't seem worth going to extra
effort to add this support. However, I've taken care to make sure that
the "why not promoted" logic recovers gracefully in patch files (by
simply not generating a context message).

Change-Id: I6c0d1c0f4b8a7690f6f775408cb5e857b2dd7b03
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/330241
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Lasse Nielsen <lrn@google.com>
2023-10-25 19:41:26 +00:00
Paul Berry bb524468ef Wire up "why not promoted" messages for failed field promotion due to conflict.
Previously, when field promotion failed due to a conflict with another
declaration in the same library, the client (analyzer or front_end)
was responsible for returning a value of
`PropertyNonPromotabilityReason.isInterferedWith` from
`Operations.whyIsPropertyNotPromotable`. Flow analysis would propagate
this value into a `PropertyNotPromoted` object whose
`documentationLink` getter returned `null`. The client was then
responsible for tracking down all the conflicting fields and getters
and creating the appropriate context messages for them (but this
functionality wasn't implemented yet).

With this change, the `PropertyNotPromoted` is now abstract, with two
subclasses to represent the two cases the client has to handle:

- `PropertyNotPromotedForInherentReason` to cover the case where a
  property cannot be promoted due to the fact that it is inherently
  not promotable (i.e. it's not final, it's public, it's external,
  it's not a field, or it's in a library where field promotion isn't
  enabled). In this case the client simply has to generate the
  appropriate context message and attach it to the site where the
  property is declared, and it can rely on having access to a non-null
  `documentationLink` to include in the context message.

- `PropertyNotPromotedDueToConflict` to cover the case where the
  property cannot be promoted due to a conflict with some other
  property in the same library. In this case the client has to
  generate multiple context messages, one for each conflicting
  declaration, and it has to associate each one with the appropriate
  documentation link from the `NonPromotionDocumentationLink` enum.

The `NonPromotionReasonVisitor` base class has been updated to reflect
this split, so that the logic for handling these two cases is in
separate methods in the client.

The front_end logic for handling non-promotion due to conflict is now
fully implemented. The analyzer logic will be addressed in a follow-up
CL, since it's more complex (it requires plumbing additional data
through the summary file format).

Finally, the nomenclature in the `FieldNameNonPromotabilityInfo` is
adjusted to match the new context messages.

Change-Id: Ieed70d1a3572abbc726ae34584d85c7a8aee0732
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/327712
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2023-09-28 03:37:01 +00:00
Paul Berry 8529e31a0f Flow analysis: track detailed non-promotability reasons for getters.
- When the shared logic in `field_promotability.dart` decides that a
  given field should be non-promotable based on attributes of the
  field itself (i.e. because it's public, non-final, or external), it
  returns information to the caller about the reason for
  non-promotability.

- This information is recorded by the analyzer and CFE, and delivered
  back to flow analysis in response to a new callback method,
  `whyIsPropertyNotPromotable`. Flow analysis records this information
  in the `PropertyNotPromoted`, which is delivered to clients when a
  compile-time error occurs due to a property access not being
  promotable. This ensures that the appropriate
  `http://dart.dev/go/non-promo-...` link will be associated with the
  context message. In a future CL, the context messages themselves
  will be updated to match the link.

- The shared logic in `field_promotability.dart` also collects, for
  each field name that isn't promotable, all the reasons why that
  particular field name is non-promotable, in a new data structure,
  `FieldNameNonPromotabilityInfo`. In a future CL, this data structure
  will be used to explain to the caller situations in which a field is
  non-promotable due to interference from other fields or getters with
  the same name.

Change-Id: I89ad102a4bec071bf59374971a8d83b061d4ec1d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/327901
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Lasse Nielsen <lrn@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2023-09-27 20:04:01 +00:00
Paul Berry 2887fe0b2c Flow analysis: rework of promotionInfo data structure.
This change updates `FlowModel.promotionInfo`, the primary data
structure used by flow analysis to track program state, so that
instead of being a `Map<int, PromotionModel<Type>>`, it is represented
by a new data structure called a `FlowLink`, an immutable data
structure describing program state in a way that's particularly
optimized for flow analysis's usage patterns.

Like a map, a `FlowLink` data structure represents a collection of
key/value pairs (where the keys are integers), however instead of
storing the keys and values in a hashtable, each `FlowLink` object
contains a single key/value pair and a pointer to a previous
`FlowLink` object. The value associated with a given key can be looked
up by starting with the current `FlowLink` and walking backwards
through the linked list of `previous` pointers until a matching key is
found. (An empty map is represented by `null`). This makes it an
`O(1)` operation to update the promotion state associated with a
single promotion key (an operation that flow analysis performs
frequently), since all that is required is a single allocation.

If the `previous` pointers are regarded as parent pointers, all the
`FlowLink` objects produced by a given run of flow analysis form a
tree that mirrors the dominator tree of the code being analyzed.

To optimize reads of `FlowLink` data structures, there is a
`FlowLinkReader` class that keeps track of a lookup table reflecting
the implicit map represented by a given `FlowLink` object; this table
can be updated to reflect a different `FlowLink` object in `O(n)`
time, where `n` is the number of edges between the two `FlowLink`
objects in the tree. Since flow analysis is based on a depth-first
traversal of the syntax tree of the code being analyzed, it has a high
degree of tree locality in the `FlowLink` objects it needs to be able
to read, so these `O(n)` updates do not consume much CPU.

The `FlowLinkReader` class is also able to compute a difference
between the program states represented by two `FlowLink` objects, in
`O(n)` time, where `n` is the number of edges between the two
`FlowLink` objects in the tree. This is used by flow analysis to
compute the program state after a control flow join, so that it does
not need to spend any time examining promotion keys that are unchanged
since the corresponding control flow split.

For more information about the `FlowLink` data structure and how it
works, see the comments in `flow_link.dart`.

This change improves the performance of CFE compilation fairly
substantially:

    instructions:u: -0.8167% +/- 0.0007% (-158214865.67 +/- 130252.25)
    branches:u: -0.4694% +/- 0.0009% (-18575169.00 +/- 37220.97)
    branch-misses:u: -1.0009% +/- 0.7189% (-575742.67 +/- 413521.70)
Change-Id: Ia87458ee599977e6efdc9f0e7aa283a41f84f616
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/326900
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Morgan :) <davidmorgan@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2023-09-25 15:41:20 +00:00
Paul Berry f5208f5d59 Flow analysis: prepare for rework of promotionInfo data structure.
`FlowModel.promotionInfo` is currently a map from integer promotion
keys to PromotionModel data structures. This is inefficient because
FlowModel is an immutable data structure, so whenever the promotion
state of a variable changes, the map must be duplicated. In a
follow-up CL I will be changing `FlowModel.promotionInfo` to a much
more efficient data structure. However, that data structure will
require some extra plumbing. For ease in code review, I'm doing the
extra plumbing first, as its own CL.

This CL makes the following changes:

- Removes unnecessary null checks from the `FlowModel.withInfo`
  constructor. These null checks are no longer needed because all the
  clients of flow analysis are now fully null safe. This change is not
  strictly necessary; it's just a long-overdue clean-up.

- Adds a `helper` argument (of type `FlowAnalysisHelper`) to
  `FlowModel.conservativeJoin`, `FlowModel.declare`, and
  `FlowModel.infoFor`, `FlowModel.inheritTested`, and
  `FlowModel._updatePromotionInfo`. This is needed because these
  methods will need access to `FlowAnalysisHelper` in order to read
  and update the new data structure.

- Removes the `typeOperations` argument of `FlowModel.inheritTested`,
  since it can be easily obtained from the new `helper` argument.

- Changes `FlowModel._updatePromotionInfo` to a public method
  annotated with `@visibleForTesting`. This will be needed by flow
  analysis unit tests to create the new data structure.

Note that this change causes a small regression in the performance of
CFE compilation, due to the extra `helper` arguments:

    instructions:u: 0.0693% +/- 0.0008% (13413234.33 +/- 155671.09)
    branches:u: 0.0886% +/- 0.0012% (3502620.67 +/- 45724.97)

The follow-up CL that switches to a more efficient data structure will
result in a performance improvement roughly an order of magnitude
larger.

Change-Id: I21c13fb817f05281b558f0473119473a26ea0fb8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/326860
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
2023-09-20 12:29:50 +00:00
Paul Berry ef74475404 Create an enum to represent links used in "why not promoted" context messages.
This makes it easier to see at a glance which messages need to be
supported by the website.

I've included the new links that I intend to support as part of the
new "field promotion" feature.

Bug: https://github.com/dart-lang/sdk/issues/53102
Change-Id: I67ad47c5a00db9807a6c726677a06427cdbe02c2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/325803
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
2023-09-18 18:44:32 +00:00
Paul Berry 602e38eab4 Flow analysis: fix integration of field promotion with object patterns.
When entering visiting a subpattern of an object pattern, the flow
analysis engine now updates `_FlowAnalysisImpl._scrutineeReference` to
a `_PropertyReference` referring to the property being matched; this
ensures that if the subpattern match implies a type promotion, and the
property in question is promotable, the type promotion will be applied
to the property.

Also, if the property has already been promoted at the time of entry
to the subpattern, the promoted property type is used as the matched
value type.

Includes unit tests and language tests for the new functionality.

Fixes #53100.

Change-Id: I6d28e9a7d188bf1136e8517d6aa06af3b4c31c69
Bug: https://github.com/dart-lang/sdk/issues/53100
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/323001
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2023-08-29 16:05:49 +00:00
Paul Berry aef81ef08e Flow analysis: fix scrutinee promotion for fields.
Prior to this change, the SSA node stored in `PromotionModel.ssaNode`
was only correct for promotion models that represented variable
references. If a promotion model represented a promotable field, its
`ssaNode` pointed to a bogus SSA node. This had two undesirable
effects:

- It meant that `FlowModel.rebaseForward` needed to contain a hack to
  prevent it from looking at the bogus SSA node for a promotable
  field, and falsely concluding that the field's value had been
  reassigned (which is impossible for promotable fields)--see
  https://dart-review.googlesource.com/c/sdk/+/321752.

- It meant that if a promotable field was used as a scrutinee in a
  refutable pattern match, the promotion logic would look at the bogus
  SSA node for the field, and falsely conclude that its value had been
  reassigned, preventing field promotion from working during pattern
  matching.

This change ensures that the correct SSA node is always stored in
`PromotionModel.ssaNode`, and removes the hack in
`FlowModel.rebaseForward`. This required some re-ordering some of the
logic for control flow joins, to ensure that when a join creates a
fresh promotion model for a property, it has already created the
corresponding `_PropertySsaNode` (previously, it created the
`_PropertySsaNode` afterwards, but that is too late since the
`PromotionModel` class is immutable).

Unit tests and language tests are introduced to validate the newly
fixed behavior for promotable fields used as a scrutinee in a
refutable pattern match.

Also, the uses of `FlowModel.infoFor` in queries such as
`getMatchedValueType`, `isAssigned`, `isUnassigned`, and
`promotedType` were changed to simple map lookups, to prevent bogus
SSA nodes from being created and then immediately discarded. This
resulted in a fairly significant boost to CFE compilation speed:

  page-faults:u: -1.2664% +/- 0.1535% (-2531.33 +/- 306.73)
  instructions:u: -0.6210% +/- 0.0009% (-119891846.00 +/- 180585.35)
  branches:u: -0.6765% +/- 0.0014% (-26637478.67 +/- 54272.64)
  branch-misses:u: -0.9562% +/- 0.8909% (-548444.00 +/- 510991.65)

Change-Id: I30f82e8a4ba11236735258077d61d36717fa32c2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/322443
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2023-08-28 15:01:56 +00:00
Paul Berry b29e4a8554 Flow analysis: unit test that join variables are usable and promotable.
These tests fill a coverage gap in the flow analysis unit
tests. Previously we tested that "join" variables were created by
logical-or patterns and switch cases that share a body, but we didn't
have any tests to verify that those variables could be used.

We now verify that those variables can be read from and promoted.

Change-Id: Ic8948cd307edff429aea9007183d65cc3a770ef3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/322360
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
2023-08-24 16:42:20 +00:00
Paul Berry 30e8ed8410 Flow analysis: fix field promotion based on condition variables.
When the result of an `is` test or null check is stored in a boolean
variable, and later recalled for use in flow control, the flow models
that were computed at the time the variable was stored need to be
updated to reflect any further change to flow state that happened
between the test and the usage. This is done by
`FlowModel.rebaseForward` method. `rebaseForward` takes two flow
models as input: `this`, which represents the flow state that was
computed at the time the condition variable was stored, and `base`,
which represents the flow state at the time the condition variable is
recalled.

Flow analysis adds promotion keys for variables to the flow state at
the time their declarations are encountered, and in certain
circumstances removes them after they go out of scope. But for
properties, it only adds promotion keys when the promotion occurs. So
prior to the addition of field promotion, if `this` contained a
promotion key that wasn't present in `base`, that could only mean that
the promotion key was associated with a variable that had gone out of
scope; accordingly, it was safe for `rebaseForward` to simply ignore
that key. (It did so implicitly, by only ever examining the promotion
keys in `this`). But with the addition of field promotion, it is now
possible that the promotion key represents a property that was
promoted in `this`, and hence the promotion needs to be kept. This CL
adds the necessary logic to keep the promotion.

In addition, there is a subtle difference in the relationship between
the `PromotionModel` and `SsaNode` data structures for local variables
versus properties. For local variables, the promotion key is
determined solely from the variable name; then, this promotion key is
looked up in the current `FlowModel` to obtain a `PromotionModel`, and
the `PromotionModel` contains a prointer to the `SsaNode`. For
properties, the property name is looked up in the
`promotableProperties` map of the parent `SsaNode`; this points to a
`_PropertySsaNode`, which contains the promotion key, and when this
promotion key is looked up in the current `FlowModel` to obtain a
`PromotionModel`, that `PromotionModel` contains a pointer to a bogus
`SsaNode`.

For local variables, the `SsaNode` pointed to by the `PromotionModel`
is important, because if it's different between `this` and `base`,
then the variable in question received a new value between the time
the condition variable was stored and the time the condition variable
was recalled; therefore the promotion should be disregarded. However,
for properties, the `SsaNode` pointed to by the `PromotionModel` is
bogus, so if it's different between `this` and `base`, that shouldn't
block promotion. This CL adds the necessary logic to avoid the
`SsaNode` check for properties.

This situation is very confusing so I've added more detail to the
comment above `PromotionModel.SsaNode` explaining it. In a future CL I
will try to clean up the confusing situation by eliminating the bogus
`SsaNode`s pointed to by `PromotionModel`s for properties.

Fixes https://github.com/dart-lang/sdk/issues/53273.

Bug: https://github.com/dart-lang/sdk/issues/53273
Change-Id: I1d528e25de1eb2ed63d0ee1a00faa5ad5b5061ca
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/321752
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2023-08-24 14:02:29 +00:00