Skip to content

Commit 1041fe5

Browse files
stereotype441Commit Queue
authored andcommitted
Document flow analysis "expression info" and "expression reference" logic.
This CL adds documentation to the flow analysis methods `_getExpressionInfo`, `_getExpressionReference`, `_setExpressionInfo`, and `_setExpressionReference`, which allow flow analysis to keep track of information between API calls. It also documents the assumptions flow analysis makes about the order in which the client traverses the AST. Note that these assumptions are not always satisfied (see #56887). Change-Id: I66fd9a75f1491a8b926281d4e3b90e90d857c89a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/389590 Reviewed-by: Johnni Winther <[email protected]> Reviewed-by: Kallen Tu <[email protected]> Commit-Queue: Paul Berry <[email protected]>
1 parent e94acd3 commit 1041fe5

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed

pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,55 @@ class ExpressionPropertyTarget<Expression extends Object>
138138
/// The client should create one instance of this class for every method, field,
139139
/// or top level variable to be analyzed, and call the appropriate methods
140140
/// while visiting the code for type inference.
141+
///
142+
/// The API for flow analysis is event-based, consisting of methods that are
143+
/// intended to be called during a single-pass depth-first pre-order* traversal
144+
/// of the AST of the code being analyzed. The client only needs to make calls
145+
/// into flow analysis when this traversal visits "flow-relevant" AST nodes
146+
/// (i.e. statements and expressions that influence flow control, such as loops,
147+
/// return statements, etc., expressions that reference something potentially
148+
/// promotable, such as a variable and property gets, and anything that performs
149+
/// a type test). Other AST nodes (known as "flow-irrelevant" AST nodes) don't
150+
/// require calls to the flow analysis API on their own, but calls to flow
151+
/// analysis may still be required when visiting their children.
152+
///
153+
/// *Where child nodes are ordered according to when they first execute. Note
154+
/// that for most constructs this matches the order in which the nodes appear in
155+
/// the source text, but there are a small number of exceptions. For example, in
156+
/// `for (INITIALIZERS; CONDITION; UPDATERS) BODY;`, `UPDATERS` is executed
157+
/// after `BODY`, so `UPDATERS` should be visited after `BODY`. Also, in
158+
/// `PATTERN = EXPRESSION;`, `PATTERN` is executed after `EXPRESSION`, so
159+
/// `PATTERN` should be visited after `EXPRESSION`.
160+
///
161+
/// With a few exceptions, the methods in this class are named after a kind of
162+
/// AST node, followed by an underscore, followed by a brief phrase indicating
163+
/// when the method should be called during the visit of that kind of AST node.
164+
/// For example, when visiting an `if` statement, the client should call
165+
/// [ifStatement_thenBegin] after visiting its condition expression but before
166+
/// visiting its "then" block. The precise order for visiting any given AST node
167+
/// is described in comments below.
168+
///
169+
/// Some API calls have arguments representing either the AST node being visited
170+
/// or one of its child nodes. For example, [isExpression_end] has an argument
171+
/// `isExpression` representing the entire "is" expression, and
172+
/// [ifStatement_thenBegin] has an argument `condition` representing the
173+
/// "condition" part of the "if" statement.
174+
///
175+
/// Among other things, these arguments allow flow analysis to recognize
176+
/// parent/child relationships between parts of the syntax tree. For example,
177+
/// when analyzing `if (x is T)`, the AST node for `x is T` is passed first to
178+
/// [isExpression_end]'s `isExpression` argument and then, immediately
179+
/// afterwards, to [ifStatement_thenBegin]'s `condition` argument; this tells
180+
/// flow analysis that the "is" expression is an immediate child of the "if"
181+
/// statement, and therefore a type promotion should occur.
182+
///
183+
/// Whereas when analyzing `if (f(x is T))`, the same sequence of calls is made
184+
/// to flow analysis (since the AST node for the invocation of `f` is
185+
/// flow-irrelevant). But the node passed to [isExpression_end]'s `isExpression`
186+
/// argument is `x is T`, whereas the node passed to [ifStatement_thenBegin]'s
187+
/// `condition` argument is `f(x is T)`. Since these nodes are different, flow
188+
/// analysis knows that the "is" expression is *not* an immediate child of the
189+
/// "if" statement, so therefore no type promotion should occur.
141190
abstract class FlowAnalysis<Node extends Object, Statement extends Node,
142191
Expression extends Node, Variable extends Object, Type extends Object> {
143192
factory FlowAnalysis(
@@ -457,6 +506,9 @@ abstract class FlowAnalysis<Node extends Object, Statement extends Node,
457506
/// Call this method to forward information on [oldExpression] to
458507
/// [newExpression].
459508
///
509+
/// This method must be called immediately after visiting the expression, and
510+
/// before continuing to visit its parent.
511+
///
460512
/// This can be used to preserve promotions through a replacement from
461513
/// [oldExpression] to [newExpression]. For instance when rewriting
462514
///
@@ -4285,18 +4337,57 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
42854337
/// The most recently visited expression for which an [ExpressionInfo] object
42864338
/// exists, or `null` if no expression has been visited that has a
42874339
/// corresponding [ExpressionInfo] object.
4340+
///
4341+
/// This field, along with [_expressionInfo], establishes a mechanism to allow
4342+
/// a flow analysis method that's handling a given AST node to retrieve an
4343+
/// [ExpressionInfo] that was previously created during the handling of one of
4344+
/// that node's children. The mechanism works as follows:
4345+
///
4346+
/// While visiting the child, [_storeExpressionInfo] is called (passing in the
4347+
/// child node and the [ExpressionInfo]). It stores the child node in
4348+
/// [_expressionWithInfo] and the info in [_expressionInfo].
4349+
///
4350+
/// While visiting the parent, [_getExpressionInfo] is called (passing in the
4351+
/// child node). It checks whether [_expressionWithInfo] matches the child
4352+
/// node; if it does match, that means there are no intervening
4353+
/// flow-irrelevant nodes, and so it returns [_expressionInfo]. If it doesn't
4354+
/// match, that means that some other flow-irrelevant was visited since the
4355+
/// last time [_storeExpressionInfo] was called, and so the info in
4356+
/// [_expressionInfo] is no longer relevant, and so it returns `null`.
4357+
///
4358+
/// Note that if [_storeExpressionInfo] is called once for expression `e1` and
4359+
/// then again for expression `e2`, the second call will overwrite the info
4360+
/// stored by the first call. So if this is followed by a [_getExpressionInfo]
4361+
/// call for `e1`, `null` will be returned. In principle this situation should
4362+
/// never arise, since the client is expected to visit AST nodes in a
4363+
/// single-pass depth-first pre-order fashion. However, in practice, it
4364+
/// happens sometimes (see https://github.com/dart-lang/sdk/issues/56887).
42884365
Expression? _expressionWithInfo;
42894366

42904367
/// If [_expressionWithInfo] is not `null`, the [ExpressionInfo] object
42914368
/// corresponding to it. Otherwise `null`.
4369+
///
4370+
/// See [_expressionWithInfo] for a detailed explanation.
42924371
ExpressionInfo<Type>? _expressionInfo;
42934372

42944373
/// The most recently visited expression which was a reference, or `null` if
42954374
/// no such expression has been visited.
4375+
///
4376+
/// This field serves the same role as [_expressionWithInfo], except that it
4377+
/// is only updated for expressions that might refer to something promotable
4378+
/// (a get of a local variable or a property), so it is less likely to have
4379+
/// trouble if the client doesn't visit AST nodes in the proper order (see
4380+
/// https://github.com/dart-lang/sdk/issues/56887).
42964381
Expression? _expressionWithReference;
42974382

42984383
/// If [_expressionWithReference] is not `null`, the reference corresponding
42994384
/// to it. Otherwise `null`.
4385+
///
4386+
/// This field serves the same role as [_expressionInfo], except that it is
4387+
/// only updated for expressions that might refer to something promotable (a
4388+
/// get of a local variable or a property), so it is less likely to have
4389+
/// trouble if the client doesn't visit AST nodes in the proper order (see
4390+
/// https://github.com/dart-lang/sdk/issues/56887).
43004391
_Reference<Type>? _expressionReference;
43014392

43024393
final AssignedVariables<Node, Variable> _assignedVariables;
@@ -5723,6 +5814,15 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
57235814
/// be the last expression that was traversed). If there is no
57245815
/// [ExpressionInfo] associated with the [expression], then `null` is
57255816
/// returned.
5817+
///
5818+
/// See [_expressionWithInfo] for details about how this works.
5819+
///
5820+
/// To reduce GC pressure, if this method returns a non-null value, it resets
5821+
/// [_expressionInfo] to `null` as a side effect. This means that if
5822+
/// [_getExpressionInfo] is called twice for the same [expression] (without
5823+
/// an intervening call to [_storeExpressionInfo]), the second call will
5824+
/// return `null`. This should not be a problem because the client is expected
5825+
/// to visit AST nodes in a single-pass depth-first pre-order fashion.
57265826
ExpressionInfo<Type>? _getExpressionInfo(Expression? expression) {
57275827
if (identical(expression, _expressionWithInfo)) {
57285828
ExpressionInfo<Type>? expressionInfo = _expressionInfo;
@@ -6108,6 +6208,8 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
61086208
/// Associates [expression], which should be the most recently visited
61096209
/// expression, with the given [expressionInfo] object, and updates the
61106210
/// current flow model state to correspond to it.
6211+
///
6212+
/// See [_expressionWithInfo] for details about how this works.
61116213
void _storeExpressionInfo(
61126214
Expression expression, ExpressionInfo<Type> expressionInfo) {
61136215
_expressionWithInfo = expression;
@@ -6116,6 +6218,12 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
61166218

61176219
/// Associates [expression], which should be the most recently visited
61186220
/// expression, with the given [expressionReference] object.
6221+
///
6222+
/// This method serves the same role as [_storeExpressionInfo], but it only
6223+
/// handles expressions that might refer to something promotable (a get of a
6224+
/// local variable or a property), so it is less likely to have trouble if the
6225+
/// client doesn't visit AST nodes in the proper order (see
6226+
/// https://github.com/dart-lang/sdk/issues/56887).
61196227
void _storeExpressionReference(
61206228
Expression expression, _Reference<Type> expressionReference) {
61216229
_expressionWithReference = expression;
@@ -7150,6 +7258,13 @@ abstract class _PropertyTargetHelper<Expression extends Object,
71507258
/// Gets the [_Reference] associated with the [expression] (which should be
71517259
/// the last expression that was traversed). If there is no [_Reference]
71527260
/// associated with the [expression], then `null` is returned.
7261+
///
7262+
/// This method serves the same role as
7263+
/// [_FlowAnalysisImpl._getExpressionInfo], but it only handles expressions
7264+
/// that might refer to something promotable (a get of a local variable or a
7265+
/// property), so it is less likely to have trouble if the client doesn't
7266+
/// visit AST nodes in the proper order (see
7267+
/// https://github.com/dart-lang/sdk/issues/56887).
71537268
_Reference<Type>? _getExpressionReference(Expression? expression);
71547269
}
71557270

pkg/front_end/test/spell_checking_list_code.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ brevity
190190
brianwilkerson
191191
bridge
192192
bridges
193+
brief
193194
brute
194195
bs
195196
bsd
@@ -722,6 +723,7 @@ gardening
722723
garner
723724
gathers
724725
gb
726+
gc
725727
gen
726728
gendir
727729
generation
@@ -906,6 +908,7 @@ intersects
906908
interspersed
907909
interval
908910
intervals
911+
intervening
909912
intl
910913
introductory
911914
introspect
@@ -1264,6 +1267,7 @@ permit
12641267
permits
12651268
persist
12661269
pertinent
1270+
phrase
12671271
physically
12681272
pi
12691273
picking
@@ -1314,6 +1318,7 @@ preorder
13141318
prepares
13151319
preprocess
13161320
presented
1321+
pressure
13171322
presubmit
13181323
presumably
13191324
presuming

0 commit comments

Comments
 (0)