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>
The CFE desugars null-aware expressions such as `a?.b` into equivalent
`let` expressions involving a temporary guard variable (e.g., `let x =
a in x == null ? null : x.b`). This desugaring happens in the body
builder, prior to flow analysis. As a result, the expressions and
variables passed to flow analysis are those of the desugared `let`
expression rather than those of the code the user wrote. So a hack is
needed to ensure that flow analysis produces the correct result; the
hack involves promoting the temporary guard variable in the code path
where it is not null.
Prior to this change, the hack was done entirely in the CFE. Instead
of making a single pair of calls to
`FlowAnalysis.nullAwareAccess_rightBegin` and
`FlowAnalysis.nullAwareAccess_end` for every null-aware expression,
the CFE made two nested pairs of calls: one to promote the true target
of the null-aware access, and the other to promote the temporary guard
variable.
This had the advantage of keeping the hack entirely in the CFE, but
unfortunately it got in the way of fixing
https://github.com/dart-lang/language/issues/4344, because it
prevented flow analysis from properly recognizing field accesses
inside null-aware expressions.
This change adds an optional `guardVariable` parameter to
`FlowAnalysis_nullAwareAccess_rightBegin`, and shifts the
responsibility for promoting the temporary guard variable to the flow
analysis engine itself. With this change, the CFE no longer needs to
make two nested pairs of calls to
`FlowAnalysis.nullAwareAccess_rightBegin` and
`FlowAnalysis.nullAwareAccess_end`, and flow analysis now has the
necessary information to recognize field accesses inside null-aware
expressions.
I will perform the actual fix for
https://github.com/dart-lang/language/issues/4344 in a follow-up CL.
Note that in the future, I would like to change the CFE so that it
desugars null-aware accesses during type inference rather than during
the body builder; this will allow the expressions and variables passed
to flow analysis to be precisely those of the code the user wrote;
that in turn will give us extra confidence that flow analysis produces
the same results in the analyzer and the CFE.
Change-Id: Ie63619a3ca0f011c1ae8e3c0c76b5bc7c1ab8419
Bug: https://github.com/dart-lang/language/issues/4344
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/427341
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
This moves the creation of class builders for anonymous mixin application to after the creation of the normal class builders.
A ClassDeclaration interface is added to support class builders from different fragments. This is also a step towards creating class builders fully through fragments.
TEST=existing
Change-Id: Ia6b4a17648bdc89b89fd3cfdfe39d24d347b6341
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/407420
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This removes the support running macros in the CFE.
The scanner and package:kernel still have support for the macro modifier. This will be removed in a follow-up.
The metadata expression parser is deliberately left in, since it might serve as the basis for a parser AST.
Change-Id: I06d91eb0fac2e7a71e6afde647b03be3814dbd5f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/406963
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Morgan :) <davidmorgan@google.com>
This updates the exhaustiveness checking to return the shortest witness when no more cases match a value.
This also fixes an exponential case that occurred when checking for reachability.
Closes#59927
Change-Id: I82ede75113ca5d361875620287f7ce3c5fb2f5d2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/405120
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Since the class `TypeConstraintGatherer` (which is only used in
`_fe_analyzer_shared`'s unit tests) is now used both by
`type_constraint_gatherer_test.dart` and by `mini_ast.dart`, it makes
sense for it to live in its own file.
Change-Id: I7c8a58cfbf3724e2c8313b701345bc47ce032522
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/404060
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Extension methods can no longer be disabled. This CL removes the option
from the scanner config and the parser related specific recovery.
It also removes any specific (triggered) test of the functionality.
A follow-up CL will do the same for NNBD.
Change-Id: Ia385008e5ed1333fb37697b8fe424b8759bce198
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/403581
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
The following shared getters are renamed so that their names are
distinct from the corresponding getters in the analyzer:
- `SharedFunctionTypeStructure.positionalParameterTypes` is renamed to
`positionalParameterTypesShared` to be distinct from the analyzer's
public getter `FunctionType.positionalParameterTypes`.*
- `SharedFunctionTypeStructure.returnType` is renamed to
`returnTypeShared` to be distinct from the analyzer's public getter
`FunctionType.returnType`.
- `SharedNamedFunctionParameterStructure.type` is renamed to
`typeShared` to be distinct from the analyzer's public getter
`FormalParameterElement.type`.
- `SharedNamedTypeStructure.type` is renamed to `typeShared` to be
distinct from the analyzer's public getter
`RecordTypeNamedField.type`.
- `SharedRecordTypeStructure.positionalTypes` is renamed to
`positionalTypesShared` to be distinct from the analyzer's public
getter `RecordType.positionalTypes`.*
- `SharedRecordTypeStructure.sortedNamedTypes` is renamed to
`sortedNamedTypesShared` to be distinct from the analyzer's public
getter `RecordType.sortedNamedTypes`.
- `SharedTypeParameterStructure.bound` is renamed to `boundShared` to
be distinct from the analyzer's public getter
`TypeParameterElement2.bound`.
*Note that `FunctionType.positionalParameterTypes`,
`RecordType.positionalTypes`, and `RecordType.sortedNamedTypes` were
unintentionally exposed as part of the analyzer's public API. In a
previous CL I marked them as deprecated.
These renames pave the way for changing the analyzer's `DartType`
class so that it implements `SharedTypeStructure<TypeImpl>` rather
than `SharedTypeStructure<DartType>` (without the renames, the public
getters mentioned above would all have to be changed to have type
`TypeImpl`, and that in turn would expose `TypeImpl` through the
analyzer public API, which we don't want to do).
Once `DartType` implements `SharedTypeStructure<TypeImpl>`, that will
allow all the other uses of `SharedTypeStructure<DartType>` in the
analyzer to be gradually migrated to
`SharedTypeStructure<TypeImpl>`. Once that is done, `DartType` can be
changed so that it no longer implements
`SharedTypeStructure<TypeImpl>` at all (`TypeImpl` will implement
`SharedTypeStructure<TypeImpl> instead). This will free us up to make
future changes to the `SharedTypeStructure` base class without
inadvertently exposing those changes through the analyzer public API.
This is part of a larger arc of work to change the analyzer's use of
the shared code so that the type parameters it supplies are not part
of the analyzer public API. See
https://github.com/dart-lang/sdk/issues/59763.
Change-Id: I0686fdeae304f8948484516f0249841b79e7da6c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/403625
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
The following shared getters are renamed so that their names are
distinct from the corresponding getters in the analyzer:
- `SharedFunctionTypeStructure.sortedNamedParameters` is renamed to
`sortedNamedParametersShared` to be distinct from the analyzer's
getter `FunctionTypeImpl.sortedNamedParameters`.
- `SharedFunctionTypeStructure.typeFormals` is renamed to
`typeParametersShared` to be distinct from the analyzer's getter
`FunctionTypeImpl.typeFormals`.
- `SharedNamedFunctionParameterStructure.name` is renamed to
`nameShared` to be distinct from the analyzer's getter
`ParameterElement.name`.
- `SharedNamedTypeStructure.name` is renamed to `nameShared` to be
distinct from the analyzer's getter `RecordTypeNamedFieldImpl.name`.
These renames pave the way for switching the analyzer's use of these
shared types over to the new element model. They're necessary because
when the analyzer switches over to the new element model:
- `FunctionTypeImpl.sortedNamedParameters` will no longer have the
correct type to override
`SharedFunctionTypeStructure.sortedNamedParameters`; instead, it
will be necessary to convert each named parameter to a corresponding
element in the new element model (`FormalParameterElementImpl` or
`ParameterMember`).
- `FunctionTypeImpl.typeFormals` will no longer have the correct type
to override `SharedFunctionTypeStructure.typeParametersShared`;
instead, it will be necessary to use
`FunctionTypeImpl.typeParameters` (which is a list of
new element model type parameters).
- `ParameterElementMixin` will no longer implement
`SharedNamedFunctionParameterStructure`; instead,
`FormalParameterElementImpl` and `ParameterMember` (which are
classes in the new element model) will implement it. The class
`FormalParameterElementImpl` deliberately doesn't have a `name`
getter; instead it has a `name3` getter, with slightly different
semantics (it returns `null` rather than the empty string when there
is no name).
Change-Id: I629e45b6fc588e6a86f7590aab853064e31ab661
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/402220
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
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>
Rework the methods `checkIR`, `checkSchema`, and `checkType`, so that
rather wrap the `CollectionElement`, `Expression`, or `Statement` node
in a new node that performs the check, they return the existing node,
and store the value to be checked in a private field. The checks are
now performed as a side effect of `dispatchCollectionElement`,
`dispatchExpression`, or `dispatchStatement`.
The major advantage of this change is that previously, adding one of
these checks to an expression in a "mini_ast" test caused an extra
call to be made to `analyzeParenthesizedExpression`, so that flow
analysis information would be propagated from the wrapped node to the
node that performs the check. At the moment, extra calls to
`analyzeParenthesizedExpression` don't have any effect other than
forwarding flow analysis information, so this is benign. But in a
follow-up CL, I plan to add null-shorting support to "mini_ast" tests,
and this will mean that a side effect of
`analyzeParentehsizedExpression` is to terminate null
shorting. Re-working the checks now will ensure that when
null-shorting support is added, the checks won't terminate null
shorting as an accidental side-effect.
Change-Id: I484f29b3ce63e15ab19100462d6a4b5c6b875d34
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/399025
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This is the shared base class for all representations of the type
`Null`. This allows the shared codebase to use `is` tests to tell when
a type is `Null`.
Change-Id: I98059b60c7eaab9c9f1e3f7addb7913dffc9cf9d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/396380
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This CL removes the unnecessary conversion from `TypeStructure` types
to `SharedTypeView` and `SharedTypeSchemaView` and back. By design,
the shared constraint generation method is operating on
`TypeStructure` types and uses the shared procedures named with the
'Internal' suffix to transform those types. In this CL the shared
procedures that are only used in the shared constraint generation
method or in 'Internal' procedures are converted to 'Internal'
themselves, and the corresponding update in the parameters accepted
and returned by those procedures is made.
Part of https://github.com/dart-lang/sdk/issues/54902
Change-Id: Ic8596b342bde7e78093e990fc0f12f705ff6dee1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/395962
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
This required improving the implementation of `operator == ` for
function types, to check that the bounds match (after performing
appropriate substitutions). It also required modifiying
`FunctionType.substitute` to apply the substitution to the bounds.
The API for `TypeParameter` was changed slightly, so that it's
possible to distinguish between an explicit bound of `Object?` an an
implicit one. This allows `FunctionType.toString()` to avoid
outputting `extends Object?` next to a type parameter that has an
implicit bound.
Change-Id: Iad31a44a1f87ca0cd830b8c802495b7ce2c1caab
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/396141
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Generic function types have the following special behaviors:
- Their implementations of `hashCode` and `==` take care to ensure
that if two generic types differ only in the names of their type
parameters (i.e. they are alpha-equivalent), they will compare equal
and have the same hash code. For example, `T Function<T>()` and `U
Function<U>()` represent the same type. This logic takes advantage
of `Type.substitute` and `Type.gatherUsedIdentifiers`, added in
recent CLs.
- When parsing a generic function type, references to type formals can
appear in the return type or parameter types. E.g. in the function
type `T Function<T>()`, the return type `T` refers to the type
formal `T` declared by the function type. It doesn't refer to a `T`
declared elsewhere. To make this possible, the parameter
`typeFormalScope` has been added to `Type.materialize`; this carries
the meaning of type formals through the recursive process of
transforming a `_PreType` into a corresponding `Type`.
In a follow-up CL, I will use this new support to unit test the
generic function type logic that's recently been added to
`TypeConstraintGenerator` (see
https://dart-review.googlesource.com/c/sdk/+/393860).
Change-Id: Ib5e392f844990185b2602d22809eac5599505aff
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/395687
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
The `_PreType` class (and its subclasses) mirrors the `Type` class,
except that it represents types in a more raw form, before identifiers
have been resolved to their associated meanings. For example, the
`_PreType` representing `int` is a `_PrePrimaryType` whose `typeName`
field is the string `int`, whereas the `Type` representing `int` is a
`PrimaryType` whose `nameInfo` field points to the `TypeNameInfo`
object representing the class `int`.
Parsing of `Type` objects is now a two-step process: the string is
first converted to a `_PreType`, and then the `_PreType` is
materialized into a `Type` by looking up each identifier in it in the
`TypeRegistry`.
This will be needed in a follow-up CL that introduces support for
generic function types, to support the possiblity that a generic
function type's return type refers to one of its type parameters
(e.g. `List<T> Function<T>()`). The reason this is will be needed is
because the meaning of the return type (`List<T>`) can't be determined
until the `TypeParameter` object representing the type parameter `T`
has been created.
Change-Id: I9f4a73bdc0f38380518a9c39db8c788234adb806
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/395686
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This method will be used by a follow-up CL that adds support for
generic functions, since the implementation of `==` and `hashCode` for
generic functions needs to perform substitutions in order to recognize
that alpha-equivalent types (such as `T Function<T>()` and `U
Function<U>`) are equal, and those substitutions need to avoid name
collisions with identifier names that already appear in the type.
Change-Id: Ifd237a9842791f440e285d89e9d80b898e996411
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/395685
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>