Shared patterns analysis: make TypeAnalyzer.flow non-nullable.

This simplifies the shared TypeAnalyzer code by avoiding a lot of null
checks.

This required modifying a few analyzer code paths that didn't
previously initialize FlowAnalysis so that they now do.

Change-Id: Ie306d3ac94c4ca00d211e9cd038fb0b001996747
Bug: https://github.com/dart-lang/sdk/issues/50419
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274940
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
Paul Berry 2022-12-13 13:10:08 +00:00 committed by Commit Queue
parent ba5f8da95d
commit 87f2429051
4 changed files with 87 additions and 72 deletions

View file

@ -231,11 +231,7 @@ mixin TypeAnalyzer<
get errors;
/// Returns the client's [FlowAnalysis] object.
///
/// May be `null`, because the analyzer doesn't have a flow analysis object
/// in play when analyzing top level initializers (see
/// https://github.com/dart-lang/sdk/issues/49701).
FlowAnalysis<Node, Statement, Expression, Variable, Type>? get flow;
FlowAnalysis<Node, Statement, Expression, Variable, Type> get flow;
/// Returns the type `int`.
Type get intType;
@ -280,13 +276,13 @@ mixin TypeAnalyzer<
matchedType: matchedType,
requiredType: variableDeclaredType);
}
flow?.write(node, variable, matchedType, context.getInitializer(node));
flow.write(node, variable, matchedType, context.getInitializer(node));
}
/// Computes the type schema for a variable pattern appearing in an assignment
/// context. [variable] is the variable being referenced.
Type analyzeAssignedVariablePatternSchema(Variable variable) =>
flow?.promotedType(variable) ?? operations.variableType(variable);
flow.promotedType(variable) ?? operations.variableType(variable);
/// Analyzes a cast pattern. [innerPattern] is the sub-pattern] and [type] is
/// the type to cast to.
@ -328,7 +324,7 @@ mixin TypeAnalyzer<
errors?.refutablePatternInIrrefutableContext(node, irrefutableContext);
}
Type staticType = analyzeExpression(expression, matchedType);
flow?.constantPattern_end(expression);
flow.constantPattern_end(expression);
// Stack: (Expression)
if (errors != null && !options.patternsEnabled) {
Expression? switchScrutinee = context.getSwitchScrutinee(node);
@ -392,7 +388,7 @@ mixin TypeAnalyzer<
matchedType: matchedType,
requiredType: staticType);
}
flow?.declaredVariablePattern(
flow.declaredVariablePattern(
matchedType: matchedType, staticType: staticType);
bool isImplicitlyTyped = declaredType == null;
if (variable != null) {
@ -400,10 +396,10 @@ mixin TypeAnalyzer<
throw new StateError(
'When the variable is not null, the name must also be not null');
}
flow?.declare(variable, false);
flow.declare(variable, false);
setVariableType(variable, staticType);
// TODO(paulberry): are we handling _isFinal correctly?
flow?.initialize(variable, matchedType, context.getInitializer(node),
flow.initialize(variable, matchedType, context.getInitializer(node),
isFinal: context.isFinal || isVariableFinal(variable),
isLate: context.isLate,
isImplicitlyTyped: isImplicitlyTyped);
@ -433,7 +429,7 @@ mixin TypeAnalyzer<
dispatchExpression(node, context);
// Stack: (Expression)
if (operations.isNever(result.provisionalType)) {
flow?.handleExit();
flow.handleExit();
}
return result.resolveShorting();
}
@ -461,9 +457,9 @@ mixin TypeAnalyzer<
required Object? context,
}) {
// Stack: ()
flow?.ifCaseStatement_begin();
flow.ifCaseStatement_begin();
Type initializerType = analyzeExpression(expression, unknownType);
flow?.ifCaseStatement_afterExpression(expression);
flow.ifCaseStatement_afterExpression(expression);
// Stack: (Expression)
// TODO(paulberry): rework handling of isFinal
dispatchPattern(
@ -478,7 +474,7 @@ mixin TypeAnalyzer<
handleNoGuard(node, 0);
}
// Stack: (Expression, Pattern, Guard)
flow?.ifCaseStatement_thenBegin(guard);
flow.ifCaseStatement_thenBegin(guard);
_analyzeIfElementCommon(node, ifTrue, ifFalse, context);
}
@ -506,9 +502,9 @@ mixin TypeAnalyzer<
Map<String, Variable> variables,
) {
// Stack: ()
flow?.ifCaseStatement_begin();
flow.ifCaseStatement_begin();
Type initializerType = analyzeExpression(expression, unknownType);
flow?.ifCaseStatement_afterExpression(expression);
flow.ifCaseStatement_afterExpression(expression);
// Stack: (Expression)
// TODO(paulberry): rework handling of isFinal
dispatchPattern(
@ -526,7 +522,7 @@ mixin TypeAnalyzer<
);
for (Variable variable in variables.values) {
flow?.declare(variable, true);
flow.declare(variable, true);
}
handle_ifCaseStatement_afterPattern(
@ -540,7 +536,7 @@ mixin TypeAnalyzer<
handleNoGuard(node, 0);
}
// Stack: (Expression, Pattern, Guard)
flow?.ifCaseStatement_thenBegin(guard);
flow.ifCaseStatement_thenBegin(guard);
_analyzeIfCommon(node, ifTrue, ifFalse);
return initializerType;
}
@ -564,11 +560,11 @@ mixin TypeAnalyzer<
required Object? context,
}) {
// Stack: ()
flow?.ifStatement_conditionBegin();
flow.ifStatement_conditionBegin();
analyzeExpression(condition, boolType);
handle_ifElement_conditionEnd(node);
// Stack: (Expression condition)
flow?.ifStatement_thenBegin(condition, node);
flow.ifStatement_thenBegin(condition, node);
_analyzeIfElementCommon(node, ifTrue, ifFalse, context);
}
@ -585,11 +581,11 @@ mixin TypeAnalyzer<
void analyzeIfStatement(Statement node, Expression condition,
Statement ifTrue, Statement? ifFalse) {
// Stack: ()
flow?.ifStatement_conditionBegin();
flow.ifStatement_conditionBegin();
analyzeExpression(condition, boolType);
handle_ifStatement_conditionEnd(node);
// Stack: (Expression condition)
flow?.ifStatement_thenBegin(condition, node);
flow.ifStatement_thenBegin(condition, node);
_analyzeIfCommon(node, ifTrue, ifFalse);
}
@ -984,7 +980,7 @@ mixin TypeAnalyzer<
// Stack: ()
Type rhsType = analyzeExpression(rhs, dispatchPatternSchema(pattern));
// Stack: (Expression)
flow?.patternAssignment_afterRhs(rhs);
flow.patternAssignment_afterRhs(rhs);
dispatchPattern(
rhsType,
new MatchContext<Node, Expression, Pattern, Type, Variable>(
@ -995,7 +991,7 @@ mixin TypeAnalyzer<
),
pattern,
);
flow?.patternAssignment_end();
flow.patternAssignment_end();
// Stack: (Expression, Pattern)
return new SimpleTypeAnalysisResult<Type>(type: rhsType);
}
@ -1021,15 +1017,15 @@ mixin TypeAnalyzer<
errors?.patternDoesNotAllowLate(pattern);
}
if (isLate) {
flow?.lateInitializer_begin(node);
flow.lateInitializer_begin(node);
}
Type initializerType =
analyzeExpression(initializer, dispatchPatternSchema(pattern));
// Stack: (Expression)
if (isLate) {
flow?.lateInitializer_end();
flow.lateInitializer_end();
}
flow?.patternVariableDeclaration_afterInitializer(initializer);
flow.patternVariableDeclaration_afterInitializer(initializer);
dispatchPattern(
initializerType,
new MatchContext<Node, Expression, Pattern, Type, Variable>(
@ -1041,7 +1037,7 @@ mixin TypeAnalyzer<
),
pattern,
);
flow?.patternVariableDeclaration_end();
flow.patternVariableDeclaration_end();
// Stack: (Expression, Pattern)
}
@ -1209,14 +1205,14 @@ mixin TypeAnalyzer<
Type expressionType = analyzeExpression(scrutinee, unknownType);
// Stack: (Expression)
handleSwitchScrutinee(expressionType);
flow?.switchStatement_expressionEnd(null, scrutinee);
flow.switchStatement_expressionEnd(null, scrutinee);
Type? lubType;
for (int i = 0; i < numCases; i++) {
// Stack: (Expression, i * ExpressionCase)
SwitchExpressionMemberInfo<Node, Expression, Variable> memberInfo =
getSwitchExpressionMemberInfo(node, i);
flow?.switchStatement_beginAlternatives();
flow?.switchStatement_beginAlternative();
flow.switchStatement_beginAlternatives();
flow.switchStatement_beginAlternative();
Node? pattern = memberInfo.head.pattern;
Expression? guard;
if (pattern != null) {
@ -1243,11 +1239,11 @@ mixin TypeAnalyzer<
} else {
handleDefault(node, i);
}
flow?.switchStatement_endAlternative(guard);
flow?.switchStatement_endAlternatives(null, hasLabels: false);
flow.switchStatement_endAlternative(guard);
flow.switchStatement_endAlternatives(null, hasLabels: false);
// Stack: (Expression, i * ExpressionCase, CaseHead)
Type type = analyzeExpression(memberInfo.expression, context);
flow?.switchStatement_afterCase();
flow.switchStatement_afterCase();
// Stack: (Expression, i * ExpressionCase, CaseHead, Expression)
if (lubType == null) {
lubType = type;
@ -1258,7 +1254,7 @@ mixin TypeAnalyzer<
// Stack: (Expression, (i + 1) * ExpressionCase)
}
// Stack: (Expression, numCases * ExpressionCase)
flow?.switchStatement_end(true);
flow.switchStatement_end(true);
return new SimpleTypeAnalysisResult<Type>(type: lubType!);
}
@ -1272,12 +1268,12 @@ mixin TypeAnalyzer<
Type scrutineeType = analyzeExpression(scrutinee, unknownType);
// Stack: (Expression)
handleSwitchScrutinee(scrutineeType);
flow?.switchStatement_expressionEnd(node, scrutinee);
flow.switchStatement_expressionEnd(node, scrutinee);
bool hasDefault = false;
bool lastCaseTerminates = true;
for (int caseIndex = 0; caseIndex < numCases; caseIndex++) {
// Stack: (Expression, numExecutionPaths * StatementCase)
flow?.switchStatement_beginAlternatives();
flow.switchStatement_beginAlternatives();
// Stack: (Expression, numExecutionPaths * StatementCase,
// numHeads * CaseHead)
SwitchStatementMemberInfo<Node, Statement, Expression, Variable>
@ -1288,7 +1284,7 @@ mixin TypeAnalyzer<
CaseHeadOrDefaultInfo<Node, Expression, Variable> head =
heads[headIndex];
Node? pattern = head.pattern;
flow?.switchStatement_beginAlternative();
flow.switchStatement_beginAlternative();
Expression? guard;
if (pattern != null) {
dispatchPattern(
@ -1321,11 +1317,11 @@ mixin TypeAnalyzer<
}
// Stack: (Expression, numExecutionPaths * StatementCase,
// numHeads * CaseHead),
flow?.switchStatement_endAlternative(guard);
flow.switchStatement_endAlternative(guard);
}
// Stack: (Expression, numExecutionPaths * StatementCase,
// numHeads * CaseHead)
flow?.switchStatement_endAlternatives(node,
flow.switchStatement_endAlternatives(node,
hasLabels: memberInfo.hasLabels);
Map<String, Variable> variables = memberInfo.variables;
_finishJoinedVariables(variables, reportErrors: false);
@ -1334,7 +1330,7 @@ mixin TypeAnalyzer<
// If there are joined variables, declare them.
if (heads.length > 1 || memberInfo.hasLabels) {
for (Variable variable in variables.values) {
flow?.declare(variable, true);
flow.declare(variable, true);
}
}
for (Statement statement in memberInfo.body) {
@ -1342,14 +1338,14 @@ mixin TypeAnalyzer<
}
// Stack: (Expression, numExecutionPaths * StatementCase, CaseHeads,
// n * Statement), where n = body.length
lastCaseTerminates = flow == null || !flow!.isReachable;
lastCaseTerminates = !flow.isReachable;
if (caseIndex < numCases - 1 &&
options.nullSafetyEnabled &&
!options.patternsEnabled &&
!lastCaseTerminates) {
errors?.switchCaseCompletesNormally(node, caseIndex, 1);
}
flow?.switchStatement_afterCase();
flow.switchStatement_afterCase();
handleMergedStatementCase(node,
caseIndex: caseIndex, isTerminating: lastCaseTerminates);
// Stack: (Expression, (numExecutionPaths + 1) * StatementCase)
@ -1363,7 +1359,7 @@ mixin TypeAnalyzer<
} else {
isExhaustive = isLegacySwitchExhaustive(node, scrutineeType);
}
flow?.switchStatement_end(isExhaustive);
flow.switchStatement_end(isExhaustive);
return new SwitchStatementTypeAnalysisResult<Type>(
hasDefault: hasDefault,
isExhaustive: isExhaustive,
@ -1387,7 +1383,7 @@ mixin TypeAnalyzer<
Node node, Variable variable, Type? declaredType,
{required bool isFinal, required bool isLate}) {
Type inferredType = declaredType ?? dynamicType;
flow?.declare(variable, false);
flow.declare(variable, false);
setVariableType(variable, inferredType);
return inferredType;
}
@ -1686,11 +1682,11 @@ mixin TypeAnalyzer<
// Stack: (Statement ifTrue)
if (ifFalse == null) {
handleNoStatement(node);
flow?.ifStatement_end(false);
flow.ifStatement_end(false);
} else {
flow?.ifStatement_elseBegin();
flow.ifStatement_elseBegin();
dispatchStatement(ifFalse);
flow?.ifStatement_end(true);
flow.ifStatement_end(true);
handle_ifStatement_elseEnd(node, ifFalse);
}
// Stack: (Statement ifTrue, Statement ifFalse)
@ -1709,11 +1705,11 @@ mixin TypeAnalyzer<
// Stack: (CollectionElement ifTrue)
if (ifFalse == null) {
handleNoCollectionElement(node);
flow?.ifStatement_end(false);
flow.ifStatement_end(false);
} else {
flow?.ifStatement_elseBegin();
flow.ifStatement_elseBegin();
dispatchCollectionElement(ifFalse, context);
flow?.ifStatement_end(true);
flow.ifStatement_end(true);
handle_ifElement_elseEnd(node, ifFalse);
}
// Stack: (CollectionElement ifTrue, CollectionElement ifFalse)

View file

@ -664,7 +664,8 @@ class _AssignedVariablesVisitor extends RecursiveAstVisitor<void> {
if (element is PromotableElement &&
node.inGetterContext() &&
node.parent is! FormalParameter &&
node.parent is! CatchClause) {
node.parent is! CatchClause &&
node.parent is! CommentReference) {
assignedVariables.read(element);
}
}

View file

@ -458,8 +458,8 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
ExecutableElement? get enclosingFunction => _enclosingFunction;
@override
FlowAnalysis<AstNode, Statement, Expression, PromotableElement, DartType>?
get flow => flowAnalysis.flow;
FlowAnalysis<AstNode, Statement, Expression, PromotableElement, DartType>
get flow => flowAnalysis.flow!;
@override
DartType get intType => throw UnimplementedError('TODO(paulberry)');
@ -1369,22 +1369,20 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
if (element.isFinal) {
final flow = this.flow;
if (flow != null) {
if (element.isLate) {
if (flow.isAssigned(element)) {
errorReporter.reportErrorForToken(
CompileTimeErrorCode.LATE_FINAL_LOCAL_ALREADY_ASSIGNED,
node.name,
);
}
} else {
if (!flow.isUnassigned(element)) {
errorReporter.reportErrorForToken(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL,
node.name,
[node.name.lexeme],
);
}
if (element.isLate) {
if (flow.isAssigned(element)) {
errorReporter.reportErrorForToken(
CompileTimeErrorCode.LATE_FINAL_LOCAL_ALREADY_ASSIGNED,
node.name,
);
}
} else {
if (!flow.isUnassigned(element)) {
errorReporter.reportErrorForToken(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL,
node.name,
[node.name.lexeme],
);
}
}
}
@ -1765,12 +1763,22 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
@override
void visitAnnotation(covariant AnnotationImpl node) {
// Annotations can contain expressions, so we need flow analysis to be
// available to process those expressions.
var isTopLevel = flowAnalysis.flow == null;
if (isTopLevel) {
flowAnalysis.topLevelDeclaration_enter(node, null);
}
assert(flowAnalysis.flow != null);
var whyNotPromotedList = <Map<DartType, NonPromotionReason> Function()>[];
_annotationResolver.resolve(node, whyNotPromotedList);
var arguments = node.arguments;
if (arguments != null) {
checkForArgumentTypesNotAssignableInList(arguments, whyNotPromotedList);
}
if (isTopLevel) {
flowAnalysis.topLevelDeclaration_exit();
}
}
@override
@ -2433,8 +2441,18 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
@override
void visitFormalParameterList(FormalParameterList node) {
// Formal parameter lists can contain default values, which in turn contain
// expressions, so we need flow analysis to be available to process those
// expressions.
var isTopLevel = flowAnalysis.flow == null;
if (isTopLevel) {
flowAnalysis.topLevelDeclaration_enter(node, null);
}
checkUnreachableNode(node);
node.visitChildren(this);
if (isTopLevel) {
flowAnalysis.topLevelDeclaration_exit();
}
}
@override

View file

@ -8680,7 +8680,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase
}
@override
FlowAnalysis<Node, Statement, Expression, VariableDeclaration, DartType>?
FlowAnalysis<Node, Statement, Expression, VariableDeclaration, DartType>
get flow => flowAnalysis;
@override