Replace CFE and analyzer legacy type promotion with a shared implementation.
This allows us to remove a substantial amount of CFE and analyzer
code.
It also fixes a minor CFE type promotion bug
(language_2/type_promotion/assignment_defeats_promotion_lhs_and_test).
TEST=standard trybots
Change-Id: Ia0c159bdb9161d73648c9eb73b92822168f28d84
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/175583
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
index a8af89d..b050ba3 100644
--- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
@@ -4794,10 +4794,6 @@
@override
void write(Node node, Variable variable, Type writtenType,
Expression? writtenExpression) {
- assert(
- _assignedVariables._anywhere._written.contains(variable),
- "Variable is written to, but was not included in "
- "_variablesWrittenAnywhere: $variable");
_writeStackForAnd.last.add(variable);
}
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 399bc43..e85f8b7 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -684,13 +684,10 @@
// Nothing for RESOLVED_UNIT9?
// Nothing for RESOLVED_UNIT10?
- FlowAnalysisHelper? flowAnalysisHelper;
- if (unit.featureSet.isEnabled(Feature.non_nullable)) {
- flowAnalysisHelper =
- FlowAnalysisHelper(_typeSystem, _testingData != null);
- _testingData?.recordFlowAnalysisDataForTesting(
- file.uri, flowAnalysisHelper.dataForTesting!);
- }
+ FlowAnalysisHelper flowAnalysisHelper = FlowAnalysisHelper(_typeSystem,
+ _testingData != null, unit.featureSet.isEnabled(Feature.non_nullable));
+ _testingData?.recordFlowAnalysisDataForTesting(
+ file.uri, flowAnalysisHelper.dataForTesting!);
unit.accept(ResolverVisitor(
_inheritance, _libraryElement, source, _typeProvider, errorListener,
diff --git a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
index c394437..2d5a144 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
@@ -703,10 +703,8 @@
// Nothing for RESOLVED_UNIT9?
// Nothing for RESOLVED_UNIT10?
- FlowAnalysisHelper? flowAnalysisHelper;
- if (unit.featureSet.isEnabled(Feature.non_nullable)) {
- flowAnalysisHelper = FlowAnalysisHelper(_typeSystem, false);
- }
+ FlowAnalysisHelper flowAnalysisHelper = FlowAnalysisHelper(
+ _typeSystem, false, unit.featureSet.isEnabled(Feature.non_nullable));
var resolverVisitor = ResolverVisitor(
_inheritance, _libraryElement, source, _typeProvider, errorListener,
diff --git a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
index 3b63d6c..b975efd 100644
--- a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
@@ -74,7 +74,7 @@
var leftType = node.writeType;
if (writeElement is VariableElement) {
leftType = _resolver.localVariableTypeProvider
- .getType(left as SimpleIdentifier);
+ .getType(left as SimpleIdentifier, isRead: false);
}
_setRhsContext(node, leftType!, operator, right);
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
index e4b016a..4e6836f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
@@ -18,20 +18,16 @@
import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';
-import 'package:analyzer/src/generated/type_promotion_manager.dart';
/// Helper for resolving [BinaryExpression]s.
class BinaryExpressionResolver {
final ResolverVisitor _resolver;
- final TypePromotionManager _promoteManager;
final TypePropertyResolver _typePropertyResolver;
final InvocationInferenceHelper _inferenceHelper;
BinaryExpressionResolver({
required ResolverVisitor resolver,
- required TypePromotionManager promoteManager,
}) : _resolver = resolver,
- _promoteManager = promoteManager,
_typePropertyResolver = resolver.typePropertyResolver,
_inferenceHelper = resolver.inferenceHelper;
@@ -176,27 +172,16 @@
left = node.leftOperand;
var leftWhyNotPromoted = _resolver.flowAnalysis?.flow?.whyNotPromoted(left);
- Map<DartType, NonPromotionReason> Function()? rightWhyNotPromoted;
- if (_resolver.flowAnalysis != null) {
- flow?.logicalBinaryOp_rightBegin(left, node, isAnd: true);
- _resolver.checkUnreachableNode(right);
+ flow?.logicalBinaryOp_rightBegin(left, node, isAnd: true);
+ _resolver.checkUnreachableNode(right);
- right.accept(_resolver);
- right = node.rightOperand;
- rightWhyNotPromoted = _resolver.flowAnalysis!.flow?.whyNotPromoted(right);
+ right.accept(_resolver);
+ right = node.rightOperand;
+ var rightWhyNotPromoted =
+ _resolver.flowAnalysis!.flow?.whyNotPromoted(right);
- _resolver.nullSafetyDeadCodeVerifier.flowEnd(right);
- flow?.logicalBinaryOp_end(node, right, isAnd: true);
- } else {
- _promoteManager.visitBinaryExpression_and_rhs(
- left,
- right,
- () {
- right.accept(_resolver);
- right = node.rightOperand;
- },
- );
- }
+ _resolver.nullSafetyDeadCodeVerifier.flowEnd(right);
+ flow?.logicalBinaryOp_end(node, right, isAnd: true);
_checkNonBoolOperand(left, '&&', whyNotPromoted: leftWhyNotPromoted);
_checkNonBoolOperand(right, '&&', whyNotPromoted: rightWhyNotPromoted);
diff --git a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
index 2478a00..b78323d 100644
--- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
@@ -39,8 +39,7 @@
/// For each top level or class level declaration, the assigned variables
/// information that was computed for it.
- final Map<Declaration,
- AssignedVariablesForTesting<AstNode, PromotableElement>>
+ final Map<AstNode, AssignedVariablesForTesting<AstNode, PromotableElement>>
assignedVariables = {};
/// For each expression that led to an error because it was not promoted, a
@@ -67,15 +66,21 @@
/// The result for post-resolution stages of analysis, for testing only.
final FlowAnalysisDataForTesting? dataForTesting;
+ final bool isNonNullableByDefault;
+
/// The current flow, when resolving a function body, or `null` otherwise.
FlowAnalysis<AstNode, Statement, Expression, PromotableElement, DartType>?
flow;
- FlowAnalysisHelper(TypeSystemImpl typeSystem, bool retainDataForTesting)
- : this._(TypeSystemTypeOperations(typeSystem),
- retainDataForTesting ? FlowAnalysisDataForTesting() : null);
+ FlowAnalysisHelper(TypeSystemImpl typeSystem, bool retainDataForTesting,
+ bool isNonNullableByDefault)
+ : this._(
+ TypeSystemTypeOperations(typeSystem),
+ retainDataForTesting ? FlowAnalysisDataForTesting() : null,
+ isNonNullableByDefault);
- FlowAnalysisHelper._(this._typeOperations, this.dataForTesting);
+ FlowAnalysisHelper._(
+ this._typeOperations, this.dataForTesting, this.isNonNullableByDefault);
LocalVariableTypeProvider get localVariableTypeProvider {
return _LocalVariableTypeProvider(this);
@@ -154,11 +159,11 @@
}
void for_bodyBegin(AstNode node, Expression? condition) {
- flow!.for_bodyBegin(node is Statement ? node : null, condition);
+ flow?.for_bodyBegin(node is Statement ? node : null, condition);
}
void for_conditionBegin(AstNode node) {
- flow!.for_conditionBegin(node);
+ flow?.for_conditionBegin(node);
}
bool isDefinitelyAssigned(
@@ -218,7 +223,7 @@
}
void topLevelDeclaration_enter(
- Declaration node, FormalParameterList? parameters, FunctionBody? body) {
+ AstNode node, FormalParameterList? parameters, FunctionBody? body) {
assert(flow == null);
assignedVariables = computeAssignedVariables(node, parameters,
retainDataForTesting: dataForTesting != null);
@@ -226,8 +231,11 @@
dataForTesting!.assignedVariables[node] = assignedVariables
as AssignedVariablesForTesting<AstNode, PromotableElement>;
}
- flow = FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
- DartType>(_typeOperations, assignedVariables!);
+ flow = isNonNullableByDefault
+ ? FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
+ DartType>(_typeOperations, assignedVariables!)
+ : FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
+ DartType>.legacy(_typeOperations, assignedVariables!);
}
void topLevelDeclaration_exit() {
@@ -267,7 +275,7 @@
/// Computes the [AssignedVariables] map for the given [node].
static AssignedVariables<AstNode, PromotableElement> computeAssignedVariables(
- Declaration node, FormalParameterList? parameters,
+ AstNode node, FormalParameterList? parameters,
{bool retainDataForTesting = false}) {
AssignedVariables<AstNode, PromotableElement> assignedVariables =
retainDataForTesting
@@ -331,13 +339,13 @@
class FlowAnalysisHelperForMigration extends FlowAnalysisHelper {
final MigrationResolutionHooks migrationResolutionHooks;
- FlowAnalysisHelperForMigration(
- TypeSystemImpl typeSystem, this.migrationResolutionHooks)
- : super(typeSystem, false);
+ FlowAnalysisHelperForMigration(TypeSystemImpl typeSystem,
+ this.migrationResolutionHooks, bool isNonNullableByDefault)
+ : super(typeSystem, false, isNonNullableByDefault);
@override
void topLevelDeclaration_enter(
- Declaration node, FormalParameterList? parameters, FunctionBody? body) {
+ AstNode node, FormalParameterList? parameters, FunctionBody? body) {
super.topLevelDeclaration_enter(node, parameters, body);
migrationResolutionHooks.setFlowAnalysis(flow);
}
@@ -672,10 +680,12 @@
_LocalVariableTypeProvider(this._manager);
@override
- DartType getType(SimpleIdentifier node) {
+ DartType getType(SimpleIdentifier node, {required bool isRead}) {
var variable = node.staticElement as VariableElement;
if (variable is PromotableElement) {
- var promotedType = _manager.flow?.variableRead(node, variable);
+ var promotedType = isRead
+ ? _manager.flow?.variableRead(node, variable)
+ : _manager.flow?.promotedType(variable);
if (promotedType != null) return promotedType;
}
return variable.type;
diff --git a/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
index 1797cfc..1c4f15f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
@@ -95,7 +95,8 @@
if (identifier != null) {
identifierElement = identifier.staticElement;
if (identifierElement is VariableElement) {
- valueType = _resolver.localVariableTypeProvider.getType(identifier);
+ valueType = _resolver.localVariableTypeProvider
+ .getType(identifier, isRead: false);
} else if (identifierElement is PropertyAccessorElement) {
var parameters = identifierElement.parameters;
if (parameters.isNotEmpty) {
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
index abb6f0a..ed02e6a 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
@@ -13,23 +13,19 @@
import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
import 'package:analyzer/src/generated/migration.dart';
import 'package:analyzer/src/generated/resolver.dart';
-import 'package:analyzer/src/generated/type_promotion_manager.dart';
import 'package:collection/collection.dart';
class FunctionExpressionResolver {
final ResolverVisitor _resolver;
final MigrationResolutionHooks? _migrationResolutionHooks;
final InvocationInferenceHelper _inferenceHelper;
- final TypePromotionManager _promoteManager;
FunctionExpressionResolver({
required ResolverVisitor resolver,
required MigrationResolutionHooks? migrationResolutionHooks,
- required TypePromotionManager promoteManager,
}) : _resolver = resolver,
_migrationResolutionHooks = migrationResolutionHooks,
- _inferenceHelper = resolver.inferenceHelper,
- _promoteManager = promoteManager;
+ _inferenceHelper = resolver.inferenceHelper;
bool get _isNonNullableByDefault => _typeSystem.isNonNullableByDefault;
@@ -39,13 +35,9 @@
var isFunctionDeclaration = node.parent is FunctionDeclaration;
var body = node.body;
- if (_resolver.flowAnalysis != null) {
- if (_resolver.flowAnalysis!.flow != null && !isFunctionDeclaration) {
- _resolver.flowAnalysis!
- .executableDeclaration_enter(node, node.parameters, true);
- }
- } else {
- _promoteManager.enterFunctionBody(body);
+ if (_resolver.flowAnalysis!.flow != null && !isFunctionDeclaration) {
+ _resolver.flowAnalysis!
+ .executableDeclaration_enter(node, node.parameters, true);
}
var contextType = InferenceContext.getContext(node);
@@ -63,19 +55,15 @@
node.visitChildren(_resolver);
_resolve2(node);
- if (_resolver.flowAnalysis != null) {
- if (_resolver.flowAnalysis!.flow != null && !isFunctionDeclaration) {
- var bodyContext = BodyInferenceContext.of(node.body);
- _resolver.checkForBodyMayCompleteNormally(
- returnType: bodyContext?.contextType,
- body: body,
- errorNode: body,
- );
- _resolver.flowAnalysis!.flow?.functionExpression_end();
- _resolver.nullSafetyDeadCodeVerifier.flowEnd(node);
- }
- } else {
- _promoteManager.exitFunctionBody();
+ if (_resolver.flowAnalysis!.flow != null && !isFunctionDeclaration) {
+ var bodyContext = BodyInferenceContext.of(node.body);
+ _resolver.checkForBodyMayCompleteNormally(
+ returnType: bodyContext?.contextType,
+ body: body,
+ errorNode: body,
+ );
+ _resolver.flowAnalysis!.flow?.functionExpression_end();
+ _resolver.nullSafetyDeadCodeVerifier.flowEnd(node);
}
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
index a6ab6e7..ad2fd4f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -544,7 +544,8 @@
}
if (element is VariableElement) {
_resolver.checkReadOfNotAssignedLocalVariable(nameNode, element);
- var targetType = _localVariableTypeProvider.getType(nameNode);
+ var targetType =
+ _localVariableTypeProvider.getType(nameNode, isRead: true);
return _rewriteAsFunctionExpressionInvocation(node, targetType);
}
// TODO(scheglov) This is a questionable distinction.
diff --git a/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart
index fb7dbb0..f1b1859 100644
--- a/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart
@@ -243,7 +243,8 @@
} else if (element is TypeParameterElement) {
staticType = _typeProvider.typeType;
} else if (element is VariableElement) {
- staticType = _resolver.localVariableTypeProvider.getType(node);
+ staticType = _resolver.localVariableTypeProvider
+ .getType(node, isRead: node.inGetterContext());
} else if (element is PrefixElement) {
var parent = node.parent;
if (parent is PrefixedIdentifier && parent.prefix == node ||
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index a6f3800..71ca8bb 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -64,7 +64,6 @@
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/static_type_analyzer.dart';
import 'package:analyzer/src/generated/this_access_tracker.dart';
-import 'package:analyzer/src/generated/type_promotion_manager.dart';
import 'package:analyzer/src/generated/variable_type_provider.dart';
import 'package:analyzer/src/util/ast_data_extractor.dart';
import 'package:meta/meta.dart';
@@ -233,9 +232,6 @@
/// Otherwise `null`.
DartType? _thisType;
- /// The object keeping track of which elements have had their types promoted.
- late final TypePromotionManager _promoteManager;
-
final FlowAnalysisHelper? flowAnalysis;
/// A comment before a function should be resolved in the context of the
@@ -314,8 +310,6 @@
super(definingLibrary, source, typeProvider as TypeProviderImpl,
errorListener,
nameScope: nameScope) {
- _promoteManager = TypePromotionManager(typeSystem);
-
var analysisOptions =
definingLibrary.context.analysisOptions as AnalysisOptionsImpl;
@@ -345,7 +339,6 @@
);
_binaryExpressionResolver = BinaryExpressionResolver(
resolver: this,
- promoteManager: _promoteManager,
);
_functionExpressionInvocationResolver =
FunctionExpressionInvocationResolver(
@@ -354,7 +347,6 @@
_functionExpressionResolver = FunctionExpressionResolver(
resolver: this,
migrationResolutionHooks: migrationResolutionHooks,
- promoteManager: _promoteManager,
);
_forResolver = ForResolver(
resolver: this,
@@ -396,7 +388,7 @@
if (flowAnalysis != null) {
return flowAnalysis!.localVariableTypeProvider;
} else {
- return _promoteManager.localVariableTypeProvider;
+ return const NonPromotingLocalVariableTypeProvider();
}
}
@@ -442,6 +434,7 @@
required FunctionBody body,
required AstNode errorNode,
}) {
+ if (!_isNonNullableByDefault) return;
if (!flowAnalysis!.flow!.isReachable) {
return;
}
@@ -630,11 +623,6 @@
// TODO(brianwilkerson) Remove this method.
}
- /// Set the enclosing function body when partial AST is resolved.
- void prepareCurrentFunctionBody(FunctionBody body) {
- _promoteManager.enterFunctionBody(body);
- }
-
/// Set information about enclosing declarations.
void prepareEnclosingDeclarations({
ClassElement? enclosingClassElement,
@@ -775,7 +763,8 @@
if (element is PropertyAccessorElement && element.isGetter) {
readType = element.returnType;
} else if (element is VariableElement) {
- readType = localVariableTypeProvider.getType(node as SimpleIdentifier);
+ readType = localVariableTypeProvider.getType(node as SimpleIdentifier,
+ isRead: true);
}
}
@@ -1199,22 +1188,12 @@
Expression thenExpression = node.thenExpression;
InferenceContext.setTypeFromNode(thenExpression, node);
- if (flowAnalysis != null) {
- if (flow != null) {
- flow.conditional_thenBegin(condition, node);
- checkUnreachableNode(thenExpression);
- }
- thenExpression.accept(this);
- nullSafetyDeadCodeVerifier.flowEnd(thenExpression);
- } else {
- _promoteManager.visitConditionalExpression_then(
- condition,
- thenExpression,
- () {
- thenExpression.accept(this);
- },
- );
+ if (flow != null) {
+ flow.conditional_thenBegin(condition, node);
+ checkUnreachableNode(thenExpression);
}
+ thenExpression.accept(this);
+ nullSafetyDeadCodeVerifier.flowEnd(thenExpression);
Expression elseExpression = node.elseExpression;
InferenceContext.setTypeFromNode(elseExpression, node);
@@ -1244,33 +1223,25 @@
var outerFunction = _enclosingFunction;
_enclosingFunction = node.declaredElement!;
- if (flowAnalysis != null) {
- flowAnalysis!.topLevelDeclaration_enter(node, node.parameters, node.body);
- flowAnalysis!.executableDeclaration_enter(node, node.parameters, false);
- } else {
- _promoteManager.enterFunctionBody(node.body);
- }
+ flowAnalysis!.topLevelDeclaration_enter(node, node.parameters, node.body);
+ flowAnalysis!.executableDeclaration_enter(node, node.parameters, false);
var returnType = _enclosingFunction!.type.returnType;
InferenceContext.setType(node.body, returnType);
super.visitConstructorDeclaration(node);
- if (flowAnalysis != null) {
- if (node.factoryKeyword != null) {
- var bodyContext = BodyInferenceContext.of(node.body);
- checkForBodyMayCompleteNormally(
- returnType: bodyContext?.contextType,
- body: node.body,
- errorNode: node,
- );
- }
- flowAnalysis!.executableDeclaration_exit(node.body, false);
- flowAnalysis!.topLevelDeclaration_exit();
- nullSafetyDeadCodeVerifier.flowEnd(node);
- } else {
- _promoteManager.exitFunctionBody();
+ if (node.factoryKeyword != null) {
+ var bodyContext = BodyInferenceContext.of(node.body);
+ checkForBodyMayCompleteNormally(
+ returnType: bodyContext?.contextType,
+ body: node.body,
+ errorNode: node,
+ );
}
+ flowAnalysis!.executableDeclaration_exit(node.body, false);
+ flowAnalysis!.topLevelDeclaration_exit();
+ nullSafetyDeadCodeVerifier.flowEnd(node);
var constructor = node.declaredElement as ConstructorElementImpl;
constructor.constantInitializers =
@@ -1487,53 +1458,45 @@
bool isLocal = node.parent is FunctionDeclarationStatement;
- if (flowAnalysis != null) {
- if (isLocal) {
- flowAnalysis!.flow!.functionExpression_begin(node);
- } else {
- flowAnalysis!.topLevelDeclaration_enter(
- node,
- node.functionExpression.parameters,
- node.functionExpression.body,
- );
- }
- flowAnalysis!.executableDeclaration_enter(
+ if (isLocal) {
+ flowAnalysis!.flow!.functionExpression_begin(node);
+ } else {
+ flowAnalysis!.topLevelDeclaration_enter(
node,
node.functionExpression.parameters,
- isLocal,
+ node.functionExpression.body,
);
- } else {
- _promoteManager.enterFunctionBody(node.functionExpression.body);
}
+ flowAnalysis!.executableDeclaration_enter(
+ node,
+ node.functionExpression.parameters,
+ isLocal,
+ );
var functionType = _enclosingFunction!.type;
InferenceContext.setType(node.functionExpression, functionType);
super.visitFunctionDeclaration(node);
- if (flowAnalysis != null) {
- // TODO(scheglov) encapsulate
- var bodyContext = BodyInferenceContext.of(
- node.functionExpression.body,
- );
- checkForBodyMayCompleteNormally(
- returnType: bodyContext?.contextType,
- body: node.functionExpression.body,
- errorNode: node.name,
- );
- flowAnalysis!.executableDeclaration_exit(
- node.functionExpression.body,
- isLocal,
- );
- if (isLocal) {
- flowAnalysis!.flow!.functionExpression_end();
- } else {
- flowAnalysis!.topLevelDeclaration_exit();
- }
- nullSafetyDeadCodeVerifier.flowEnd(node);
+ // TODO(scheglov) encapsulate
+ var bodyContext = BodyInferenceContext.of(
+ node.functionExpression.body,
+ );
+ checkForBodyMayCompleteNormally(
+ returnType: bodyContext?.contextType,
+ body: node.functionExpression.body,
+ errorNode: node.name,
+ );
+ flowAnalysis!.executableDeclaration_exit(
+ node.functionExpression.body,
+ isLocal,
+ );
+ if (isLocal) {
+ flowAnalysis!.flow!.functionExpression_end();
} else {
- _promoteManager.exitFunctionBody();
+ flowAnalysis!.topLevelDeclaration_exit();
}
+ nullSafetyDeadCodeVerifier.flowEnd(node);
_enclosingFunction = outerFunction;
}
@@ -1620,18 +1583,8 @@
whyNotPromoted: whyNotPromoted);
CollectionElement thenElement = node.thenElement;
- if (flowAnalysis != null) {
- flowAnalysis!.flow?.ifStatement_thenBegin(condition, node);
- thenElement.accept(this);
- } else {
- _promoteManager.visitIfElement_thenElement(
- condition,
- thenElement,
- () {
- thenElement.accept(this);
- },
- );
- }
+ flowAnalysis!.flow?.ifStatement_thenBegin(condition, node);
+ thenElement.accept(this);
var elseElement = node.elseElement;
if (elseElement != null) {
@@ -1661,19 +1614,9 @@
whyNotPromoted: whyNotPromoted);
Statement thenStatement = node.thenStatement;
- if (flowAnalysis != null) {
- flowAnalysis!.flow?.ifStatement_thenBegin(condition, node);
- visitStatementInScope(thenStatement);
- nullSafetyDeadCodeVerifier.flowEnd(thenStatement);
- } else {
- _promoteManager.visitIfStatement_thenStatement(
- condition,
- thenStatement,
- () {
- visitStatementInScope(thenStatement);
- },
- );
- }
+ flowAnalysis!.flow?.ifStatement_thenBegin(condition, node);
+ visitStatementInScope(thenStatement);
+ nullSafetyDeadCodeVerifier.flowEnd(thenStatement);
var elseStatement = node.elseStatement;
if (elseStatement != null) {
@@ -1770,32 +1713,24 @@
var outerFunction = _enclosingFunction;
_enclosingFunction = node.declaredElement!;
- if (flowAnalysis != null) {
- flowAnalysis!.topLevelDeclaration_enter(node, node.parameters, node.body);
- flowAnalysis!.executableDeclaration_enter(node, node.parameters, false);
- } else {
- _promoteManager.enterFunctionBody(node.body);
- }
+ flowAnalysis!.topLevelDeclaration_enter(node, node.parameters, node.body);
+ flowAnalysis!.executableDeclaration_enter(node, node.parameters, false);
DartType returnType = _enclosingFunction!.returnType;
InferenceContext.setType(node.body, returnType);
super.visitMethodDeclaration(node);
- if (flowAnalysis != null) {
- // TODO(scheglov) encapsulate
- var bodyContext = BodyInferenceContext.of(node.body);
- checkForBodyMayCompleteNormally(
- returnType: bodyContext?.contextType,
- body: node.body,
- errorNode: node.name,
- );
- flowAnalysis!.executableDeclaration_exit(node.body, false);
- flowAnalysis!.topLevelDeclaration_exit();
- nullSafetyDeadCodeVerifier.flowEnd(node);
- } else {
- _promoteManager.exitFunctionBody();
- }
+ // TODO(scheglov) encapsulate
+ var bodyContext = BodyInferenceContext.of(node.body);
+ checkForBodyMayCompleteNormally(
+ returnType: bodyContext?.contextType,
+ body: node.body,
+ errorNode: node.name,
+ );
+ flowAnalysis!.executableDeclaration_exit(node.body, false);
+ flowAnalysis!.topLevelDeclaration_exit();
+ nullSafetyDeadCodeVerifier.flowEnd(node);
_enclosingFunction = outerFunction;
}
@@ -2030,7 +1965,7 @@
super.visitSwitchCase(node);
var flow = flowAnalysis?.flow;
- if (flow != null && flow.isReachable) {
+ if (flow != null && flow.isReachable && _isNonNullableByDefault) {
var switchStatement = node.parent as SwitchStatement;
if (switchStatement.members.last != node && node.statements.isNotEmpty) {
errorReporter.reportErrorForToken(
@@ -2162,7 +2097,8 @@
if (initializer != null) {
var initializerStaticType = initializer.typeOrThrow;
if (declaredType == null) {
- if (initializerStaticType is TypeParameterType) {
+ if (_isNonNullableByDefault &&
+ initializerStaticType is TypeParameterType) {
flowAnalysis?.flow?.promote(
declaredElement as PromotableElement, initializerStaticType);
}
@@ -2482,7 +2418,7 @@
null,
true,
FlowAnalysisHelperForMigration(
- typeSystem, migrationResolutionHooks),
+ typeSystem, migrationResolutionHooks, true),
migrationResolutionHooks,
migrationResolutionHooks);
diff --git a/pkg/analyzer/lib/src/generated/type_promotion_manager.dart b/pkg/analyzer/lib/src/generated/type_promotion_manager.dart
deleted file mode 100644
index 965b30f..0000000
--- a/pkg/analyzer/lib/src/generated/type_promotion_manager.dart
+++ /dev/null
@@ -1,388 +0,0 @@
-// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/ast/token.dart';
-import 'package:analyzer/dart/ast/visitor.dart';
-import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/dart/element/type.dart';
-import 'package:analyzer/src/dart/ast/extensions.dart';
-import 'package:analyzer/src/dart/element/type_system.dart';
-import 'package:analyzer/src/generated/variable_type_provider.dart';
-
-/// Instances of the class `TypePromotionManager` manage the ability to promote
-/// types of local variables and formal parameters from their declared types
-/// based on control flow.
-class TypePromotionManager {
- final TypeSystemImpl _typeSystem;
-
- /// The current promotion scope.
- _TypePromoteScope _currentScope = _TypePromoteScope(null);
-
- final List<FunctionBody?> _functionBodyStack = [];
-
- /// Body of the function currently being analyzed, if any.
- FunctionBody? _currentFunctionBody;
-
- TypePromotionManager(this._typeSystem);
-
- LocalVariableTypeProvider get localVariableTypeProvider {
- return _LegacyLocalVariableTypeProvider(this);
- }
-
- /// Returns the elements with promoted types.
- Iterable<Element> get _promotedElements {
- return _currentScope.promotedElements;
- }
-
- void enterFunctionBody(FunctionBody body) {
- _functionBodyStack.add(_currentFunctionBody);
- _currentFunctionBody = body;
- }
-
- void exitFunctionBody() {
- if (_functionBodyStack.isEmpty) {
- assert(false, 'exitFunctionBody without a matching enterFunctionBody');
- } else {
- _currentFunctionBody = _functionBodyStack.removeLast();
- }
- }
-
- void visitBinaryExpression_and_rhs(
- Expression leftOperand, Expression rightOperand, void Function() f) {
- _enterScope();
- try {
- // Type promotion.
- _promoteTypes(leftOperand);
- _clearTypePromotionsIfPotentiallyMutatedIn(leftOperand);
- _clearTypePromotionsIfPotentiallyMutatedIn(rightOperand);
- _clearTypePromotionsIfAccessedInClosureAndPotentiallyMutated(
- rightOperand);
- // Visit right operand.
- f();
- } finally {
- _exitScope();
- }
- }
-
- void visitConditionalExpression_then(
- Expression condition, Expression thenExpression, void Function() f) {
- _enterScope();
- try {
- // Type promotion.
- _promoteTypes(condition);
- _clearTypePromotionsIfPotentiallyMutatedIn(thenExpression);
- _clearTypePromotionsIfAccessedInClosureAndPotentiallyMutated(
- thenExpression,
- );
- // Visit "then" expression.
- f();
- } finally {
- _exitScope();
- }
- }
-
- void visitIfElement_thenElement(
- Expression condition, CollectionElement thenElement, void Function() f) {
- _enterScope();
- try {
- // Type promotion.
- _promoteTypes(condition);
- _clearTypePromotionsIfPotentiallyMutatedIn(thenElement);
- _clearTypePromotionsIfAccessedInClosureAndPotentiallyMutated(thenElement);
- // Visit "then".
- f();
- } finally {
- _exitScope();
- }
- }
-
- void visitIfStatement_thenStatement(
- Expression condition, Statement thenStatement, void Function() f) {
- _enterScope();
- try {
- // Type promotion.
- _promoteTypes(condition);
- _clearTypePromotionsIfPotentiallyMutatedIn(thenStatement);
- _clearTypePromotionsIfAccessedInClosureAndPotentiallyMutated(
- thenStatement);
- // Visit "then".
- f();
- } finally {
- _exitScope();
- }
- }
-
- /// Checks each promoted variable in the current scope for compliance with the
- /// following specification statement:
- ///
- /// If the variable <i>v</i> is accessed by a closure in <i>s<sub>1</sub></i>
- /// then the variable <i>v</i> is not potentially mutated anywhere in the
- /// scope of <i>v</i>.
- void _clearTypePromotionsIfAccessedInClosureAndPotentiallyMutated(
- AstNode target) {
- for (Element element in _promotedElements) {
- if (_currentFunctionBody!
- .isPotentiallyMutatedInScope(element as VariableElement)) {
- if (_isVariableAccessedInClosure(element, target)) {
- _setType(element, null);
- }
- }
- }
- }
-
- /// Checks each promoted variable in the current scope for compliance with the
- /// following specification statement:
- ///
- /// <i>v</i> is not potentially mutated in <i>s<sub>1</sub></i> or within a
- /// closure.
- void _clearTypePromotionsIfPotentiallyMutatedIn(AstNode target) {
- for (Element element in _promotedElements) {
- if (_isVariablePotentiallyMutatedIn(element, target)) {
- _setType(element, null);
- }
- }
- }
-
- /// Enter a new promotions scope.
- void _enterScope() {
- _currentScope = _TypePromoteScope(_currentScope);
- }
-
- /// Exit the current promotion scope.
- void _exitScope() {
- var outerScope = _currentScope._outerScope;
- if (outerScope == null) {
- throw StateError("No scope to exit");
- }
- _currentScope = outerScope;
- }
-
- /// Return the promoted type of the given [element], or `null` if the type of
- /// the element has not been promoted.
- DartType? _getPromotedType(Element element) {
- return _currentScope.getType(element);
- }
-
- /// Return the static element associated with the given expression whose type
- /// can be promoted, or `null` if there is no element whose type can be
- /// promoted.
- VariableElement? _getPromotionStaticElement(Expression expression) {
- expression = expression.unParenthesized;
- if (expression is SimpleIdentifier) {
- var element = expression.staticElement;
- if (element is VariableElement) {
- ElementKind kind = element.kind;
- if (kind == ElementKind.LOCAL_VARIABLE ||
- kind == ElementKind.PARAMETER) {
- return element;
- }
- }
- }
- return null;
- }
-
- /// Given that the [node] is a reference to a [VariableElement], return the
- /// static type of the variable at this node - declared or promoted.
- DartType _getType(SimpleIdentifier node) {
- var variable = node.staticElement as VariableElement;
- return _getPromotedType(variable) ?? variable.type;
- }
-
- /// Return `true` if the given variable is accessed within a closure in the
- /// given [AstNode] and also mutated somewhere in variable scope. This
- /// information is only available for local variables (including parameters).
- ///
- /// @param variable the variable to check
- /// @param target the [AstNode] to check within
- /// @return `true` if this variable is potentially mutated somewhere in the
- /// given ASTNode
- bool _isVariableAccessedInClosure(Element variable, AstNode target) {
- _ResolverVisitor_isVariableAccessedInClosure visitor =
- _ResolverVisitor_isVariableAccessedInClosure(variable);
- target.accept(visitor);
- return visitor.result;
- }
-
- /// Return `true` if the given variable is potentially mutated somewhere in
- /// the given [AstNode]. This information is only available for local
- /// variables (including parameters).
- ///
- /// @param variable the variable to check
- /// @param target the [AstNode] to check within
- /// @return `true` if this variable is potentially mutated somewhere in the
- /// given ASTNode
- bool _isVariablePotentiallyMutatedIn(Element variable, AstNode target) {
- _ResolverVisitor_isVariablePotentiallyMutatedIn visitor =
- _ResolverVisitor_isVariablePotentiallyMutatedIn(variable);
- target.accept(visitor);
- return visitor.result;
- }
-
- /// If it is appropriate to do so, promotes the current type of the static
- /// element associated with the given expression with the given type.
- /// Generally speaking, it is appropriate if the given type is more specific
- /// than the current type.
- ///
- /// @param expression the expression used to access the static element whose
- /// types might be promoted
- /// @param potentialType the potential type of the elements
- void _promote(Expression expression, DartType potentialType) {
- var element = _getPromotionStaticElement(expression);
- if (element != null) {
- // may be mutated somewhere in closure
- if (_currentFunctionBody!.isPotentiallyMutatedInClosure(element)) {
- return;
- }
- // prepare current variable type
- DartType type = _getPromotedType(element) ?? expression.typeOrThrow;
-
- // Check if we can promote to potentialType from type.
- var promoteType = _typeSystem.tryPromoteToType(potentialType, type);
- if (promoteType != null) {
- // Do promote type of variable.
- _setType(element, promoteType);
- }
- }
- }
-
- /// Promotes type information using given condition.
- void _promoteTypes(Expression condition) {
- if (condition is BinaryExpression) {
- if (condition.operator.type == TokenType.AMPERSAND_AMPERSAND) {
- Expression left = condition.leftOperand;
- Expression right = condition.rightOperand;
- _promoteTypes(left);
- _promoteTypes(right);
- _clearTypePromotionsIfPotentiallyMutatedIn(right);
- }
- } else if (condition is IsExpression) {
- if (condition.notOperator == null) {
- _promote(condition.expression, condition.type.typeOrThrow);
- }
- } else if (condition is ParenthesizedExpression) {
- _promoteTypes(condition.expression);
- }
- }
-
- /// Set the promoted type of the given element to the given type.
- ///
- /// @param element the element whose type might have been promoted
- /// @param type the promoted type of the given element
- void _setType(Element element, DartType? type) {
- if (_currentScope._outerScope == null) {
- throw StateError("Cannot promote without a scope");
- }
- _currentScope.setType(element, type);
- }
-}
-
-/// The legacy, pre-NNBD implementation of [LocalVariableTypeProvider].
-class _LegacyLocalVariableTypeProvider implements LocalVariableTypeProvider {
- final TypePromotionManager _manager;
-
- _LegacyLocalVariableTypeProvider(this._manager);
-
- @override
- DartType getType(SimpleIdentifier node) {
- return _manager._getType(node);
- }
-}
-
-class _ResolverVisitor_isVariableAccessedInClosure
- extends RecursiveAstVisitor<void> {
- final Element variable;
-
- bool result = false;
-
- bool _inClosure = false;
-
- _ResolverVisitor_isVariableAccessedInClosure(this.variable);
-
- @override
- void visitFunctionExpression(FunctionExpression node) {
- bool inClosure = _inClosure;
- try {
- _inClosure = true;
- super.visitFunctionExpression(node);
- } finally {
- _inClosure = inClosure;
- }
- }
-
- @override
- void visitSimpleIdentifier(SimpleIdentifier node) {
- if (result) {
- return;
- }
- if (_inClosure && identical(node.staticElement, variable)) {
- result = true;
- }
- }
-}
-
-class _ResolverVisitor_isVariablePotentiallyMutatedIn
- extends RecursiveAstVisitor<void> {
- final Element variable;
-
- bool result = false;
-
- _ResolverVisitor_isVariablePotentiallyMutatedIn(this.variable);
-
- @override
- void visitSimpleIdentifier(SimpleIdentifier node) {
- if (result) {
- return;
- }
- if (identical(node.staticElement, variable)) {
- if (node.inSetterContext()) {
- result = true;
- }
- }
- }
-}
-
-/// Instances of the class `TypePromoteScope` represent a scope in which the
-/// types of elements can be promoted.
-class _TypePromoteScope {
- /// The outer scope in which types might be promoted.
- final _TypePromoteScope? _outerScope;
-
- /// A table mapping elements to the promoted type of that element.
- final Map<Element, DartType?> _promotedTypes = {};
-
- /// Initialize a newly created scope to be an empty child of the given scope.
- ///
- /// @param outerScope the outer scope in which types might be promoted
- _TypePromoteScope(this._outerScope);
-
- /// Returns the elements with promoted types.
- Iterable<Element> get promotedElements => _promotedTypes.keys.toSet();
-
- /// Return the promoted type of the given element, or `null` if the type of
- /// the element has not been promoted.
- ///
- /// @param element the element whose type might have been promoted
- /// @return the promoted type of the given element
- DartType? getType(Element element) {
- var type = _promotedTypes[element];
- if (type == null && element is PropertyAccessorElement) {
- type = _promotedTypes[element.variable];
- }
- if (type != null) {
- return type;
- } else if (_outerScope != null) {
- return _outerScope!.getType(element);
- }
- return null;
- }
-
- /// Set the promoted type of the given element to the given type.
- ///
- /// @param element the element whose type might have been promoted
- /// @param type the promoted type of the given element
- void setType(Element element, DartType? type) {
- _promotedTypes[element] = type;
- }
-}
diff --git a/pkg/analyzer/lib/src/generated/variable_type_provider.dart b/pkg/analyzer/lib/src/generated/variable_type_provider.dart
index 376d102..4258443 100644
--- a/pkg/analyzer/lib/src/generated/variable_type_provider.dart
+++ b/pkg/analyzer/lib/src/generated/variable_type_provider.dart
@@ -3,11 +3,24 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
/// Provider of types for local variables and formal parameters.
abstract class LocalVariableTypeProvider {
/// Given that the [node] is a reference to a local variable, or a parameter,
/// return the type of the variable at the node - declared or promoted.
- DartType getType(SimpleIdentifier node);
+ DartType getType(SimpleIdentifier node, {required bool isRead});
+}
+
+/// Implementation of [LocalVariableTypeProvider] that does not promote, for use
+/// in situations where no flow analysis is being done. This happens in some
+/// analyzer internal unit tests.
+class NonPromotingLocalVariableTypeProvider
+ implements LocalVariableTypeProvider {
+ const NonPromotingLocalVariableTypeProvider();
+
+ @override
+ DartType getType(SimpleIdentifier node, {required bool isRead}) =>
+ (node.staticElement as VariableElement).type;
}
diff --git a/pkg/analyzer/lib/src/summary2/ast_resolver.dart b/pkg/analyzer/lib/src/summary2/ast_resolver.dart
index 85a2151..3be65c2 100644
--- a/pkg/analyzer/lib/src/summary2/ast_resolver.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_resolver.dart
@@ -53,16 +53,12 @@
node.accept(variableResolverVisitor);
}
- FlowAnalysisHelper? flowAnalysis;
+ FlowAnalysisHelper flowAnalysis = FlowAnalysisHelper(
+ _unitElement.library.typeSystem,
+ false,
+ _unitElement.library.isNonNullableByDefault);
if (isTopLevelVariableInitializer) {
- if (_unitElement.library.isNonNullableByDefault) {
- flowAnalysis = FlowAnalysisHelper(
- _unitElement.library.typeSystem,
- false,
- );
- flowAnalysis.topLevelDeclaration_enter(
- node.parent as Declaration, null, null);
- }
+ flowAnalysis.topLevelDeclaration_enter(node.parent!, null, null);
}
var resolverVisitor = ResolverVisitor(
@@ -80,14 +76,11 @@
enclosingClassElement: enclosingClassElement,
enclosingExecutableElement: enclosingExecutableElement,
);
- if (enclosingFunctionBody != null) {
- resolverVisitor.prepareCurrentFunctionBody(enclosingFunctionBody);
- }
node.accept(resolverVisitor);
if (isTopLevelVariableInitializer) {
- flowAnalysis?.topLevelDeclaration_exit();
+ flowAnalysis.topLevelDeclaration_exit();
}
}
}
diff --git a/pkg/analyzer/lib/src/summary2/default_value_resolver.dart b/pkg/analyzer/lib/src/summary2/default_value_resolver.dart
index 9566af2..f5d81ef 100644
--- a/pkg/analyzer/lib/src/summary2/default_value_resolver.dart
+++ b/pkg/analyzer/lib/src/summary2/default_value_resolver.dart
@@ -119,6 +119,7 @@
},
enclosingClassElement: _classElement,
enclosingExecutableElement: _executableElement,
+ isTopLevelVariableInitializer: true,
);
}
diff --git a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
index 4b2720b..a7659a2 100644
--- a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
@@ -799,23 +799,15 @@
for (VariableDeclaration parameter
in member.function.positionalParameters) {
inferrer.flowAnalysis?.declare(parameter, true);
- positionalArguments.add(new VariableGetImpl(
- parameter,
- inferrer.typePromoter.getFactForAccess(parameter, 0),
- inferrer.typePromoter.currentScope,
- forNullGuardedAccess: false));
+ positionalArguments.add(
+ new VariableGetImpl(parameter, forNullGuardedAccess: false));
}
List<NamedExpression> namedArguments = <NamedExpression>[];
for (VariableDeclaration parameter
in member.function.namedParameters) {
inferrer.flowAnalysis?.declare(parameter, true);
- namedArguments.add(new NamedExpression(
- parameter.name,
- new VariableGetImpl(
- parameter,
- inferrer.typePromoter.getFactForAccess(parameter, 0),
- inferrer.typePromoter.currentScope,
- forNullGuardedAccess: false)));
+ namedArguments.add(new NamedExpression(parameter.name,
+ new VariableGetImpl(parameter, forNullGuardedAccess: false)));
}
// If arguments are created using [Forest.createArguments], and the
// type arguments are omitted, they are to be inferred.
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 8fc537e..3f3cf82 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -110,9 +110,6 @@
import '../type_inference/type_inferrer.dart'
show TypeInferrer, InferredFunctionBody;
-import '../type_inference/type_promotion.dart'
- show TypePromoter, TypePromotionFact, TypePromotionScope;
-
import '../type_inference/type_schema.dart' show UnknownType;
import '../util/helpers.dart' show DelayedActionPerformer;
@@ -202,8 +199,6 @@
final TypeInferrer typeInferrer;
- final TypePromoter typePromoter;
-
/// Only used when [member] is a constructor. It tracks if an implicit super
/// initializer is needed.
///
@@ -375,7 +370,6 @@
libraryBuilder.importUri.path == "ui"),
needsImplicitSuperInitializer = declarationBuilder is ClassBuilder &&
coreTypes?.objectClass != declarationBuilder.cls,
- typePromoter = typeInferrer?.typePromoter,
super(enclosingScope) {
formalParameterScope?.forEach((String name, Builder builder) {
if (builder is VariableBuilder) {
@@ -463,7 +457,6 @@
@override
void registerVariableAssignment(VariableDeclaration variable) {
- typePromoter?.mutateVariable(variable, functionNestingLevel);
typeInferrer?.assignedVariables?.write(variable);
}
@@ -948,7 +941,6 @@
void finishFunction(
FormalParameters formals, AsyncMarker asyncModifier, Statement body) {
debugEvent("finishFunction");
- typePromoter?.finished();
typeInferrer?.assignedVariables?.finish();
FunctionBuilder builder = member;
@@ -1791,7 +1783,6 @@
bool isAnd = optional("&&", token);
if (isAnd || optional("||", token)) {
Expression lhs = popForValue();
- typePromoter?.enterLogicalExpression(lhs, token.stringValue);
// This is matched by the call to [endNode] in
// [doLogicalExpression].
if (isAnd) {
@@ -1879,7 +1870,6 @@
Expression receiver = pop();
Expression logicalExpression = forest.createLogicalExpression(
offsetForToken(token), receiver, token.stringValue, argument);
- typePromoter?.exitLogicalExpression(argument, logicalExpression);
push(logicalExpression);
if (optional("&&", token)) {
// This is matched by the call to [beginNode] in
@@ -2105,10 +2095,7 @@
if (!(variable as VariableDeclarationImpl).isLocalFunction) {
typeInferrer?.assignedVariables?.read(variable);
}
- Object fact =
- typePromoter?.getFactForAccess(variable, functionNestingLevel);
- Object scope = typePromoter?.currentScope;
- return new VariableGetImpl(variable, fact, scope,
+ return new VariableGetImpl(variable,
forNullGuardedAccess: forNullGuardedAccess)
..fileOffset = charOffset;
}
@@ -2472,7 +2459,6 @@
@override
void beginThenStatement(Token token) {
Expression condition = popForValue();
- enterThenForTypePromotion(condition);
// This is matched by the call to [deferNode] in
// [endThenStatement].
typeInferrer?.assignedVariables?.beginNode();
@@ -2482,7 +2468,6 @@
@override
void endThenStatement(Token token) {
- typePromoter?.enterElse();
super.endThenStatement(token);
// This is matched by the call to [beginNode] in
// [beginThenStatement] and by the call to [storeInfo] in
@@ -2497,7 +2482,6 @@
pop();
Statement thenPart = popStatement();
Expression condition = pop();
- typePromoter?.exitConditional();
Statement node = forest.createIfStatement(
offsetForToken(ifToken), condition, thenPart, elsePart);
// This is matched by the call to [deferNode] in
@@ -3535,24 +3519,18 @@
DartType type = buildDartType(pop(),
allowPotentiallyConstantType: libraryBuilder.isNonNullableByDefault);
Expression operand = popForValue();
- bool isInverted = not != null;
Expression isExpression = forest.createIsExpression(
offsetForToken(isOperator), operand, type,
forNonNullableByDefault: libraryBuilder.isNonNullableByDefault,
notFileOffset: not != null ? offsetForToken(not) : null);
libraryBuilder.checkBoundsInType(
type, typeEnvironment, uri, isOperator.charOffset);
- if (operand is VariableGet) {
- typePromoter?.handleIsCheck(isExpression, isInverted, operand.variable,
- type, functionNestingLevel);
- }
push(isExpression);
}
@override
void beginConditionalExpression(Token question) {
Expression condition = popForValue();
- typePromoter?.enterThen(condition);
// This is matched by the call to [deferNode] in
// [handleConditionalExpressionColon].
typeInferrer?.assignedVariables?.beginNode();
@@ -3563,7 +3541,6 @@
@override
void handleConditionalExpressionColon() {
Expression then = popForValue();
- typePromoter?.enterElse();
// This is matched by the call to [beginNode] in
// [beginConditionalExpression] and by the call to [storeInfo] in
// [endConditionalExpression].
@@ -3580,7 +3557,6 @@
AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesInfo =
pop();
Expression condition = pop();
- typePromoter?.exitConditional();
Expression node = forest.createConditionalExpression(
offsetForToken(question), condition, thenExpression, elseExpression);
push(node);
@@ -4791,7 +4767,6 @@
@override
void handleThenControlFlow(Token token) {
Expression condition = popForValue();
- enterThenForTypePromotion(condition);
// This is matched by the call to [deferNode] in
// [handleElseControlFlow] and by the call to [endNode] in
// [endIfControlFlow].
@@ -4811,7 +4786,6 @@
// [endIfElseControlFlow].
push(typeInferrer?.assignedVariables?.deferNode());
push(node);
- typePromoter?.enterElse();
}
@override
@@ -4831,8 +4805,6 @@
offsetForToken(ifToken), toValue(condition), toValue(entry));
}
push(node);
- typePromoter?.enterElse();
- typePromoter?.exitConditional();
// This is matched by the call to [beginNode] in
// [handleThenControlFlow].
typeInferrer?.assignedVariables?.endNode(node);
@@ -4847,7 +4819,6 @@
pop();
Object condition = pop(); // parenthesized expression
Token ifToken = pop();
- typePromoter?.exitConditional();
transformCollections = true;
TreeNode node;
@@ -5343,12 +5314,8 @@
/// lvalue = #t;
/// body;
/// }
- TypePromotionFact fact =
- typePromoter?.getFactForAccess(variable, functionNestingLevel);
- TypePromotionScope scope = typePromoter?.currentScope;
elements.syntheticAssignment = lvalue.buildAssignment(
- new VariableGetImpl(variable, fact, scope,
- forNullGuardedAccess: false)
+ new VariableGetImpl(variable, forNullGuardedAccess: false)
..fileOffset = inToken.offset,
voidContext: true);
} else {
@@ -6560,12 +6527,6 @@
return new DeferredCheck(check, expression)..fileOffset = charOffset;
}
- /// TODO(ahe): This method is temporarily implemented. Once type promotion is
- /// independent of shadow nodes, remove this method.
- void enterThenForTypePromotion(Expression condition) {
- typePromoter?.enterThen(condition);
- }
-
bool isErroneousNode(TreeNode node) {
return libraryBuilder.loader.handledErrors.isNotEmpty &&
forest.isErroneousNode(node);
diff --git a/pkg/front_end/lib/src/fasta/kernel/collections.dart b/pkg/front_end/lib/src/fasta/kernel/collections.dart
index e2fb85b..b29d126 100644
--- a/pkg/front_end/lib/src/fasta/kernel/collections.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/collections.dart
@@ -48,10 +48,11 @@
/// Returns this control flow element as a [MapEntry], or `null` if this
/// control flow element cannot be converted into a [MapEntry].
///
- /// [onConvertForElement] is called when a [ForElement] or [ForInElement] is
- /// converted to a [ForMapEntry] or [ForInMapEntry], respectively.
+ /// [onConvertElement] is called when a [ForElement], [ForInElement], or
+ /// [IfElement] is converted to a [ForMapEntry], [ForInMapEntry], or
+ /// [IfMapEntry], respectively.
// TODO(johnniwinther): Merge this with [convertToMapEntry].
- MapEntry toMapEntry(void onConvertForElement(TreeNode from, TreeNode to));
+ MapEntry toMapEntry(void onConvertElement(TreeNode from, TreeNode to));
}
/// A spread element in a list or set literal.
@@ -91,8 +92,7 @@
}
@override
- SpreadMapEntry toMapEntry(
- void onConvertForElement(TreeNode from, TreeNode to)) {
+ SpreadMapEntry toMapEntry(void onConvertElement(TreeNode from, TreeNode to)) {
return new SpreadMapEntry(expression, isNullAware)..fileOffset = fileOffset;
}
@@ -163,23 +163,25 @@
}
@override
- MapEntry toMapEntry(void onConvertForElement(TreeNode from, TreeNode to)) {
+ MapEntry toMapEntry(void onConvertElement(TreeNode from, TreeNode to)) {
MapEntry thenEntry;
if (then is ControlFlowElement) {
ControlFlowElement thenElement = then;
- thenEntry = thenElement.toMapEntry(onConvertForElement);
+ thenEntry = thenElement.toMapEntry(onConvertElement);
}
if (thenEntry == null) return null;
MapEntry otherwiseEntry;
if (otherwise != null) {
if (otherwise is ControlFlowElement) {
ControlFlowElement otherwiseElement = otherwise;
- otherwiseEntry = otherwiseElement.toMapEntry(onConvertForElement);
+ otherwiseEntry = otherwiseElement.toMapEntry(onConvertElement);
}
if (otherwiseEntry == null) return null;
}
- return new IfMapEntry(condition, thenEntry, otherwiseEntry)
+ IfMapEntry result = new IfMapEntry(condition, thenEntry, otherwiseEntry)
..fileOffset = fileOffset;
+ onConvertElement(this, result);
+ return result;
}
@override
@@ -251,17 +253,17 @@
}
@override
- MapEntry toMapEntry(void onConvertForElement(TreeNode from, TreeNode to)) {
+ MapEntry toMapEntry(void onConvertElement(TreeNode from, TreeNode to)) {
MapEntry bodyEntry;
if (body is ControlFlowElement) {
ControlFlowElement bodyElement = body;
- bodyEntry = bodyElement.toMapEntry(onConvertForElement);
+ bodyEntry = bodyElement.toMapEntry(onConvertElement);
}
if (bodyEntry == null) return null;
ForMapEntry result =
new ForMapEntry(variables, condition, updates, bodyEntry)
..fileOffset = fileOffset;
- onConvertForElement(this, result);
+ onConvertElement(this, result);
return result;
}
@@ -367,18 +369,18 @@
}
@override
- MapEntry toMapEntry(void onConvertForElement(TreeNode from, TreeNode to)) {
+ MapEntry toMapEntry(void onConvertElement(TreeNode from, TreeNode to)) {
MapEntry bodyEntry;
if (body is ControlFlowElement) {
ControlFlowElement bodyElement = body;
- bodyEntry = bodyElement.toMapEntry(onConvertForElement);
+ bodyEntry = bodyElement.toMapEntry(onConvertElement);
}
if (bodyEntry == null) return null;
ForInMapEntry result = new ForInMapEntry(variable, iterable,
syntheticAssignment, expressionEffects, bodyEntry, problem,
isAsync: isAsync)
..fileOffset = fileOffset;
- onConvertForElement(this, result);
+ onConvertElement(this, result);
return result;
}
@@ -707,31 +709,31 @@
/// converted an error reported through [helper] and an invalid expression is
/// returned.
///
-/// [onConvertForMapEntry] is called when a [ForMapEntry] or [ForInMapEntry] is
-/// converted to a [ForElement] or [ForInElement], respectively.
+/// [onConvertMapEntry] is called when a [ForMapEntry], [ForInMapEntry], or
+/// [IfMapEntry] is converted to a [ForElement], [ForInElement], or [IfElement],
+/// respectively.
Expression convertToElement(MapEntry entry, InferenceHelper helper,
- void onConvertForMapEntry(TreeNode from, TreeNode to)) {
+ void onConvertMapEntry(TreeNode from, TreeNode to)) {
if (entry is SpreadMapEntry) {
return new SpreadElement(entry.expression, entry.isNullAware)
..fileOffset = entry.expression.fileOffset;
}
if (entry is IfMapEntry) {
- return new IfElement(
+ IfElement result = new IfElement(
entry.condition,
- convertToElement(entry.then, helper, onConvertForMapEntry),
+ convertToElement(entry.then, helper, onConvertMapEntry),
entry.otherwise == null
? null
- : convertToElement(entry.otherwise, helper, onConvertForMapEntry))
+ : convertToElement(entry.otherwise, helper, onConvertMapEntry))
..fileOffset = entry.fileOffset;
+ onConvertMapEntry(entry, result);
+ return result;
}
if (entry is ForMapEntry) {
- ForElement result = new ForElement(
- entry.variables,
- entry.condition,
- entry.updates,
- convertToElement(entry.body, helper, onConvertForMapEntry))
+ ForElement result = new ForElement(entry.variables, entry.condition,
+ entry.updates, convertToElement(entry.body, helper, onConvertMapEntry))
..fileOffset = entry.fileOffset;
- onConvertForMapEntry(entry, result);
+ onConvertMapEntry(entry, result);
return result;
}
if (entry is ForInMapEntry) {
@@ -740,11 +742,11 @@
entry.iterable,
entry.syntheticAssignment,
entry.expressionEffects,
- convertToElement(entry.body, helper, onConvertForMapEntry),
+ convertToElement(entry.body, helper, onConvertMapEntry),
entry.problem,
isAsync: entry.isAsync)
..fileOffset = entry.fileOffset;
- onConvertForMapEntry(entry, result);
+ onConvertMapEntry(entry, result);
return result;
}
Expression key = entry.key;
@@ -782,31 +784,34 @@
/// converted an error reported through [helper] and a map entry holding an
/// invalid expression is returned.
///
-/// [onConvertForElement] is called when a [ForElement] or [ForInElement] is
-/// converted to a [ForMapEntry] or [ForInMapEntry], respectively.
+/// [onConvertElement] is called when a [ForElement], [ForInElement], or
+/// [IfElement] is converted to a [ForMapEntry], [ForInMapEntry], or
+/// [IfMapEntry], respectively.
MapEntry convertToMapEntry(Expression element, InferenceHelper helper,
- void onConvertForElement(TreeNode from, TreeNode to)) {
+ void onConvertElement(TreeNode from, TreeNode to)) {
if (element is SpreadElement) {
return new SpreadMapEntry(element.expression, element.isNullAware)
..fileOffset = element.expression.fileOffset;
}
if (element is IfElement) {
- return new IfMapEntry(
+ IfMapEntry result = new IfMapEntry(
element.condition,
- convertToMapEntry(element.then, helper, onConvertForElement),
+ convertToMapEntry(element.then, helper, onConvertElement),
element.otherwise == null
? null
- : convertToMapEntry(element.otherwise, helper, onConvertForElement))
+ : convertToMapEntry(element.otherwise, helper, onConvertElement))
..fileOffset = element.fileOffset;
+ onConvertElement(element, result);
+ return result;
}
if (element is ForElement) {
ForMapEntry result = new ForMapEntry(
element.variables,
element.condition,
element.updates,
- convertToMapEntry(element.body, helper, onConvertForElement))
+ convertToMapEntry(element.body, helper, onConvertElement))
..fileOffset = element.fileOffset;
- onConvertForElement(element, result);
+ onConvertElement(element, result);
return result;
}
if (element is ForInElement) {
@@ -815,11 +820,11 @@
element.iterable,
element.syntheticAssignment,
element.expressionEffects,
- convertToMapEntry(element.body, helper, onConvertForElement),
+ convertToMapEntry(element.body, helper, onConvertElement),
element.problem,
isAsync: element.isAsync)
..fileOffset = element.fileOffset;
- onConvertForElement(element, result);
+ onConvertElement(element, result);
return result;
}
return new MapEntry(
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 3e36973..20a67a7 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -6545,7 +6545,8 @@
if (initializerResult != null) {
DartType initializerType = initializerResult.inferredType;
if (node.isImplicitlyTyped) {
- if (initializerType is TypeParameterType) {
+ if (inferrer.isNonNullableByDefault &&
+ initializerType is TypeParameterType) {
inferrer.flowAnalysis.promote(node, initializerType);
}
} else {
@@ -6735,20 +6736,14 @@
DartType declaredOrInferredType = variable.lateType ?? variable.type;
if (isExtensionThis(variable)) {
inferrer.flowAnalysis.thisOrSuper(node, variable.type);
- } else if (inferrer.isNonNullableByDefault) {
- if (node.forNullGuardedAccess) {
- DartType nonNullableType = variable.type.toNonNull();
- if (nonNullableType != variable.type) {
- promotedType = nonNullableType;
- }
- } else if (!variable.isLocalFunction) {
- // Don't promote local functions.
- promotedType = inferrer.flowAnalysis.variableRead(node, variable);
+ } else if (inferrer.isNonNullableByDefault && node.forNullGuardedAccess) {
+ DartType nonNullableType = variable.type.toNonNull();
+ if (nonNullableType != variable.type) {
+ promotedType = nonNullableType;
}
- } else {
- bool mutatedInClosure = variable.mutatedInClosure;
- promotedType = inferrer.typePromoter
- .computePromotedType(node.fact, node.scope, mutatedInClosure);
+ } else if (!variable.isLocalFunction) {
+ // Don't promote local functions.
+ promotedType = inferrer.flowAnalysis.variableRead(node, variable);
}
if (promotedType != null) {
inferrer.instrumentation?.record(
diff --git a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
index 35da3d2..46afae9 100644
--- a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
@@ -40,14 +40,8 @@
import '../type_inference/type_inference_engine.dart';
import '../type_inference/type_inferrer.dart';
-import '../type_inference/type_promotion.dart'
- show TypePromoter, TypePromoterImpl, TypePromotionFact, TypePromotionScope;
-
import '../type_inference/type_schema.dart' show UnknownType;
-import '../type_inference/type_schema_environment.dart'
- show TypeSchemaEnvironment;
-
import 'inference_visitor.dart';
/// Computes the return type of a (possibly factory) constructor.
@@ -1449,50 +1443,6 @@
}
}
-/// Concrete implementation of [TypePromoter] specialized to work with kernel
-/// objects.
-class ShadowTypePromoter extends TypePromoterImpl {
- ShadowTypePromoter.private(TypeSchemaEnvironment typeSchemaEnvironment)
- : super.private(typeSchemaEnvironment);
-
- @override
- int getVariableFunctionNestingLevel(VariableDeclaration variable) {
- if (variable is VariableDeclarationImpl) {
- return variable.functionNestingLevel;
- } else {
- // Hack to deal with the fact that BodyBuilder still creates raw
- // VariableDeclaration objects sometimes.
- // TODO(paulberry): get rid of this once the type parameter is
- // KernelVariableDeclaration.
- return 0;
- }
- }
-
- @override
- bool isPromotionCandidate(VariableDeclaration variable) {
- assert(variable is VariableDeclarationImpl);
- VariableDeclarationImpl kernelVariableDeclaration = variable;
- return !kernelVariableDeclaration.isLocalFunction;
- }
-
- @override
- bool sameExpressions(Expression a, Expression b) {
- return identical(a, b);
- }
-
- @override
- void setVariableMutatedInClosure(VariableDeclaration variable) {
- if (variable is VariableDeclarationImpl) {
- variable.mutatedInClosure = true;
- } else {
- // Hack to deal with the fact that BodyBuilder still creates raw
- // VariableDeclaration objects sometimes.
- // TODO(paulberry): get rid of this once the type parameter is
- // KernelVariableDeclaration.
- }
- }
-}
-
/// Front end specific implementation of [VariableDeclaration].
class VariableDeclarationImpl extends VariableDeclaration {
final bool forSyntheticToken;
@@ -1635,16 +1585,11 @@
/// Front end specific implementation of [VariableGet].
class VariableGetImpl extends VariableGet {
- final TypePromotionFact fact;
-
- final TypePromotionScope scope;
-
// TODO(johnniwinther): Remove the need for this by encoding all null aware
// expressions explicitly.
final bool forNullGuardedAccess;
- VariableGetImpl(VariableDeclaration variable, this.fact, this.scope,
- {this.forNullGuardedAccess})
+ VariableGetImpl(VariableDeclaration variable, {this.forNullGuardedAccess})
: assert(forNullGuardedAccess != null),
super(variable);
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index eafc3e4..aa896d9 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -59,8 +59,6 @@
import 'type_inference_engine.dart';
-import 'type_promotion.dart' show TypePromoter;
-
import 'type_schema.dart' show isKnown, UnknownType;
import 'type_schema_elimination.dart' show greatestClosure;
@@ -123,10 +121,6 @@
abstract class TypeInferrer {
SourceLibraryBuilder get library;
- /// Gets the [TypePromoter] that can be used to perform type promotion within
- /// this method body or initializer.
- TypePromoter get typePromoter;
-
/// Gets the [TypeSchemaEnvironment] being used for type inference.
TypeSchemaEnvironment get typeSchemaEnvironment;
@@ -183,9 +177,6 @@
final TypeInferenceEngine engine;
- @override
- final TypePromoter typePromoter;
-
final FlowAnalysis<TreeNode, Statement, Expression, VariableDeclaration,
DartType> flowAnalysis;
@@ -227,10 +218,13 @@
instrumentation = topLevel ? null : engine.instrumentation,
typeSchemaEnvironment = engine.typeSchemaEnvironment,
isTopLevel = topLevel,
- typePromoter = new TypePromoter(engine.typeSchemaEnvironment),
- flowAnalysis = new FlowAnalysis(
- new TypeOperationsCfe(engine.typeSchemaEnvironment),
- assignedVariables);
+ flowAnalysis = library.isNonNullableByDefault
+ ? new FlowAnalysis(
+ new TypeOperationsCfe(engine.typeSchemaEnvironment),
+ assignedVariables)
+ : new FlowAnalysis.legacy(
+ new TypeOperationsCfe(engine.typeSchemaEnvironment),
+ assignedVariables);
CoreTypes get coreTypes => engine.coreTypes;
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_promotion.dart b/pkg/front_end/lib/src/fasta/type_inference/type_promotion.dart
deleted file mode 100644
index f94934e..0000000
--- a/pkg/front_end/lib/src/fasta/type_inference/type_promotion.dart
+++ /dev/null
@@ -1,704 +0,0 @@
-// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE.md file.
-
-// @dart = 2.9
-
-import 'package:kernel/ast.dart'
- show DartType, Expression, TypeParameterType, VariableDeclaration;
-
-import 'package:kernel/type_environment.dart' show SubtypeCheckMode;
-
-import '../fasta_codes.dart' show templateInternalProblemStackNotEmpty;
-
-import '../problems.dart' show internalProblem;
-
-import '../kernel/internal_ast.dart' show ShadowTypePromoter;
-
-import 'type_schema_environment.dart' show TypeSchemaEnvironment;
-
-/// Keeps track of the state necessary to perform type promotion.
-///
-/// Theory of operation: during parsing, the BodyBuilder calls methods in this
-/// class to inform it of syntactic constructs that are encountered. Those
-/// methods maintain a linked list of [TypePromotionFact] objects tracking what
-/// is known about the state of each variable at the current point in the code,
-/// as well as a linked list of [TypePromotionScope] objects tracking the
-/// component's nesting structure. Whenever a variable is read, the current
-/// [TypePromotionFact] and [TypePromotionScope] are recorded for later use.
-///
-/// During type inference, the [TypeInferrer] calls back into this class to ask
-/// whether each variable read is a promoted read. This is determined by
-/// examining the [TypePromotionScope] and [TypePromotionFact] objects that were
-/// recorded at the time the variable read was parsed, as well as other state
-/// that may have been updated later during the parsing process.
-///
-/// This class abstracts away the representation of the underlying AST using
-/// generic parameters. Derived classes should set E and V to the class they
-/// use to represent expressions and variable declarations, respectively.
-abstract class TypePromoter {
- TypePromoter.private();
-
- factory TypePromoter(TypeSchemaEnvironment typeSchemaEnvironment) =
- ShadowTypePromoter.private;
-
- factory TypePromoter.disabled() = TypePromoterDisabled.private;
-
- /// Returns the current type promotion scope.
- TypePromotionScope get currentScope;
-
- /// Computes the promoted type of a variable read having the given [fact] and
- /// [scope]. Returns `null` if there is no promotion.
- ///
- /// [mutatedInClosure] indicates whether the variable was mutated in a closure
- /// somewhere in the method.
- DartType computePromotedType(
- TypePromotionFact fact, TypePromotionScope scope, bool mutatedInClosure);
-
- /// Updates the state to reflect the fact that we are entering an "else"
- /// branch.
- void enterElse();
-
- /// Updates the state to reflect the fact that the LHS of an "&&" or "||"
- /// expression has just been parsed, and we are entering the RHS.
- void enterLogicalExpression(Expression lhs, String operator);
-
- /// Updates the state to reflect the fact that the "condition" part of an "if"
- /// statement or conditional expression has just been parsed, and we are
- /// entering the "then" branch.
- void enterThen(Expression condition);
-
- /// Updates the state to reflect the fact that we have exited the "else"
- /// branch of an "if" statement or conditional expression.
- void exitConditional();
-
- /// Updates the state to reflect the fact that we have exited the RHS of an
- /// "&&" or "||" expression.
- void exitLogicalExpression(Expression rhs, Expression logicalExpression);
-
- /// Verifies that enter/exit calls were properly nested.
- void finished();
-
- /// Records that the given [variable] was accessed for reading, and returns a
- /// [TypePromotionFact] describing the variable's current type promotion
- /// state.
- ///
- /// [functionNestingLevel] should be the current nesting level of closures.
- /// This is used to determine if the variable was accessed in a closure.
- TypePromotionFact getFactForAccess(
- VariableDeclaration variable, int functionNestingLevel);
-
- /// Updates the state to reflect the fact that an "is" check of a local
- /// variable was just parsed.
- void handleIsCheck(Expression isExpression, bool isInverted,
- VariableDeclaration variable, DartType type, int functionNestingLevel);
-
- /// Updates the state to reflect the fact that the given [variable] was
- /// mutated.
- void mutateVariable(VariableDeclaration variable, int functionNestingLevel);
-}
-
-/// Implementation of [TypePromoter] which doesn't do any type promotion.
-///
-/// This is intended for profiling, to ensure that type inference and type
-/// promotion do not slow down compilation too much.
-class TypePromoterDisabled extends TypePromoter {
- TypePromoterDisabled.private() : super.private();
-
- @override
- TypePromotionScope get currentScope => null;
-
- @override
- DartType computePromotedType(TypePromotionFact fact, TypePromotionScope scope,
- bool mutatedInClosure) =>
- null;
-
- @override
- void enterElse() {}
-
- @override
- void enterLogicalExpression(Expression lhs, String operator) {}
-
- @override
- void enterThen(Expression condition) {}
-
- @override
- void exitConditional() {}
-
- @override
- void exitLogicalExpression(Expression rhs, Expression logicalExpression) {}
-
- @override
- void finished() {}
-
- @override
- TypePromotionFact getFactForAccess(
- VariableDeclaration variable, int functionNestingLevel) =>
- null;
-
- @override
- void handleIsCheck(Expression isExpression, bool isInverted,
- VariableDeclaration variable, DartType type, int functionNestingLevel) {}
-
- @override
- void mutateVariable(VariableDeclaration variable, int functionNestingLevel) {}
-}
-
-/// Derived class containing generic implementations of [TypePromoter].
-///
-/// This class contains as much of the implementation of type promotion as
-/// possible without needing access to private members of shadow objects. It
-/// defers to abstract methods for everything else.
-abstract class TypePromoterImpl extends TypePromoter {
- final TypeSchemaEnvironment typeSchemaEnvironment;
-
- /// [TypePromotionFact] representing the initial state (no facts have been
- /// determined yet).
- ///
- /// All linked lists of facts terminate in this object.
- final _NullFact _nullFacts;
-
- /// Map from variable declaration to the most recent [TypePromotionFact]
- /// associated with the variable.
- ///
- /// [TypePromotionFact]s that are not associated with any variable show up in
- /// this map under the key `null`.
- final _factCache = <VariableDeclaration, TypePromotionFact>{};
-
- /// Linked list of [TypePromotionFact]s that was current at the time the
- /// [_factCache] was last updated.
- TypePromotionFact _factCacheState;
-
- /// Linked list of [TypePromotionFact]s describing what is known to be true
- /// after execution of the expression or statement that was most recently
- /// parsed.
- TypePromotionFact _currentFacts;
-
- /// The most recently parsed expression whose outcome potentially affects what
- /// is known to be true (e.g. an "is" check or a logical expression). May be
- /// `null` if no such expression has been encountered yet.
- Expression _promotionExpression;
-
- /// Linked list of [TypePromotionFact]s describing what is known to be true
- /// after execution of [_promotionExpression], assuming that
- /// [_promotionExpression] evaluates to `true`.
- TypePromotionFact _trueFactsForPromotionExpression;
-
- /// Linked list of [TypePromotionScope]s describing the nesting structure that
- /// contains the expression or statement that was most recently parsed.
- TypePromotionScope _currentScope = const _TopLevelScope();
-
- /// The sequence number of the [TypePromotionFact] that was most recently
- /// created.
- int _lastFactSequenceNumber = 0;
-
- /// Map from variables to the set of scopes in which the variable is mutated.
- /// If a variable is missing from the map, it is not mutated anywhere.
- Map<VariableDeclaration, Set<TypePromotionScope>> _variableMutationScopes =
- new Map<VariableDeclaration, Set<TypePromotionScope>>.identity();
-
- TypePromoterImpl.private(TypeSchemaEnvironment typeSchemaEnvironment)
- : this._(typeSchemaEnvironment, new _NullFact());
-
- TypePromoterImpl._(this.typeSchemaEnvironment, _NullFact this._nullFacts)
- : _factCacheState = _nullFacts,
- _currentFacts = _nullFacts,
- super.private() {
- _factCache[null] = _nullFacts;
- }
-
- @override
- TypePromotionScope get currentScope => _currentScope;
-
- @override
- DartType computePromotedType(
- TypePromotionFact fact, TypePromotionScope scope, bool mutatedInClosure) {
- if (mutatedInClosure) return null;
- return fact?._computePromotedType(
- this, scope, _variableMutationScopes[fact.variable]);
- }
-
- /// For internal debugging use, optionally prints the current state followed
- /// by the event name. Uncomment the call to [_printEvent] to see the
- /// sequence of calls into the type promoter and the corresponding states.
- void debugEvent(String name) {
- // _printEvent(name);
- }
-
- @override
- void enterElse() {
- debugEvent('enterElse');
- // Pop the scope and restore the facts to the state they were in before we
- // entered the conditional. No promotion happens in the "else" branch.
- _ConditionalScope scope = _currentScope;
- _currentScope = _currentScope._enclosing;
- _currentFacts = scope.beforeElse;
- }
-
- @override
- void enterLogicalExpression(Expression lhs, String operator) {
- debugEvent('enterLogicalExpression');
- if (!identical(operator, '&&')) {
- // We don't promote for `||`.
- _currentScope = new _LogicalScope(_currentScope, false, _currentFacts);
- } else {
- // Figure out what the facts are based on possible LHS outcomes.
- TypePromotionFact trueFacts = _factsWhenTrue(lhs);
- // Record the fact that we are entering a new scope, and save the
- // appropriate facts for the case where the expression gets short-cut.
- _currentScope = new _LogicalScope(_currentScope, true, _currentFacts);
- // While processing the RHS, assume the condition was true.
- _currentFacts = _addBlockingScopeToFacts(trueFacts);
- }
- }
-
- @override
- void enterThen(Expression condition) {
- debugEvent('enterThen');
- // Figure out what the facts are based on possible condition outcomes.
- TypePromotionFact trueFacts = _factsWhenTrue(condition);
- // Record the fact that we are entering a new scope, and save the current
- // facts for when we enter the "else" branch.
- _currentScope = new _ConditionalScope(_currentScope, _currentFacts);
- // While processing the "then" block, assume the condition was true.
- _currentFacts = _addBlockingScopeToFacts(trueFacts);
- }
-
- @override
- void exitConditional() {
- debugEvent('exitConditional');
- }
-
- @override
- void exitLogicalExpression(Expression rhs, Expression logicalExpression) {
- debugEvent('exitLogicalExpression');
- _LogicalScope scope = _currentScope;
- if (scope.isAnd) {
- TypePromotionFact factsWhenTrue = _factsWhenTrue(rhs);
- _currentFacts = scope.shortcutFacts;
- _recordPromotionExpression(
- logicalExpression, _addBlockingScopeToFacts(factsWhenTrue));
- }
- _currentScope = _currentScope._enclosing;
- }
-
- @override
- void finished() {
- debugEvent('finished');
- if (_currentScope is! _TopLevelScope) {
- internalProblem(
- templateInternalProblemStackNotEmpty.withArguments(
- "$runtimeType", "$_currentScope"),
- -1,
- null);
- }
- }
-
- @override
- TypePromotionFact getFactForAccess(
- VariableDeclaration variable, int functionNestingLevel) {
- debugEvent('getFactForAccess');
- TypePromotionFact fact = _computeCurrentFactMap()[variable];
- TypePromotionFact._recordAccessedInScope(
- fact, _currentScope, functionNestingLevel);
- return fact;
- }
-
- /// Returns the nesting level that was in effect when [variable] was declared.
- int getVariableFunctionNestingLevel(VariableDeclaration variable);
-
- @override
- void handleIsCheck(Expression isExpression, bool isInverted,
- VariableDeclaration variable, DartType type, int functionNestingLevel) {
- debugEvent('handleIsCheck');
- if (!isPromotionCandidate(variable)) return;
- _IsCheck isCheck = new _IsCheck(
- ++_lastFactSequenceNumber,
- variable,
- _currentFacts,
- _computeCurrentFactMap()[variable],
- functionNestingLevel,
- type, []);
- if (!isInverted) {
- _recordPromotionExpression(isExpression, isCheck);
- }
- }
-
- /// Determines whether the given variable should be considered for promotion
- /// at all.
- ///
- /// This is needed because in kernel, [VariableDeclaration] objects are
- /// sometimes used to represent local functions, which are not subject to
- /// promotion.
- bool isPromotionCandidate(VariableDeclaration variable);
-
- /// Updates the state to reflect the fact that the given [variable] was
- /// mutated.
- void mutateVariable(VariableDeclaration variable, int functionNestingLevel) {
- debugEvent('mutateVariable');
- (_variableMutationScopes[variable] ??=
- new Set<TypePromotionScope>.identity())
- .add(_currentScope);
- if (getVariableFunctionNestingLevel(variable) < functionNestingLevel) {
- setVariableMutatedInClosure(variable);
- }
- }
-
- /// Determines whether [a] and [b] represent the same expression, after
- /// dropping redundant enclosing parentheses.
- bool sameExpressions(Expression a, Expression b);
-
- /// Records that the given variable was mutated inside a closure.
- void setVariableMutatedInClosure(VariableDeclaration variable);
-
- /// Updates any facts that are present in [facts] but not in [_currentFacts]
- /// so that they include [_currentScope] in their list of blocking scopes, and
- /// returns the resulting new linked list of facts.
- ///
- /// This is used when entering the body of a conditional, or the RHS of a
- /// logical "and", to ensure that promotions are blocked if the construct
- /// being entered contains any modifications of the corresponding variables.
- /// It is also used when leaving the RHS of a logical "and", to ensure that
- /// any promotions induced by the RHS of the "and" are blocked if the RHS of
- /// the "and" contains any modifications of the corresponding variables.
- TypePromotionFact _addBlockingScopeToFacts(TypePromotionFact facts) {
- List<TypePromotionFact> factsToUpdate = [];
- while (facts != _currentFacts) {
- factsToUpdate.add(facts);
- facts = facts.previous;
- }
- Map<VariableDeclaration, TypePromotionFact> factMap =
- _computeCurrentFactMap();
- for (TypePromotionFact fact in factsToUpdate.reversed) {
- _IsCheck isCheck = fact as _IsCheck;
- VariableDeclaration variable = isCheck.variable;
- facts = new _IsCheck(
- ++_lastFactSequenceNumber,
- variable,
- facts,
- factMap[variable],
- isCheck.functionNestingLevel,
- isCheck.checkedType,
- [...isCheck._blockingScopes, _currentScope]);
- factMap[variable] = facts;
- _factCacheState = facts;
- }
- return facts;
- }
-
- /// Returns a map from variable declaration to the most recent
- /// [TypePromotionFact] associated with the variable.
- Map<VariableDeclaration, TypePromotionFact> _computeCurrentFactMap() {
- // Roll back any map entries associated with facts that are no longer in
- // effect, and set [commonAncestor] to the fact that is an ancestor of
- // the current state and the previously cached state. To do this, we set a
- // variable pointing to [_currentFacts], and then walk both it and
- // [_factCacheState] back to their common ancestor, updating [_factCache] as
- // we go.
- TypePromotionFact commonAncestor = _currentFacts;
- while (commonAncestor.sequenceNumber != _factCacheState.sequenceNumber) {
- if (commonAncestor.sequenceNumber > _factCacheState.sequenceNumber) {
- // The currently cached state is older than the common ancestor guess,
- // so the common ancestor guess needs to be walked back.
- commonAncestor = commonAncestor.previous;
- } else {
- // The common ancestor guess is older than the currently cached state,
- // so we need to roll back the map entry associated with the currently
- // cached state.
- _factCache[_factCacheState.variable] =
- _factCacheState.previousForVariable;
- _factCacheState = _factCacheState.previous;
- }
- }
- assert(identical(commonAncestor, _factCacheState));
- // Roll forward any map entries associated with facts that are newly in
- // effect. Since newer facts link to older ones, it is easiest to do roll
- // forward the most recent facts first.
- for (TypePromotionFact newState = _currentFacts;
- !identical(newState, commonAncestor);
- newState = newState.previous) {
- TypePromotionFact currentlyCached = _factCache[newState.variable];
- // Note: Since we roll forward the most recent facts first, we need to be
- // careful not write an older fact over a newer one.
- if (currentlyCached == null ||
- newState.sequenceNumber > currentlyCached.sequenceNumber) {
- _factCache[newState.variable] = newState;
- }
- }
- _factCacheState = _currentFacts;
- return _factCache;
- }
-
- /// Returns the set of facts known to be true after the execution of [e]
- /// assuming it evaluates to `true`.
- ///
- /// [e] must be the most recently parsed expression or statement.
- TypePromotionFact _factsWhenTrue(Expression e) =>
- sameExpressions(_promotionExpression, e)
- ? _trueFactsForPromotionExpression
- : _currentFacts;
-
- /// For internal debugging use, prints the current state followed by the event
- /// name.
- // ignore: unused_element
- void _printEvent(String name) {
- Iterable<TypePromotionFact> factChain(TypePromotionFact fact) sync* {
- while (fact != null) {
- yield fact;
- fact = fact.previousForVariable;
- }
- }
-
- _computeCurrentFactMap().forEach((variable, fact) {
- if (fact == null) return;
- print(' ${variable ?? '(null)'}: ${factChain(fact).join(' -> ')}');
- });
- if (_promotionExpression != null) {
- print(' _promotionExpression: $_promotionExpression');
- if (!identical(_trueFactsForPromotionExpression, _currentFacts)) {
- print(' if true: '
- '${factChain(_trueFactsForPromotionExpression).join(' -> ')}');
- }
- }
- print(name);
- }
-
- /// Records that after the evaluation of [expression], the facts will be
- /// [ifTrue] on a branch where the expression evaluated to `true`.
- void _recordPromotionExpression(
- Expression expression, TypePromotionFact ifTrue) {
- _promotionExpression = expression;
- _trueFactsForPromotionExpression = ifTrue;
- }
-}
-
-/// A single fact which is known to the type promotion engine about the state of
-/// a variable (or about the flow control of the component).
-///
-/// The type argument V represents is the class which represents local variable
-/// declarations.
-///
-/// Facts are linked together into linked lists via the [previous] pointer into
-/// a data structure called a "fact chain" (or sometimes a "fact state"), which
-/// represents all facts that are known to hold at a certain point in the
-/// component.
-///
-/// The fact is said to "apply" to a given point in the execution of the
-/// component if the fact is part of the current fact state at the point the
-/// parser reaches that point in the component.
-///
-/// Note: just because a fact "applies" to a given point in the execution of
-/// the component doesn't mean a type will be promoted--it simply means that
-/// the fact was deduced at a previous point in the straight line execution of
-/// the code. It's possible that the fact will be overshadowed by a later
-/// fact, or its effect will be cancelled by a later assignment. The final
-/// determination of whether promotion occurs is left to [_computePromotedType].
-abstract class TypePromotionFact {
- /// The variable this fact records information about, or `null` if this fact
- /// records information about general flow control.
- final VariableDeclaration variable;
-
- /// The fact chain that was in effect prior to execution of the statement or
- /// expression that caused this fact to be true.
- final TypePromotionFact previous;
-
- /// Integer associated with this fact. Each time a fact is created it is
- /// given a sequence number one greater than the previously generated fact.
- /// This simplifies the algorithm for finding the common ancestor of two
- /// facts; we repeatedly walk backward the fact with the larger sequence
- /// number until the sequence numbers are the same.
- final int sequenceNumber;
-
- /// The most recent fact appearing in the fact chain [previous] whose
- /// [variable] matches this one, or `null` if there is no such fact.
- final TypePromotionFact previousForVariable;
-
- /// The function nesting level of the expression that led to this fact.
- final int functionNestingLevel;
-
- /// Indicates whether this fact's variable was accessed inside a closure
- /// within the scope the fact applies to.
- bool _accessedInClosureInScope = false;
-
- TypePromotionFact(this.sequenceNumber, this.variable, this.previous,
- this.previousForVariable, this.functionNestingLevel);
-
- /// Computes the promoted type for [variable] at a location in the code where
- /// this fact applies.
- ///
- /// [scope] is the scope containing the read that might be promoted, and
- /// [mutationScopes] is the set of scopes in which the variable is mutated, or
- /// `null` if the variable isn't mutated anywhere.
- ///
- /// Should not be called until after parsing of the entire method is complete.
- DartType _computePromotedType(TypePromoterImpl promoter,
- TypePromotionScope scope, Iterable<TypePromotionScope> mutationScopes);
-
- /// Records the fact that the variable referenced by [fact] was accessed
- /// within the given scope, at the given function nesting level.
- ///
- /// If `null` is passed in for [fact], there is no effect.
- static void _recordAccessedInScope(TypePromotionFact fact,
- TypePromotionScope scope, int functionNestingLevel) {
- // TODO(paulberry): make some integration test cases that exercise the
- // behaviors of this function. In particular verify that it's correct to
- // test functionNestingLevel against fact.functionNestingLevel (as opposed
- // to testing it against getVariableFunctionNestingLevel(variable)).
- while (fact != null) {
- if (functionNestingLevel > fact.functionNestingLevel) {
- if (fact._accessedInClosureInScope) {
- // The variable has already been accessed in a closure in the scope of
- // the current promotion (and this, any enclosing promotions), so
- // no further information needs to be updated.
- return;
- }
- fact._accessedInClosureInScope = true;
- }
- fact = fact.previousForVariable;
- }
- }
-}
-
-/// Represents a contiguous block of program text in which variables may or may
-/// not be promoted. Also used as a stack to keep track of state while the
-/// method is being parsed.
-class TypePromotionScope {
- /// The nesting depth of this scope. The outermost scope (representing the
- /// whole method body) has a depth of 0.
- final int _depth;
-
- /// The [TypePromotionScope] representing the scope enclosing this one.
- final TypePromotionScope _enclosing;
-
- TypePromotionScope(this._enclosing) : _depth = _enclosing._depth + 1;
-
- const TypePromotionScope._topLevel()
- : _enclosing = null,
- _depth = 0;
-
- /// Determines whether this scope completely encloses (or is the same as)
- /// [other].
- bool containsScope(TypePromotionScope other) {
- if (this._depth > other._depth) {
- // We can't possibly contain a scope if we are at greater nesting depth
- // than it is.
- return false;
- }
- while (this._depth < other._depth) {
- other = other._enclosing;
- }
- return identical(this, other);
- }
-}
-
-/// [TypePromotionScope] representing the "then" and "else" bodies of an "if"
-/// statement or conditional expression.
-class _ConditionalScope extends TypePromotionScope {
- /// The fact state in effect at the top of the "else" block.
- final TypePromotionFact beforeElse;
-
- _ConditionalScope(TypePromotionScope enclosing, this.beforeElse)
- : super(enclosing);
-}
-
-/// [TypePromotionFact] representing an "is" check which succeeded.
-class _IsCheck extends TypePromotionFact {
- /// The type appearing on the right hand side of "is".
- final DartType checkedType;
-
- /// List of the scopes in which a mutation to the variable would block
- /// promotion.
- final List<TypePromotionScope> _blockingScopes;
-
- _IsCheck(
- int sequenceNumber,
- VariableDeclaration variable,
- TypePromotionFact previous,
- TypePromotionFact previousForVariable,
- int functionNestingLevel,
- this.checkedType,
- this._blockingScopes)
- : super(sequenceNumber, variable, previous, previousForVariable,
- functionNestingLevel);
-
- @override
- String toString() => 'isCheck($checkedType)';
-
- @override
- DartType _computePromotedType(TypePromoterImpl promoter,
- TypePromotionScope scope, Iterable<TypePromotionScope> mutationScopes) {
- DartType previousPromotedType = previousForVariable?._computePromotedType(
- promoter, scope, mutationScopes);
-
- if (mutationScopes != null) {
- // If the variable was mutated somewhere in a that blocks the promotion,
- // promotion does not occur.
- for (TypePromotionScope blockingScope in _blockingScopes) {
- for (TypePromotionScope mutationScope in mutationScopes) {
- if (blockingScope.containsScope(mutationScope)) {
- return previousPromotedType;
- }
- }
- }
-
- // If the variable was mutated anywhere, and it was accessed inside a
- // closure somewhere in the scope of the potential promotion, promotion
- // does not occur.
- if (_accessedInClosureInScope) {
- return previousPromotedType;
- }
- }
-
- // What we do now depends on the relationship between the previous type of
- // the variable and the type we are checking against.
- DartType previousType = previousPromotedType ?? variable.type;
- if (promoter.typeSchemaEnvironment.isSubtypeOf(
- checkedType, previousType, SubtypeCheckMode.withNullabilities)) {
- // The type we are checking against is a subtype of the previous type of
- // the variable, so this is a refinement; we can promote.
- return checkedType;
- } else if (previousType is TypeParameterType &&
- promoter.typeSchemaEnvironment.isSubtypeOf(checkedType,
- previousType.bound, SubtypeCheckMode.withNullabilities)) {
- // The type we are checking against is a subtype of the bound of the
- // previous type of the variable; we can promote the bound.
- return new TypeParameterType.intersection(
- previousType.parameter, previousType.nullability, checkedType);
- } else {
- // The types aren't sufficiently related; we can't promote.
- return previousPromotedType;
- }
- }
-}
-
-/// [TypePromotionScope] representing the RHS of a logical expression.
-class _LogicalScope extends TypePromotionScope {
- /// Indicates whether the logical operation is an `&&` or an `||`.
- final bool isAnd;
-
- /// The fact state in effect if the logical expression gets short-cut.
- final TypePromotionFact shortcutFacts;
-
- _LogicalScope(TypePromotionScope enclosing, this.isAnd, this.shortcutFacts)
- : super(enclosing);
-}
-
-/// Instance of [TypePromotionFact] representing the facts which are known on
-/// entry to the method (i.e. nothing).
-class _NullFact extends TypePromotionFact {
- _NullFact() : super(0, null, null, null, 0);
-
- @override
- String toString() => 'null';
-
- @override
- DartType _computePromotedType(TypePromoter promoter, TypePromotionScope scope,
- Iterable<TypePromotionScope> mutationScopes) {
- throw new StateError('Tried to create promoted type for no variable');
- }
-}
-
-/// Instance of [TypePromotionScope] representing the entire method body.
-class _TopLevelScope extends TypePromotionScope {
- const _TopLevelScope() : super._topLevel();
-}
diff --git a/pkg/front_end/test/text_representation/internal_ast_text_representation_test.dart b/pkg/front_end/test/text_representation/internal_ast_text_representation_test.dart
index b51b99d..543a79f 100644
--- a/pkg/front_end/test/text_representation/internal_ast_text_representation_test.dart
+++ b/pkg/front_end/test/text_representation/internal_ast_text_representation_test.dart
@@ -511,15 +511,12 @@
void _testVariableGetImpl() {
VariableDeclaration variable = new VariableDeclaration('foo');
- testExpression(
- new VariableGetImpl(variable, null, null, forNullGuardedAccess: false),
- '''
+ testExpression(new VariableGetImpl(variable, forNullGuardedAccess: false), '''
+foo''');
+ testExpression(new VariableGetImpl(variable, forNullGuardedAccess: true), '''
foo''');
testExpression(
- new VariableGetImpl(variable, null, null, forNullGuardedAccess: true), '''
-foo''');
- testExpression(
- new VariableGetImpl(variable, null, null, forNullGuardedAccess: false)
+ new VariableGetImpl(variable, forNullGuardedAccess: false)
..promotedType = const VoidType(),
'''
foo{void}''');
diff --git a/pkg/vm/testcases/transformations/type_flow/summary_collector/bool_expressions.dart.expect b/pkg/vm/testcases/transformations/type_flow/summary_collector/bool_expressions.dart.expect
index 96eea1d..625d97f 100644
--- a/pkg/vm/testcases/transformations/type_flow/summary_collector/bool_expressions.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/summary_collector/bool_expressions.dart.expect
@@ -14,7 +14,7 @@
t6* = _Call direct [#lib::bar] ()
t7* = _Call direct [#lib::foo] ()
t8 = _Join [dynamic] (_T (dart.core::bool, true), t7)
-t9 = _TypeCheck (t8 against dart.core::bool*) (for (x{dart.core::bool*} ?{dynamic} true : #lib::foo()) as dart.core::bool*)
+t9 = _TypeCheck (t8 against dart.core::bool*) (for (x ?{dynamic} true : #lib::foo()) as dart.core::bool*)
t10* = _Call direct [#lib::bar] ()
t11* = _Call direct [#lib::bar] ()
t12* = _Call direct [#lib::foo] ()