Shared type analysis for patterns: clean up switch data structures.

- `CaseHeadInfo` is renamed to `CaseHeadOrDefaultInfo`, to reflect the
  fact that it is used for both case heads and default clauses.

- `CaseHeadOrDefaultInfo.node` is no longer needed; this used to be
  used for error reporting, but after the refactor of
  https://dart-review.googlesource.com/c/sdk/+/259021 is was no longer
  used.

- `ExpressionCaseInfo` is renamed to `SwitchExpressionMemberInfo`,
  consistent with the AST structure `SwitchExpressionMember` in the
  analyzer.

- `SwitchExpressionMemberInfo.body` is renamed to
  `SwitchExpressionMemberInfo.expression`, consistent with the
  nomenclature used in the analyzer.

- `StatementCaseInfo` is renamed to `SwitchStatementMemberInfo` for
  consistency with `SwitchExpressionMemberInfo`.  Note that the
  analyzer calls its corresponding AST structure `SwitchMember` rather
  than `SwitchStatementMember` for legacy reasons.

- `SwitchExpressionMemberInfo` no longer extends
  `CaseHeadOrDefaultInfo`; it contains a pointer to the head or
  default info.  This is more consistent with
  `SwitchStatementMemberInfo`.

Change-Id: I727766a6f0601ec5cd8aff824364319a54446bd2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/259880
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
Paul Berry 2022-09-20 14:03:35 +00:00 committed by Commit Bot
parent 83ab5d5ca3
commit 84b71e55d4
2 changed files with 43 additions and 46 deletions

View file

