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';
/// 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.
class CaseHeadInfo<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;
class CaseHeadOrDefaultInfo<Node extends Object, Expression extends Node> {
/// For a `case` clause, the case pattern. For a `default` clause, `null`.
final Node? pattern;
@ -23,36 +19,34 @@ class CaseHeadInfo<Node extends Object, Expression extends Node> {
/// `when`. Otherwise `null`.
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]
/// about an individual `case` or `default` clause.
///
/// The client is free to `implement` or `extend` this class.
class ExpressionCaseInfo<Node extends Object, Expression extends Node>
extends CaseHeadInfo<Node, Expression> {
/// The body of the `case` or `default` clause.
final Expression body;
class SwitchExpressionMemberInfo<Node extends Object, Expression extends Node> {
/// The [CaseOrDefaultHead] associated with this clause.
final CaseHeadOrDefaultInfo<Node, Expression> head;
ExpressionCaseInfo(
{required super.node,
required super.pattern,
super.guard,
required this.body});
/// The body of the `case` or `default` clause.
final Expression expression;
SwitchExpressionMemberInfo({required this.head, required this.expression});
}
/// Information supplied by the client to [TypeAnalyzer.analyzeSwitchStatement]
/// about an individual `case` or `default` clause.
///
/// 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> {
/// The list of case heads for this case.
///
/// 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.
final List<CaseHeadInfo<Node, Expression>> heads;
final List<CaseHeadOrDefaultInfo<Node, Expression>> heads;
/// The labels preceding this `case` or `default` clause, if any.
final List<Node> labels;
@ -63,7 +57,7 @@ class StatementCaseInfo<Node extends Object, Statement extends Node,
/// that follows.
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
@ -320,11 +314,11 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
Type? lubType;
for (int i = 0; i < numCases; i++) {
// Stack: (Expression, i * ExpressionCase)
ExpressionCaseInfo<Node, Expression> caseInfo =
getExpressionCaseInfo(node, i);
SwitchExpressionMemberInfo<Node, Expression> memberInfo =
getSwitchExpressionMemberInfo(node, i);
flow?.switchStatement_beginCase();
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos = {};
Node? pattern = caseInfo.pattern;
Node? pattern = memberInfo.head.pattern;
if (pattern != null) {
dispatchPattern(pattern).match(
expressionType,
@ -334,7 +328,7 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
switchScrutinee: scrutinee,
topPattern: pattern));
// Stack: (Expression, i * ExpressionCase, Pattern)
Expression? guard = caseInfo.guard;
Expression? guard = memberInfo.head.guard;
bool hasGuard = guard != null;
if (hasGuard) {
_checkGuardType(guard, analyzeExpression(guard, boolType));
@ -349,7 +343,7 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
handleDefault(node, i);
}
// Stack: (Expression, i * ExpressionCase, CaseHead)
Type type = analyzeExpression(caseInfo.body, context);
Type type = analyzeExpression(memberInfo.expression, context);
// Stack: (Expression, i * ExpressionCase, CaseHead, Expression)
if (lubType == null) {
lubType = type;
@ -391,14 +385,14 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
while (i < numCases) {
// Stack: (Expression, numExecutionPaths * StatementCase,
// numHeads * CaseHead)
StatementCaseInfo<Node, Statement, Expression> caseInfo =
getStatementCaseInfo(node, i);
if (caseInfo.labels.isNotEmpty) {
SwitchStatementMemberInfo<Node, Statement, Expression> memberInfo =
getSwitchStatementMemberInfo(node, i);
if (memberInfo.labels.isNotEmpty) {
hasLabels = true;
}
List<CaseHeadInfo<Node, Expression>> heads = caseInfo.heads;
List<CaseHeadOrDefaultInfo<Node, Expression>> heads = memberInfo.heads;
for (int j = 0; j < heads.length; j++) {
CaseHeadInfo<Node, Expression> head = heads[j];
CaseHeadOrDefaultInfo<Node, Expression> head = heads[j];
Node? pattern = head.pattern;
if (pattern != null) {
dispatchPattern(pattern).match(
@ -429,7 +423,7 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
// Stack: (Expression, numExecutionPaths * StatementCase,
// numHeads * CaseHead),
flow?.switchStatement_endAlternative();
body = caseInfo.body;
body = memberInfo.body;
}
i++;
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.
///
/// See [analyzeSwitchExpression].
ExpressionCaseInfo<Node, Expression> getExpressionCaseInfo(
SwitchExpressionMemberInfo<Node, Expression> getSwitchExpressionMemberInfo(
Expression node, int index);
/// 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.
///
/// See [analyzeSwitchStatement].
StatementCaseInfo<Node, Statement, Expression> getStatementCaseInfo(
Statement node, int caseIndex);
SwitchStatementMemberInfo<Node, Statement, Expression>
getSwitchStatementMemberInfo(Statement node, int caseIndex);
/// 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
/// [caseExpr] to create instances of this class.
class ExpressionCase extends Node
implements ExpressionCaseInfo<Node, Expression> {
implements
SwitchExpressionMemberInfo<Node, Expression>,
CaseHeadOrDefaultInfo<Node, Expression> {
@override
final Pattern? pattern;
@ -407,26 +409,26 @@ class ExpressionCase extends Node
final Expression? guard;
@override
final Expression body;
final Expression expression;
ExpressionCase._(this.pattern, this.guard, this.body,
ExpressionCase._(this.pattern, this.guard, this.expression,
{required super.location})
: super._();
@override
Node get node => this;
CaseHeadOrDefaultInfo<Node, Expression> get head => this;
String toString() => [
pattern == null ? 'default' : 'case $pattern',
if (guard != null) ' when $guard',
': $body'
': $expression'
].join('');
void _preVisit(PreVisitor visitor) {
var variableBinder = VariableBinder<Node, Var, Type>(visitor);
pattern?.preVisit(visitor, variableBinder);
variableBinder.finish();
body.preVisit(visitor);
expression.preVisit(visitor);
}
}
@ -2447,18 +2449,19 @@ class _MiniAstTypeAnalyzer
}
@override
ExpressionCaseInfo<Node, Expression> getExpressionCaseInfo(
SwitchExpressionMemberInfo<Node, Expression> getSwitchExpressionMemberInfo(
covariant _SwitchExpression node, int index) =>
node.cases[index];
@override
StatementCaseInfo<Node, Statement, Expression> getStatementCaseInfo(
SwitchStatementMemberInfo<Node, Statement, Expression>
getSwitchStatementMemberInfo(
covariant _SwitchStatement node, int caseIndex) {
StatementCase case_ = node.cases[caseIndex];
return StatementCaseInfo([
return SwitchStatementMemberInfo([
for (var caseHead in case_._caseHeads._caseHeads)
CaseHeadInfo(
node: caseHead, pattern: caseHead._pattern, guard: caseHead._guard)
CaseHeadOrDefaultInfo(
pattern: caseHead._pattern, guard: caseHead._guard)
], case_._body.statements, labels: case_._caseHeads._labels);
}