@ -7,15 +7,11 @@ import 'type_analysis_result.dart';
import 'type_operations.dart'; import 'type_operations.dart';
/// Information supplied by the client to [TypeAnalyzer.analyzeSwitchExpression] /// Information supplied by the client to [TypeAnalyzer.analyzeSwitchExpression]
/// or [TypeAnalyzer.analyzeSwitchStatement] about a single case head. /// or [TypeAnalyzer.analyzeSwitchStatement] about a single case head or
/// `default` clause.
/// ///
/// The client is free to `implement` or `extend` this class. /// The client is free to `implement` or `extend` this class.
class CaseHeadInfo<Node extends Object, Expression extends Node> { class CaseHeadOrDefaultInfo<Node extends Object, Expression extends Node> {
/// The AST node for this `case` or `default` clause. This is used for error
/// reporting, in case errors arise from mismatch among the variables bound by
/// various cases that share a body.
final Node node;
/// For a `case` clause, the case pattern. For a `default` clause, `null`. /// For a `case` clause, the case pattern. For a `default` clause, `null`.
final Node? pattern; final Node? pattern;
@ -23,36 +19,34 @@ class CaseHeadInfo<Node extends Object, Expression extends Node> {
/// `when`. Otherwise `null`. /// `when`. Otherwise `null`.
final Expression? guard; final Expression? guard;
CaseHeadInfo({required this.node, required this.pattern, this.guard}); CaseHeadOrDefaultInfo({required this.pattern, this.guard});
} }
/// Information supplied by the client to [TypeAnalyzer.analyzeSwitchExpression] /// Information supplied by the client to [TypeAnalyzer.analyzeSwitchExpression]
/// about an individual `case` or `default` clause. /// about an individual `case` or `default` clause.
/// ///
/// The client is free to `implement` or `extend` this class. /// The client is free to `implement` or `extend` this class.
class ExpressionCaseInfo<Node extends Object, Expression extends Node> class SwitchExpressionMemberInfo<Node extends Object, Expression extends Node> {
extends CaseHeadInfo<Node, Expression> { /// The [CaseOrDefaultHead] associated with this clause.
/// The body of the `case` or `default` clause. final CaseHeadOrDefaultInfo<Node, Expression> head;
final Expression body;
ExpressionCaseInfo( /// The body of the `case` or `default` clause.
{required super.node, final Expression expression;
required super.pattern,
super.guard, SwitchExpressionMemberInfo({required this.head, required this.expression});
required this.body});
} }
/// Information supplied by the client to [TypeAnalyzer.analyzeSwitchStatement] /// Information supplied by the client to [TypeAnalyzer.analyzeSwitchStatement]
/// about an individual `case` or `default` clause. /// about an individual `case` or `default` clause.
/// ///
/// The client is free to `implement` or `extend` this class. /// The client is free to `implement` or `extend` this class.
class StatementCaseInfo<Node extends Object, Statement extends Node, class SwitchStatementMemberInfo<Node extends Object, Statement extends Node,
Expression extends Node> { Expression extends Node> {
/// The list of case heads for this case. /// The list of case heads for this case.
/// ///
/// The reason this is a list rather than a single head is because the front /// The reason this is a list rather than a single head is because the front
/// end merges together cases that share a body at parse time. /// end merges together cases that share a body at parse time.
final List<CaseHeadInfo<Node, Expression>> heads; final List<CaseHeadOrDefaultInfo<Node, Expression>> heads;
/// The labels preceding this `case` or `default` clause, if any. /// The labels preceding this `case` or `default` clause, if any.
final List<Node> labels; final List<Node> labels;
@ -63,7 +57,7 @@ class StatementCaseInfo<Node extends Object, Statement extends Node,
/// that follows. /// that follows.
final List<Statement> body; final List<Statement> body;
StatementCaseInfo(this.heads, this.body, {this.labels = const []}); SwitchStatementMemberInfo(this.heads, this.body, {this.labels = const []});
} }
/// Type analysis logic to be shared between the analyzer and front end. The /// Type analysis logic to be shared between the analyzer and front end. The
@ -320,11 +314,11 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
Type? lubType; Type? lubType;
for (int i = 0; i < numCases; i++) { for (int i = 0; i < numCases; i++) {
// Stack: (Expression, i * ExpressionCase) // Stack: (Expression, i * ExpressionCase)
ExpressionCaseInfo<Node, Expression> caseInfo = SwitchExpressionMemberInfo<Node, Expression> memberInfo =
getExpressionCaseInfo(node, i); getSwitchExpressionMemberInfo(node, i);
flow?.switchStatement_beginCase(); flow?.switchStatement_beginCase();
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos = {}; Map<Variable, VariableTypeInfo<Node, Type>> typeInfos = {};
Node? pattern = caseInfo.pattern; Node? pattern = memberInfo.head.pattern;
if (pattern != null) { if (pattern != null) {
dispatchPattern(pattern).match( dispatchPattern(pattern).match(
expressionType, expressionType,
@ -334,7 +328,7 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
switchScrutinee: scrutinee, switchScrutinee: scrutinee,
topPattern: pattern)); topPattern: pattern));
// Stack: (Expression, i * ExpressionCase, Pattern) // Stack: (Expression, i * ExpressionCase, Pattern)
Expression? guard = caseInfo.guard; Expression? guard = memberInfo.head.guard;
bool hasGuard = guard != null; bool hasGuard = guard != null;
if (hasGuard) { if (hasGuard) {
_checkGuardType(guard, analyzeExpression(guard, boolType)); _checkGuardType(guard, analyzeExpression(guard, boolType));
@ -349,7 +343,7 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
handleDefault(node, i); handleDefault(node, i);
} }
// Stack: (Expression, i * ExpressionCase, CaseHead) // Stack: (Expression, i * ExpressionCase, CaseHead)
Type type = analyzeExpression(caseInfo.body, context); Type type = analyzeExpression(memberInfo.expression, context);
// Stack: (Expression, i * ExpressionCase, CaseHead, Expression) // Stack: (Expression, i * ExpressionCase, CaseHead, Expression)
if (lubType == null) { if (lubType == null) {
lubType = type; lubType = type;
@ -391,14 +385,14 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
while (i < numCases) { while (i < numCases) {
// Stack: (Expression, numExecutionPaths * StatementCase, // Stack: (Expression, numExecutionPaths * StatementCase,
// numHeads * CaseHead) // numHeads * CaseHead)
StatementCaseInfo<Node, Statement, Expression> caseInfo = SwitchStatementMemberInfo<Node, Statement, Expression> memberInfo =
getStatementCaseInfo(node, i); getSwitchStatementMemberInfo(node, i);
if (caseInfo.labels.isNotEmpty) { if (memberInfo.labels.isNotEmpty) {
hasLabels = true; hasLabels = true;
} }
List<CaseHeadInfo<Node, Expression>> heads = caseInfo.heads; List<CaseHeadOrDefaultInfo<Node, Expression>> heads = memberInfo.heads;
for (int j = 0; j < heads.length; j++) { for (int j = 0; j < heads.length; j++) {
CaseHeadInfo<Node, Expression> head = heads[j]; CaseHeadOrDefaultInfo<Node, Expression> head = heads[j];
Node? pattern = head.pattern; Node? pattern = head.pattern;
if (pattern != null) { if (pattern != null) {
dispatchPattern(pattern).match( dispatchPattern(pattern).match(
@ -429,7 +423,7 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
// Stack: (Expression, numExecutionPaths * StatementCase, // Stack: (Expression, numExecutionPaths * StatementCase,
// numHeads * CaseHead), // numHeads * CaseHead),
flow?.switchStatement_endAlternative(); flow?.switchStatement_endAlternative();
body = caseInfo.body; body = memberInfo.body;
} }
i++; i++;
if (body.isNotEmpty) break; if (body.isNotEmpty) break;
@ -558,7 +552,7 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
/// simply return the [index]th `case` or `default` clause. /// simply return the [index]th `case` or `default` clause.
/// ///
/// See [analyzeSwitchExpression]. /// See [analyzeSwitchExpression].
ExpressionCaseInfo<Node, Expression> getExpressionCaseInfo( SwitchExpressionMemberInfo<Node, Expression> getSwitchExpressionMemberInfo(
Expression node, int index); Expression node, int index);
/// Returns a [StatementCaseInfo] object describing the [index]th `case` or /// Returns a [StatementCaseInfo] object describing the [index]th `case` or
@ -569,8 +563,8 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
/// simply return the [index]th `case` or `default` clause. /// simply return the [index]th `case` or `default` clause.
/// ///
/// See [analyzeSwitchStatement]. /// See [analyzeSwitchStatement].
StatementCaseInfo<Node, Statement, Expression> getStatementCaseInfo( SwitchStatementMemberInfo<Node, Statement, Expression>
Statement node, int caseIndex); getSwitchStatementMemberInfo(Statement node, int caseIndex);
/// Called after visiting a merged set of `case` / `default` clauses. /// Called after visiting a merged set of `case` / `default` clauses.
/// ///

View file

@ -399,7 +399,9 @@ abstract class Expression extends Node {
/// Representation of a single case clause in a switch expression. Use /// Representation of a single case clause in a switch expression. Use
/// [caseExpr] to create instances of this class. /// [caseExpr] to create instances of this class.
class ExpressionCase extends Node class ExpressionCase extends Node
implements ExpressionCaseInfo<Node, Expression> { implements
SwitchExpressionMemberInfo<Node, Expression>,
CaseHeadOrDefaultInfo<Node, Expression> {
@override @override
final Pattern? pattern; final Pattern? pattern;
@ -407,26 +409,26 @@ class ExpressionCase extends Node
final Expression? guard; final Expression? guard;
@override @override
final Expression body; final Expression expression;
ExpressionCase._(this.pattern, this.guard, this.body, ExpressionCase._(this.pattern, this.guard, this.expression,
{required super.location}) {required super.location})
: super._(); : super._();
@override @override
Node get node => this; CaseHeadOrDefaultInfo<Node, Expression> get head => this;
String toString() => [ String toString() => [
pattern == null ? 'default' : 'case $pattern', pattern == null ? 'default' : 'case $pattern',
if (guard != null) ' when $guard', if (guard != null) ' when $guard',
': $body' ': $expression'
].join(''); ].join('');
void _preVisit(PreVisitor visitor) { void _preVisit(PreVisitor visitor) {
var variableBinder = VariableBinder<Node, Var, Type>(visitor); var variableBinder = VariableBinder<Node, Var, Type>(visitor);
pattern?.preVisit(visitor, variableBinder); pattern?.preVisit(visitor, variableBinder);
variableBinder.finish(); variableBinder.finish();
body.preVisit(visitor); expression.preVisit(visitor);
} }
} }
@ -2447,18 +2449,19 @@ class _MiniAstTypeAnalyzer
} }
@override @override
ExpressionCaseInfo<Node, Expression> getExpressionCaseInfo( SwitchExpressionMemberInfo<Node, Expression> getSwitchExpressionMemberInfo(
covariant _SwitchExpression node, int index) => covariant _SwitchExpression node, int index) =>
node.cases[index]; node.cases[index];
@override @override
StatementCaseInfo<Node, Statement, Expression> getStatementCaseInfo( SwitchStatementMemberInfo<Node, Statement, Expression>
covariant _SwitchStatement node, int caseIndex) { getSwitchStatementMemberInfo(
covariant _SwitchStatement node, int caseIndex) {
StatementCase case_ = node.cases[caseIndex]; StatementCase case_ = node.cases[caseIndex];
return StatementCaseInfo([ return SwitchStatementMemberInfo([
for (var caseHead in case_._caseHeads._caseHeads) for (var caseHead in case_._caseHeads._caseHeads)
CaseHeadInfo( CaseHeadOrDefaultInfo(
node: caseHead, pattern: caseHead._pattern, guard: caseHead._guard) pattern: caseHead._pattern, guard: caseHead._guard)
], case_._body.statements, labels: case_._caseHeads._labels); ], case_._body.statements, labels: case_._caseHeads._labels);
} }