blob: f09b6fe1e493b55faccc57cb316b3e56b17f4ee7 [file] [log] [blame]
// Copyright (c) 2018, 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.
// TODO(jensj): Probably all `_createVariableGet(result)` needs their offset
// "nulled out".
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
import 'package:_fe_analyzer_shared/src/type_inference/type_analysis_result.dart';
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart'
as shared;
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart'
hide MapPatternEntry;
import 'package:_fe_analyzer_shared/src/util/link.dart';
import 'package:_fe_analyzer_shared/src/util/null_value.dart';
import 'package:_fe_analyzer_shared/src/util/stack_checker.dart';
import 'package:_fe_analyzer_shared/src/util/value_kind.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/names.dart';
import 'package:kernel/type_algebra.dart';
import 'package:kernel/type_environment.dart';
import 'package:kernel/src/non_null.dart';
import '../api_prototype/experimental_flags.dart';
import '../api_prototype/lowering_predicates.dart';
import '../base/instrumentation.dart'
show
InstrumentationValueForMember,
InstrumentationValueForType,
InstrumentationValueForTypeArgs;
import '../base/problems.dart' as problems
show internalProblem, unhandled, unsupported;
import '../base/uri_offset.dart';
import '../codes/cfe_codes.dart';
import '../kernel/body_builder.dart' show combineStatements;
import '../kernel/collections.dart'
show
ControlFlowElement,
ControlFlowMapEntry,
ForElement,
ForElementBase,
ForInElement,
ForInMapEntry,
ForMapEntry,
ForMapEntryBase,
IfCaseElement,
IfCaseMapEntry,
IfElement,
IfMapEntry,
NullAwareElement,
NullAwareMapEntry,
PatternForElement,
PatternForMapEntry,
SpreadElement,
SpreadMapEntry,
convertToElement;
import '../kernel/hierarchy/class_member.dart';
import '../kernel/implicit_type_argument.dart' show ImplicitTypeArgument;
import '../kernel/internal_ast.dart';
import '../kernel/late_lowering.dart' as late_lowering;
import '../source/constructor_declaration.dart';
import '../source/source_library_builder.dart';
import '../source/source_loader.dart';
import 'closure_context.dart';
import 'external_ast_helper.dart';
import 'for_in.dart';
import 'inference_helper.dart';
import 'inference_results.dart';
import 'inference_visitor_base.dart';
import 'object_access_target.dart';
import 'shared_type_analyzer.dart';
import 'stack_values.dart';
import 'type_constraint_gatherer.dart';
import 'type_inference_engine.dart';
import 'type_inferrer.dart' show TypeInferrerImpl;
import 'type_schema.dart' show UnknownType;
abstract class InferenceVisitor {
/// Performs type inference on the given [expression].
///
/// [typeContext] is the expected type of the expression, based on surrounding
/// code. [typeNeeded] indicates whether it is necessary to compute the
/// actual type of the expression. If [typeNeeded] is `true`,
/// [ExpressionInferenceResult.inferredType] is the actual type of the
/// expression; otherwise the [UnknownType].
///
/// Derived classes should override this method with logic that dispatches on
/// the expression type and calls the appropriate specialized "infer" method.
ExpressionInferenceResult inferExpression(
Expression expression, DartType typeContext,
{bool isVoidAllowed = false, bool forEffect = false});
/// Performs type inference on the given [statement].
///
/// If [closureContext] is not null, the [statement] is inferred using
/// [closureContext] as the current context.
StatementInferenceResult inferStatement(Statement statement,
[ClosureContext? closureContext]);
/// Performs type inference on the given [initializer].
InitializerInferenceResult inferInitializer(Initializer initializer);
}
class InferenceVisitorImpl extends InferenceVisitorBase
with
TypeAnalyzer<
TreeNode,
Statement,
Expression,
VariableDeclaration,
DartType,
Pattern,
InvalidExpression,
DartType,
StructuralParameter,
TypeDeclarationType,
TypeDeclaration>,
StackChecker
implements
ExpressionVisitor1<ExpressionInferenceResult, DartType>,
StatementVisitor<StatementInferenceResult>,
InitializerVisitor<InitializerInferenceResult>,
PatternVisitor1<PatternResult<DartType>, SharedMatchContext>,
InferenceVisitor {
/// Debug-only: if `true`, manipulations of [_rewriteStack] performed by
/// [popRewrite] and [pushRewrite] will be printed.
static const bool _debugRewriteStack = false;
Class? mapEntryClass;
@override
final OperationsCfe operations;
/// Context information for the current closure, or `null` if we are not
/// inside a closure.
ClosureContext? _closureContext;
/// If a switch statement is being visited and the type being switched on is a
/// (possibly nullable) enumerated type, the set of enum values for which no
/// case head has been seen yet; otherwise `null`.
///
/// Enum values are represented by the [Field] object they are desugared into.
/// If the type being switched on is nullable, then this set also includes a
/// value of `null` if no case head has been seen yet that handles `null`.
Set<Field?>? _enumFields;
/// Stack for obtaining rewritten expressions and statements. After
/// [dispatchExpression] or [dispatchStatement] visits a node for type
/// inference, the visited node (which may have been changed by the inference
/// process) is pushed onto this stack. Later, during the processing of the
/// enclosing node, the visited node is popped off the stack again, and the
/// enclosing node is updated to point to the new, rewritten node.
///
/// The stack sometimes contains `null`s. These account for situations where
/// it's necessary to push a value onto the stack to balance a later pop, but
/// there is no suitable expression or statement to push.
final List<Object> _rewriteStack = [];
@override
final TypeAnalyzerOptions options;
final ConstructorDeclaration? constructorDeclaration;
@override
late final SharedTypeAnalyzerErrors errors = new SharedTypeAnalyzerErrors(
visitor: this,
helper: helper,
uri: uriForInstrumentation,
coreTypes: coreTypes);
/// The innermost cascade whose expressions are currently being visited, or
/// `null` if no cascade's expressions are currently being visited.
Cascade? _enclosingCascade;
/// Set to `true` when we are inside a try-statement or a local function.
///
/// This is used to optimize the encoding of [AssignedVariablePattern]. When
/// a pattern assignment occurs in a try block or a local function, a
/// partially matched pattern is observable, since exceptions occurring during
/// the matching can be caught.
// TODO(johnniwinther): This can be improved by detecting whether the assigned
// variable was declared outside the try statement or local function.
bool _inTryOrLocalFunction = false;
InferenceVisitorImpl(TypeInferrerImpl inferrer, InferenceHelper helper,
this.constructorDeclaration, this.operations)
: options = new TypeAnalyzerOptions(
nullSafetyEnabled: true,
patternsEnabled:
inferrer.libraryBuilder.libraryFeatures.patterns.isEnabled,
inferenceUpdate3Enabled: inferrer
.libraryBuilder.libraryFeatures.inferenceUpdate3.isEnabled),
super(inferrer, helper);
@override
int get stackHeight => _rewriteStack.length;
@override
Object? lookupStack(int index) =>
_rewriteStack[_rewriteStack.length - index - 1];
/// Used to report an internal error encountered in the stack listener.
@override
// Coverage-ignore(suite): Not run.
Never internalProblem(Message message, int charOffset, Uri uri) {
return problems.internalProblem(message, charOffset, uri);
}
/// Checks that [base] is a valid base stack height for a call to
/// [checkStack].
///
/// This can be used to initialize a stack base for subsequent calls to
/// [checkStack]. For instance:
///
/// int? stackBase;
/// // Set up the current stack height as the stack base.
/// assert(checkStackBase(node, stackBase = stackHeight));
/// ...
/// // Check that the stack is empty, relative to the stack base.
/// assert(checkStack(node, []));
///
/// or
///
/// int? stackBase;
/// // Assert that the current stack height is at least 4 and set
/// // the stack height - 4 up as the stack base.
/// assert(checkStackBase(node, stackBase = stackHeight - 4));
/// ...
/// // Check that the stack contains a single `Expression` element,
/// // relative to the stack base.
/// assert(checkStack(node, [ValuesKind.Expression]));
///
bool checkStackBase(TreeNode? node, int base) {
return checkStackBaseStateForAssert(helper.uri, node?.fileOffset, base);
}
/// Checks the top of the current stack against [kinds]. If a mismatch is
/// found, a top of the current stack is print along with the expected [kinds]
/// marking the frames that don't match, and throws an exception.
///
/// [base] it is used as the reference stack base height at which the [kinds]
/// are expected to occur, which allows for checking that the stack is empty
/// wrt. the stack base height.
///
/// Use this in assert statements like
///
/// assert(checkState(node,
/// [ValueKind.Expression, ValueKind.StatementOrNull],
/// base: stackBase));
///
/// to document the expected stack and get earlier errors on unexpected stack
/// content.
bool checkStack(TreeNode? node, int? base, List<ValueKind> kinds) {
return checkStackStateForAssert(helper.uri, node?.fileOffset, kinds,
base: base);
}
ClosureContext get closureContext => _closureContext!;
@override
StatementInferenceResult inferStatement(Statement statement,
[ClosureContext? closureContext]) {
ClosureContext? oldClosureContext = _closureContext;
if (closureContext != null) {
_closureContext = closureContext;
}
registerIfUnreachableForTesting(statement);
// For full (non-top level) inference, we need access to the
// ExpressionGeneratorHelper so that we can perform error recovery.
StatementInferenceResult result;
if (statement is InternalStatement) {
result = statement.acceptInference(this);
} else {
result = statement.accept(this);
}
_closureContext = oldClosureContext;
return result;
}
ExpressionInferenceResult _inferExpression(
Expression expression, DartType typeContext,
{bool isVoidAllowed = false, bool forEffect = false}) {
registerIfUnreachableForTesting(expression);
ExpressionInferenceResult result;
if (expression is ExpressionJudgment) {
result = expression.acceptInference(this, typeContext);
} else if (expression is InternalExpression) {
result = expression.acceptInference(this, typeContext);
} else {
result = expression.accept1(this, typeContext);
}
DartType inferredType = result.inferredType;
if (inferredType is VoidType && !isVoidAllowed) {
if (expression.parent is! ArgumentsImpl) {
helper.addProblem(
messageVoidExpression, expression.fileOffset, noLength);
}
}
if (coreTypes.isBottom(result.inferredType)) {
flowAnalysis.handleExit();
if (shouldThrowUnsoundnessException &&
// Coverage-ignore(suite): Not run.
// Don't throw on expressions that inherently return the bottom type.
!(result.nullAwareAction is Throw ||
result.nullAwareAction is Rethrow ||
result.nullAwareAction is InvalidExpression)) {
// Coverage-ignore-block(suite): Not run.
Expression replacement = createLet(
createVariable(result.expression, result.inferredType),
createReachabilityError(
expression.fileOffset, messageNeverValueError));
flowAnalysis.forwardExpression(replacement, result.expression);
result =
new ExpressionInferenceResult(result.inferredType, replacement);
}
}
return result;
}
@override
ExpressionInferenceResult inferExpression(
Expression expression, DartType typeContext,
{bool isVoidAllowed = false, bool forEffect = false}) {
ExpressionInferenceResult result = _inferExpression(expression, typeContext,
isVoidAllowed: isVoidAllowed, forEffect: forEffect);
return result.stopShorting();
}
@override
InitializerInferenceResult inferInitializer(Initializer initializer) {
InitializerInferenceResult inferenceResult;
if (initializer is InitializerJudgment) {
inferenceResult = initializer.acceptInference(this);
} else {
inferenceResult = initializer.accept(this);
}
return inferenceResult;
}
ExpressionInferenceResult inferNullAwareExpression(
Expression expression, DartType typeContext,
{bool isVoidAllowed = false, bool forEffect = false}) {
ExpressionInferenceResult result = _inferExpression(expression, typeContext,
isVoidAllowed: isVoidAllowed, forEffect: forEffect);
return result;
}
void inferSyntheticVariable(VariableDeclarationImpl variable) {
assert(variable.isImplicitlyTyped);
assert(variable.initializer != null);
ExpressionInferenceResult result = inferExpression(
variable.initializer!, const UnknownType(),
isVoidAllowed: true);
variable.initializer = result.expression..parent = variable;
DartType inferredType =
inferDeclarationType(result.inferredType, forSyntheticVariable: true);
instrumentation?.record(uriForInstrumentation, variable.fileOffset, 'type',
new InstrumentationValueForType(inferredType));
variable.type = inferredType;
}
Link<NullAwareGuard> inferSyntheticVariableNullAware(
VariableDeclarationImpl variable) {
assert(variable.isImplicitlyTyped);
assert(variable.initializer != null);
ExpressionInferenceResult result = inferNullAwareExpression(
variable.initializer!, const UnknownType(),
isVoidAllowed: true);
Link<NullAwareGuard> nullAwareGuards = result.nullAwareGuards;
variable.initializer = result.nullAwareAction..parent = variable;
DartType inferredType =
inferDeclarationType(result.inferredType, forSyntheticVariable: true);
instrumentation?.record(uriForInstrumentation, variable.fileOffset, 'type',
new InstrumentationValueForType(inferredType));
variable.type = inferredType;
return nullAwareGuards;
}
// Coverage-ignore(suite): Not run.
/// Computes uri and offset for [node] for internal errors in a way that is
/// safe for both top-level and full inference.
UriOffset _computeUriOffset(TreeNode node) {
Uri uri = helper.uri;
int fileOffset = node.fileOffset;
return new UriOffset(uri, fileOffset);
}
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult _unhandledExpression(
Expression node, DartType typeContext) {
UriOffset uriOffset = _computeUriOffset(node);
problems.unhandled("$node (${node.runtimeType})", "InferenceVisitor",
uriOffset.fileOffset, uriOffset.uri);
}
@override
ExpressionInferenceResult visitBlockExpression(
BlockExpression node, DartType typeContext) {
// This is only used for error cases. The spec doesn't use this and
// therefore doesn't specify the type context for the subterms.
StatementInferenceResult bodyResult = inferStatement(node.body);
if (bodyResult.hasChanged) {
// Coverage-ignore-block(suite): Not run.
node.body = (bodyResult.statement as Block)..parent = node;
}
ExpressionInferenceResult valueResult =
inferExpression(node.value, const UnknownType(), isVoidAllowed: true);
node.value = valueResult.expression..parent = node;
return new ExpressionInferenceResult(valueResult.inferredType, node);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitConstantExpression(
ConstantExpression node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitDynamicGet(
DynamicGet node, DartType typeContext) {
// The node has already been inferred, for instance as part of a for-in
// loop, so just compute the result type.
DartType resultType;
switch (node.kind) {
case DynamicAccessKind.Dynamic:
resultType = const DynamicType();
break;
case DynamicAccessKind.Never:
// Coverage-ignore(suite): Not run.
resultType = NeverType.fromNullability(Nullability.nonNullable);
break;
case DynamicAccessKind.Invalid:
case DynamicAccessKind.Unresolved:
resultType = const InvalidType();
break;
}
return new ExpressionInferenceResult(resultType, node);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitInstanceGet(
InstanceGet node, DartType typeContext) {
// This node is created as part of a lowering and doesn't need inference.
return new ExpressionInferenceResult(
node.getStaticType(staticTypeContext), node);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitInstanceTearOff(
InstanceTearOff node, DartType typeContext) {
// This node is created as part of a lowering and doesn't need inference.
return new ExpressionInferenceResult(
node.getStaticType(staticTypeContext), node);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitDynamicInvocation(
DynamicInvocation node, DartType typeContext) {
// This node is created as part of a lowering and doesn't need inference.
return new ExpressionInferenceResult(
node.getStaticType(staticTypeContext), node);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitDynamicSet(
DynamicSet node, DartType typeContext) {
// This node is created as part of a lowering and doesn't need inference.
return new ExpressionInferenceResult(
node.getStaticType(staticTypeContext), node);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitEqualsCall(
EqualsCall node, DartType typeContext) {
// This node is created as part of a lowering and doesn't need inference.
return new ExpressionInferenceResult(
node.getStaticType(staticTypeContext), node);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitEqualsNull(
EqualsNull node, DartType typeContext) {
// This node is created as part of a lowering and doesn't need inference.
return new ExpressionInferenceResult(
node.getStaticType(staticTypeContext), node);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitFunctionInvocation(
FunctionInvocation node, DartType typeContext) {
// This node is created as part of a lowering and doesn't need inference.
return new ExpressionInferenceResult(
node.getStaticType(staticTypeContext), node);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitInstanceInvocation(
InstanceInvocation node, DartType typeContext) {
// This node is created as part of a lowering and doesn't need inference.
return new ExpressionInferenceResult(
node.getStaticType(staticTypeContext), node);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitInstanceGetterInvocation(
InstanceGetterInvocation node, DartType typeContext) {
// This node is created as part of a lowering and doesn't need inference.
return new ExpressionInferenceResult(
node.getStaticType(staticTypeContext), node);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitInstanceSet(
InstanceSet node, DartType typeContext) {
// This node is created as part of a lowering and doesn't need inference.
return new ExpressionInferenceResult(
node.getStaticType(staticTypeContext), node);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitLocalFunctionInvocation(
LocalFunctionInvocation node, DartType typeContext) {
// This node is created as part of a lowering and doesn't need inference.
return new ExpressionInferenceResult(
node.getStaticType(staticTypeContext), node);
}
@override
ExpressionInferenceResult visitStaticTearOff(
StaticTearOff node, DartType typeContext) {
ensureMemberType(node.target);
DartType type =
node.target.function.computeFunctionType(Nullability.nonNullable);
return instantiateTearOff(type, typeContext, node);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitFunctionTearOff(
FunctionTearOff node, DartType typeContext) {
// This node is created as part of a lowering and doesn't need inference.
return new ExpressionInferenceResult(
node.getStaticType(staticTypeContext), node);
}
@override
ExpressionInferenceResult visitFileUriExpression(
FileUriExpression node, DartType typeContext) {
ExpressionInferenceResult result =
inferExpression(node.expression, typeContext);
node.expression = result.expression..parent = node;
return new ExpressionInferenceResult(result.inferredType, node);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitInstanceCreation(
InstanceCreation node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitConstructorTearOff(
ConstructorTearOff node, DartType typeContext) {
ensureMemberType(node.target);
DartType type =
node.target.function!.computeFunctionType(Nullability.nonNullable);
return instantiateTearOff(type, typeContext, node);
}
@override
ExpressionInferenceResult visitRedirectingFactoryTearOff(
RedirectingFactoryTearOff node, DartType typeContext) {
DartType type =
node.target.function.computeFunctionType(Nullability.nonNullable);
return instantiateTearOff(type, typeContext, node);
}
@override
ExpressionInferenceResult visitTypedefTearOff(
TypedefTearOff node, DartType typeContext) {
ExpressionInferenceResult expressionResult = inferExpression(
node.expression, const UnknownType(),
isVoidAllowed: true);
node.expression = expressionResult.expression..parent = node;
assert(
expressionResult.inferredType is FunctionType,
// Coverage-ignore(suite): Not run.
"Expected a FunctionType from tearing off a constructor from "
"a typedef, but got '${expressionResult.inferredType.runtimeType}'.");
FunctionType expressionType = expressionResult.inferredType as FunctionType;
assert(expressionType.typeParameters.length == node.typeArguments.length);
FunctionType resultType = FunctionTypeInstantiator.instantiate(
expressionType, node.typeArguments);
FreshStructuralParameters freshStructuralParameters =
getFreshStructuralParameters(node.structuralParameters);
resultType =
freshStructuralParameters.substitute(resultType) as FunctionType;
resultType = new FunctionType(resultType.positionalParameters,
resultType.returnType, resultType.declaredNullability,
namedParameters: resultType.namedParameters,
typeParameters: freshStructuralParameters.freshTypeParameters,
requiredParameterCount: resultType.requiredParameterCount);
ExpressionInferenceResult inferredResult =
instantiateTearOff(resultType, typeContext, node);
return ensureAssignableResult(typeContext, inferredResult);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitListConcatenation(
ListConcatenation node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitMapConcatenation(
MapConcatenation node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitSetConcatenation(
SetConcatenation node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
// Coverage-ignore(suite): Not run.
StatementInferenceResult _unhandledStatement(Statement node) {
UriOffset uriOffset = _computeUriOffset(node);
return problems.unhandled("${node.runtimeType}", "InferenceVisitor",
uriOffset.fileOffset, uriOffset.uri);
}
@override
// Coverage-ignore(suite): Not run.
StatementInferenceResult visitAssertBlock(AssertBlock node) {
return _unhandledStatement(node);
}
@override
// Coverage-ignore(suite): Not run.
StatementInferenceResult visitTryCatch(TryCatch node) {
return _unhandledStatement(node);
}
@override
// Coverage-ignore(suite): Not run.
StatementInferenceResult visitTryFinally(TryFinally node) {
return _unhandledStatement(node);
}
// Coverage-ignore(suite): Not run.
Never _unhandledInitializer(Initializer node) {
problems.unhandled("${node.runtimeType}", "InferenceVisitor",
node.fileOffset, node.location!.file);
}
@override
// Coverage-ignore(suite): Not run.
InitializerInferenceResult visitInvalidInitializer(InvalidInitializer node) {
_unhandledInitializer(node);
}
@override
// Coverage-ignore(suite): Not run.
InitializerInferenceResult visitLocalInitializer(LocalInitializer node) {
_unhandledInitializer(node);
}
@override
ExpressionInferenceResult visitInvalidExpression(
InvalidExpression node, DartType typeContext) {
if (node.expression != null) {
ExpressionInferenceResult result =
inferExpression(node.expression!, typeContext, isVoidAllowed: true);
node.expression = result.expression..parent = node;
}
return new ExpressionInferenceResult(const InvalidType(), node);
}
@override
ExpressionInferenceResult visitInstantiation(
Instantiation node, DartType typeContext) {
ExpressionInferenceResult operandResult = inferExpression(
node.expression, const UnknownType(),
isVoidAllowed: true);
Expression operand = operandResult.expression;
DartType operandType = operandResult.inferredType;
if (operandType is! FunctionType) {
ObjectAccessTarget callMember = findInterfaceMember(
operandType, callName, operand.fileOffset,
isSetter: false, includeExtensionMethods: true);
switch (callMember.kind) {
case ObjectAccessTargetKind.instanceMember:
Member? target = callMember.classMember;
if (target is Procedure && target.kind == ProcedureKind.Method) {
operandType = callMember.getGetterType(this);
operand = new InstanceTearOff(
InstanceAccessKind.Instance, operand, callName,
interfaceTarget: target, resultType: operandType)
..fileOffset = operand.fileOffset;
}
break;
case ObjectAccessTargetKind.extensionMember:
case ObjectAccessTargetKind.extensionTypeMember:
if (callMember.tearoffTarget != null &&
callMember.declarationMethodKind == ClassMemberKind.Method) {
operandType = callMember.getGetterType(this);
operand = new StaticInvocation(
callMember.tearoffTarget as Procedure,
new Arguments(<Expression>[operand],
types: callMember.receiverTypeArguments)
..fileOffset = operand.fileOffset)
..fileOffset = operand.fileOffset;
}
break;
case ObjectAccessTargetKind.nullableInstanceMember:
case ObjectAccessTargetKind.superMember:
case ObjectAccessTargetKind.objectMember:
case ObjectAccessTargetKind.nullableCallFunction:
case ObjectAccessTargetKind.nullableExtensionMember:
case ObjectAccessTargetKind.dynamic:
case ObjectAccessTargetKind.never:
case ObjectAccessTargetKind.invalid:
case ObjectAccessTargetKind.missing:
case ObjectAccessTargetKind.ambiguous:
case ObjectAccessTargetKind.callFunction:
case ObjectAccessTargetKind.recordIndexed:
case ObjectAccessTargetKind.nullableRecordIndexed:
case ObjectAccessTargetKind.nullableRecordNamed:
case ObjectAccessTargetKind.recordNamed:
case ObjectAccessTargetKind.nullableExtensionTypeMember:
case ObjectAccessTargetKind.extensionTypeRepresentation:
// Coverage-ignore(suite): Not run.
case ObjectAccessTargetKind.nullableExtensionTypeRepresentation:
break;
}
}
node.expression = operand..parent = node;
Expression result = node;
DartType resultType = const InvalidType();
if (operandType is FunctionType) {
if (operandType.typeParameters.length == node.typeArguments.length) {
checkBoundsInInstantiation(
operandType, node.typeArguments, node.fileOffset,
inferred: false);
if (operandType.isPotentiallyNullable) {
result = helper.buildProblem(
templateInstantiationNullableGenericFunctionType
.withArguments(operandType),
node.fileOffset,
noLength);
} else {
resultType = FunctionTypeInstantiator.instantiate(
operandType, node.typeArguments);
}
} else {
if (operandType.typeParameters.isEmpty) {
result = helper.buildProblem(
templateInstantiationNonGenericFunctionType
.withArguments(operandType),
node.fileOffset,
noLength);
} else if (operandType.typeParameters.length >
node.typeArguments.length) {
result = helper.buildProblem(
templateInstantiationTooFewArguments.withArguments(
operandType.typeParameters.length, node.typeArguments.length),
node.fileOffset,
noLength);
} else if (operandType.typeParameters.length <
node.typeArguments.length) {
result = helper.buildProblem(
templateInstantiationTooManyArguments.withArguments(
operandType.typeParameters.length, node.typeArguments.length),
node.fileOffset,
noLength);
}
}
} else if (operandType is! InvalidType) {
result = helper.buildProblem(
templateInstantiationNonGenericFunctionType
.withArguments(operandType),
node.fileOffset,
noLength);
}
return new ExpressionInferenceResult(resultType, result);
}
@override
ExpressionInferenceResult visitIntLiteral(
IntLiteral node, DartType typeContext) {
return new ExpressionInferenceResult(
coreTypes.intRawType(Nullability.nonNullable), node);
}
@override
ExpressionInferenceResult visitAsExpression(
AsExpression node, DartType typeContext) {
ExpressionInferenceResult operandResult =
inferExpression(node.operand, const UnknownType(), isVoidAllowed: true);
node.operand = operandResult.expression..parent = node;
flowAnalysis.asExpression_end(node.operand, node.type);
return new ExpressionInferenceResult(node.type, node);
}
@override
InitializerInferenceResult visitAssertInitializer(AssertInitializer node) {
StatementInferenceResult result = inferStatement(node.statement);
if (result.hasChanged) {
// Coverage-ignore-block(suite): Not run.
node.statement = (result.statement as AssertStatement)..parent = node;
}
return const SuccessfulInitializerInferenceResult();
}
@override
StatementInferenceResult visitAssertStatement(AssertStatement node) {
flowAnalysis.assert_begin();
InterfaceType expectedType = coreTypes.boolRawType(Nullability.nonNullable);
ExpressionInferenceResult conditionResult =
inferExpression(node.condition, expectedType, isVoidAllowed: true);
Expression condition =
ensureAssignableResult(expectedType, conditionResult).expression;
node.condition = condition..parent = node;
flowAnalysis.assert_afterCondition(node.condition);
if (node.message != null) {
ExpressionInferenceResult messageResult = inferExpression(
node.message!, const UnknownType(),
isVoidAllowed: true);
node.message = messageResult.expression..parent = node;
}
flowAnalysis.assert_end();
return const StatementInferenceResult();
}
bool _isIncompatibleWithAwait(DartType type) {
if (isNullableTypeConstructorApplication(type)) {
return _isIncompatibleWithAwait(
computeTypeWithoutNullabilityMarker(type));
} else {
switch (type) {
case ExtensionType():
return typeSchemaEnvironment.hierarchy
.getExtensionTypeAsInstanceOfClass(
type, coreTypes.futureClass) ==
null;
case TypeParameterType():
return _isIncompatibleWithAwait(type.parameter.bound);
case StructuralParameterType():
// Coverage-ignore(suite): Not run.
return _isIncompatibleWithAwait(type.parameter.bound);
case IntersectionType():
return _isIncompatibleWithAwait(type.right);
case DynamicType():
case VoidType():
case FutureOrType():
case InterfaceType():
case TypedefType():
case FunctionType():
case RecordType():
case NullType():
case NeverType():
case AuxiliaryType():
case InvalidType():
return false;
}
}
}
@override
ExpressionInferenceResult visitAwaitExpression(
AwaitExpression node, DartType typeContext) {
if (typeContext is DynamicType) {
typeContext = const UnknownType();
}
typeContext = wrapFutureOrType(typeContext);
ExpressionInferenceResult operandResult =
inferExpression(node.operand, typeContext, isVoidAllowed: false);
DartType operandType = operandResult.inferredType;
DartType flattenType = typeSchemaEnvironment.flatten(operandType);
if (_isIncompatibleWithAwait(operandType)) {
Expression wrapped = operandResult.expression;
node.operand = helper.wrapInProblem(
wrapped, messageAwaitOfExtensionTypeNotFuture, wrapped.fileOffset, 1);
wrapped.parent = node.operand;
} else {
node.operand = operandResult.expression..parent = node;
}
DartType runtimeCheckType = new InterfaceType(
coreTypes.futureClass, Nullability.nonNullable, [flattenType]);
if (!typeSchemaEnvironment.isSubtypeOf(
operandType, runtimeCheckType, SubtypeCheckMode.withNullabilities)) {
node.runtimeCheckType = runtimeCheckType;
}
return new ExpressionInferenceResult(flattenType, node);
}
List<Statement>? _visitStatements<T extends Statement>(List<T> statements) {
List<Statement>? result;
for (int index = 0; index < statements.length; index++) {
T statement = statements[index];
StatementInferenceResult statementResult = inferStatement(statement);
if (statementResult.hasChanged) {
if (result == null) {
result = <T>[];
result.addAll(statements.sublist(0, index));
}
if (statementResult.statementCount == 1) {
result.add(statementResult.statement);
} else {
result.addAll(statementResult.statements);
}
} else if (result != null) {
result.add(statement);
}
}
return result;
}
@override
StatementInferenceResult visitBlock(Block node) {
registerIfUnreachableForTesting(node);
List<Statement>? result = _visitStatements<Statement>(node.statements);
if (result != null) {
Block block = new Block(result)..fileOffset = node.fileOffset;
libraryBuilder.loader.dataForTesting
// Coverage-ignore(suite): Not run.
?.registerAlias(node, block);
return new StatementInferenceResult.single(block);
} else {
return const StatementInferenceResult();
}
}
@override
ExpressionInferenceResult visitBoolLiteral(
BoolLiteral node, DartType typeContext) {
flowAnalysis.booleanLiteral(node, node.value);
return new ExpressionInferenceResult(
coreTypes.boolRawType(Nullability.nonNullable), node);
}
@override
StatementInferenceResult visitBreakStatement(
covariant BreakStatementImpl node) {
// TODO(johnniwinther): Refactor break/continue encoding.
assert(node.targetStatement != null);
if (node.isContinue) {
flowAnalysis.handleContinue(node.targetStatement);
} else {
flowAnalysis.handleBreak(node.targetStatement);
}
return const StatementInferenceResult();
}
ExpressionInferenceResult visitCascade(Cascade node, DartType typeContext) {
ExpressionInferenceResult result = inferExpression(
node.variable.initializer!, typeContext,
isVoidAllowed: false);
node.variable.initializer = result.expression..parent = node.variable;
node.variable.type = result.inferredType;
flowAnalysis.cascadeExpression_afterTarget(
result.expression, result.inferredType,
isNullAware: node.isNullAware);
NullAwareGuard? nullAwareGuard;
if (node.isNullAware) {
nullAwareGuard = createNullAwareGuard(node.variable);
}
Cascade? previousEnclosingCascade = _enclosingCascade;
_enclosingCascade = node;
List<ExpressionInferenceResult> expressionResults =
<ExpressionInferenceResult>[];
for (Expression expression in node.expressions) {
expressionResults.add(inferExpression(expression, const UnknownType(),
isVoidAllowed: true, forEffect: true));
}
List<Statement> body = [];
for (int index = 0; index < expressionResults.length; index++) {
body.add(_createExpressionStatement(expressionResults[index].expression));
}
_enclosingCascade = previousEnclosingCascade;
Expression replacement = _createBlockExpression(node.variable.fileOffset,
_createBlock(body), createVariableGet(node.variable));
if (node.isNullAware) {
replacement =
nullAwareGuard!.createExpression(result.inferredType, replacement);
} else {
replacement = new Let(node.variable, replacement)
..fileOffset = node.fileOffset;
}
flowAnalysis.cascadeExpression_end(replacement);
return new ExpressionInferenceResult(result.inferredType, replacement);
}
@override
PropertyTarget<Expression> computePropertyTarget(Expression target) {
if (_enclosingCascade case Cascade(:var variable)
when target is VariableGet && target.variable == variable) {
// `target` is an implicit reference to the target of a cascade
// expression; flow analysis uses `CascadePropertyTarget` to represent
// this situation.
return CascadePropertyTarget.singleton;
} else {
// `target` is an ordinary expression.
return new ExpressionPropertyTarget(target);
}
}
Block _createBlock(List<Statement> statements) {
return new Block(statements);
}
BlockExpression _createBlockExpression(
int fileOffset, Block body, Expression value) {
assert(fileOffset != TreeNode.noOffset);
return new BlockExpression(body, value)..fileOffset = fileOffset;
}
ExpressionStatement _createExpressionStatement(Expression expression) {
assert(expression.fileOffset != TreeNode.noOffset);
return new ExpressionStatement(expression)
..fileOffset = expression.fileOffset;
}
@override
ExpressionInferenceResult visitConditionalExpression(
ConditionalExpression node, DartType typeContext) {
flowAnalysis.conditional_conditionBegin();
InterfaceType expectedType = coreTypes.boolRawType(Nullability.nonNullable);
ExpressionInferenceResult conditionResult =
inferExpression(node.condition, expectedType, isVoidAllowed: true);
Expression condition =
ensureAssignableResult(expectedType, conditionResult).expression;
node.condition = condition..parent = node;
flowAnalysis.conditional_thenBegin(node.condition, node);
bool isThenReachable = flowAnalysis.isReachable;
// A conditional expression `E` of the form `b ? e1 : e2` with context
// type `K` is analyzed as follows:
//
// - Let `T1` be the type of `e1` inferred with context type `K`
ExpressionInferenceResult thenResult =
inferExpression(node.then, typeContext, isVoidAllowed: true);
node.then = thenResult.expression..parent = node;
registerIfUnreachableForTesting(node.then, isReachable: isThenReachable);
DartType t1 = thenResult.inferredType;
// - Let `T2` be the type of `e2` inferred with context type `K`
flowAnalysis.conditional_elseBegin(node.then, thenResult.inferredType);
bool isOtherwiseReachable = flowAnalysis.isReachable;
ExpressionInferenceResult otherwiseResult =
inferExpression(node.otherwise, typeContext, isVoidAllowed: true);
node.otherwise = otherwiseResult.expression..parent = node;
registerIfUnreachableForTesting(node.otherwise,
isReachable: isOtherwiseReachable);
DartType t2 = otherwiseResult.inferredType;
// - Let `T` be `UP(T1, T2)`
DartType t = typeSchemaEnvironment.getStandardUpperBound(t1, t2);
// - Let `S` be the greatest closure of `K`
DartType s = computeGreatestClosure(typeContext);
DartType inferredType;
// If `inferenceUpdate3` is not enabled, then the type of `E` is `T`.
if (!libraryBuilder.libraryFeatures.inferenceUpdate3.isEnabled) {
inferredType = t;
} else
// - If `T <: S` then the type of `E` is `T`
if (typeSchemaEnvironment.isSubtypeOf(
t, s, SubtypeCheckMode.withNullabilities)) {
inferredType = t;
} else
// - Otherwise, if `T1 <: S` and `T2 <: S`, then the type of `E` is `S`
if (typeSchemaEnvironment.isSubtypeOf(
t1, s, SubtypeCheckMode.withNullabilities) &&
typeSchemaEnvironment.isSubtypeOf(
t2, s, SubtypeCheckMode.withNullabilities)) {
inferredType = s;
} else
// - Otherwise, the type of `E` is `T`
{
inferredType = t;
}
flowAnalysis.conditional_end(
node, inferredType, node.otherwise, otherwiseResult.inferredType);
node.staticType = inferredType;
return new ExpressionInferenceResult(inferredType, node);
}
@override
ExpressionInferenceResult visitConstructorInvocation(
ConstructorInvocation node, DartType typeContext) {
ensureMemberType(node.target);
bool hadExplicitTypeArguments = hasExplicitTypeArguments(node.arguments);
FunctionType functionType =
node.target.function.computeThisFunctionType(Nullability.nonNullable);
InvocationInferenceResult result = inferInvocation(
this,
typeContext,
node.fileOffset,
new InvocationTargetFunctionType(functionType),
node.arguments as ArgumentsImpl,
isConst: node.isConst,
staticTarget: node.target);
SourceLibraryBuilder library = libraryBuilder;
if (!hadExplicitTypeArguments) {
library.checkBoundsInConstructorInvocation(
node, typeSchemaEnvironment, helper.uri,
inferred: true);
}
return new ExpressionInferenceResult(
result.inferredType, result.applyResult(node));
}
@override
StatementInferenceResult visitContinueSwitchStatement(
ContinueSwitchStatement node) {
flowAnalysis.handleContinue(node.target.body);
return const StatementInferenceResult();
}
ExpressionInferenceResult visitExtensionTearOff(
ExtensionTearOff node, DartType typeContext) {
FunctionType calleeType =
node.target.function.computeFunctionType(Nullability.nonNullable);
TypeArgumentsInfo typeArgumentsInfo = getTypeArgumentsInfo(node.arguments);
InvocationInferenceResult result = inferInvocation(
this,
typeContext,
node.fileOffset,
new InvocationTargetFunctionType(calleeType),
node.arguments as ArgumentsImpl,
staticTarget: node.target);
StaticInvocation replacement =
new StaticInvocation(node.target, node.arguments);
libraryBuilder.checkBoundsInStaticInvocation(
replacement, typeSchemaEnvironment, helper.uri, typeArgumentsInfo);
return instantiateTearOff(
result.inferredType, typeContext, result.applyResult(replacement));
}
ExpressionInferenceResult visitExtensionSet(
ExtensionSet node, DartType typeContext) {
ExpressionInferenceResult receiverResult = inferExpression(
node.receiver, const UnknownType(),
isVoidAllowed: false);
List<DartType> extensionTypeArguments = computeExtensionTypeArgument(
node.extension, node.explicitTypeArguments, receiverResult.inferredType,
treeNodeForTesting: node);
DartType receiverType =
getExtensionReceiverType(node.extension, extensionTypeArguments);
Expression receiver =
ensureAssignableResult(receiverType, receiverResult).expression;
ObjectAccessTarget target = new ExtensionAccessTarget(receiverType,
node.target, null, ClassMemberKind.Setter, extensionTypeArguments);
DartType valueType = target.getSetterType(this);
ExpressionInferenceResult valueResult =
inferExpression(node.value, valueType, isVoidAllowed: false);
valueResult = ensureAssignableResult(valueType, valueResult);
Expression value = valueResult.expression;
VariableDeclaration? valueVariable;
if (node.forEffect) {
// No need for value variable.
} else {
valueVariable = createVariable(value, valueResult.inferredType);
value = createVariableGet(valueVariable);
}
VariableDeclaration? receiverVariable;
if (node.forEffect || isPureExpression(receiver)) {
// No need for receiver variable.
} else {
receiverVariable = createVariable(receiver, receiverResult.inferredType);
receiver = createVariableGet(receiverVariable);
}
Expression assignment = new StaticInvocation(
node.target,
new Arguments(<Expression>[receiver, value],
types: extensionTypeArguments)
..fileOffset = node.fileOffset)
..fileOffset = node.fileOffset;
Expression replacement;
if (node.forEffect) {
assert(receiverVariable == null);
assert(valueVariable == null);
replacement = assignment;
} else {
assert(valueVariable != null);
VariableDeclaration assignmentVariable =
createVariable(assignment, const VoidType());
replacement = createLet(valueVariable!,
createLet(assignmentVariable, createVariableGet(valueVariable)));
if (receiverVariable != null) {
replacement = createLet(receiverVariable, replacement);
}
}
replacement.fileOffset = node.fileOffset;
return new ExpressionInferenceResult(valueResult.inferredType, replacement);
}
ExpressionInferenceResult visitCompoundExtensionSet(
CompoundExtensionSet node, DartType typeContext) {
ExpressionInferenceResult receiverResult = inferExpression(
node.receiver, const UnknownType(),
isVoidAllowed: false);
List<DartType> extensionTypeArguments = computeExtensionTypeArgument(
node.extension, node.explicitTypeArguments, receiverResult.inferredType,
treeNodeForTesting: node);
DartType receiverType =
getExtensionReceiverType(node.extension, extensionTypeArguments);
Expression receiver =
ensureAssignableResult(receiverType, receiverResult).expression;
VariableDeclaration? receiverVariable;
Expression readReceiver;
Expression writeReceiver;
if (isPureExpression(receiver)) {
// Coverage-ignore-block(suite): Not run.
readReceiver = receiver;
writeReceiver = clonePureExpression(receiver);
} else {
receiverVariable = createVariable(receiver, receiverType);
readReceiver = createVariableGet(receiverVariable);
writeReceiver = createVariableGet(receiverVariable);
}
ObjectAccessTarget readTarget = node.getter == null
? const ObjectAccessTarget.missing()
: new ExtensionAccessTarget(receiverType, node.getter!, null,
ClassMemberKind.Getter, extensionTypeArguments);
DartType readType = readTarget.getGetterType(this);
Expression read;
if (readTarget.isMissing) {
// Coverage-ignore-block(suite): Not run.
read = createMissingPropertyGet(
node.readOffset, readType, node.propertyName,
receiver: readReceiver);
} else {
assert(readTarget.isExtensionMember);
read = new StaticInvocation(
readTarget.member as Procedure,
new Arguments(<Expression>[
readReceiver,
], types: readTarget.receiverTypeArguments)
..fileOffset = node.readOffset)
..fileOffset = node.readOffset;
}
ObjectAccessTarget writeTarget = node.setter == null
? const ObjectAccessTarget.missing()
: new ExtensionAccessTarget(receiverType, node.setter!, null,
ClassMemberKind.Setter, extensionTypeArguments);
DartType valueType = writeTarget.getSetterType(this);
ExpressionInferenceResult binaryResult = _computeBinaryExpression(
node.binaryOffset,
valueType,
read,
readType,
node.binaryName,
node.rhs,
null);
binaryResult =
ensureAssignableResult(valueType, binaryResult, isVoidAllowed: true);
Expression value = binaryResult.expression;
VariableDeclaration? valueVariable;
if (node.forEffect) {
// No need for value variable.
} else {
valueVariable = createVariable(value, valueType);
value = createVariableGet(valueVariable);
}
Expression write;
if (writeTarget.isMissing) {
write = createMissingPropertySet(
node.writeOffset, writeReceiver, readType, node.propertyName, value,
forEffect: node.forEffect);
} else {
assert(writeTarget.isExtensionMember);
write = new StaticInvocation(
writeTarget.member as Procedure,
new Arguments(<Expression>[
writeReceiver,
value,
], types: writeTarget.receiverTypeArguments)
..fileOffset = node.writeOffset)
..fileOffset = node.writeOffset;
}
Expression replacement;
if (node.forEffect) {
assert(valueVariable == null);
replacement = write;
} else {
assert(valueVariable != null);
VariableDeclaration writeVariable =
createVariable(write, const VoidType());
replacement = createLet(valueVariable!,
createLet(writeVariable, createVariableGet(valueVariable)));
}
if (receiverVariable != null) {
replacement = createLet(receiverVariable, replacement);
}
replacement.fileOffset = node.fileOffset;
return new ExpressionInferenceResult(valueType, replacement);
}
ExpressionInferenceResult visitDeferredCheck(
DeferredCheck node, DartType typeContext) {
// Since the variable is not used in the body we don't need to type infer
// it. We can just type infer the body.
ExpressionInferenceResult result =
inferExpression(node.expression, typeContext, isVoidAllowed: true);
Expression replacement = new Let(node.variable, result.expression)
..fileOffset = node.fileOffset;
return new ExpressionInferenceResult(result.inferredType, replacement);
}
@override
StatementInferenceResult visitDoStatement(DoStatement node) {
flowAnalysis.doStatement_bodyBegin(node);
StatementInferenceResult bodyResult = inferStatement(node.body);
if (bodyResult.hasChanged) {
// Coverage-ignore-block(suite): Not run.
node.body = bodyResult.statement..parent = node;
}
flowAnalysis.doStatement_conditionBegin();
InterfaceType boolType = coreTypes.boolRawType(Nullability.nonNullable);
ExpressionInferenceResult conditionResult =
inferExpression(node.condition, boolType, isVoidAllowed: true);
Expression condition =
ensureAssignableResult(boolType, conditionResult).expression;
node.condition = condition..parent = node;
flowAnalysis.doStatement_end(condition);
return const StatementInferenceResult();
}
@override
ExpressionInferenceResult visitDoubleLiteral(
DoubleLiteral node, DartType typeContext) {
return new ExpressionInferenceResult(
coreTypes.doubleRawType(Nullability.nonNullable), node);
}
@override
StatementInferenceResult visitEmptyStatement(EmptyStatement node) {
// No inference needs to be done.
return const StatementInferenceResult();
}
@override
StatementInferenceResult visitExpressionStatement(ExpressionStatement node) {
ExpressionInferenceResult result = inferExpression(
node.expression, const UnknownType(),
isVoidAllowed: true, forEffect: true);
node.expression = result.expression..parent = node;
return const StatementInferenceResult();
}
ExpressionInferenceResult visitFactoryConstructorInvocation(
FactoryConstructorInvocation node, DartType typeContext) {
bool hadExplicitTypeArguments = hasExplicitTypeArguments(node.arguments);
FunctionType functionType =
node.target.function.computeThisFunctionType(Nullability.nonNullable);
InvocationInferenceResult result = inferInvocation(
this,
typeContext,
node.fileOffset,
new InvocationTargetFunctionType(functionType),
node.arguments as ArgumentsImpl,
isConst: node.isConst,
staticTarget: node.target);
node.hasBeenInferred = true;
SourceLibraryBuilder library = libraryBuilder;
if (!hadExplicitTypeArguments) {
library.checkBoundsInFactoryInvocation(
node, typeSchemaEnvironment, helper.uri,
inferred: true);
}
Expression resolvedExpression = helper.resolveRedirectingFactoryTarget(
node.target, node.arguments, node.fileOffset, node.isConst)!;
Expression resultExpression = result.applyResult(resolvedExpression);
return new ExpressionInferenceResult(result.inferredType, resultExpression);
}
/// Returns the function type of [constructor] when called through [typedef].
FunctionType _computeAliasedConstructorFunctionType(
Constructor constructor, Typedef typedef) {
ensureMemberType(constructor);
FunctionNode function = constructor.function;
// We need create a copy of the list of type parameters, otherwise
// transformations like erasure don't work.
List<TypeParameter> classTypeParametersCopy =
new List.of(constructor.enclosingClass.typeParameters);
FreshStructuralParametersFromTypeParameters freshTypeParameters =
getFreshStructuralParametersFromTypeParameters(typedef.typeParameters);
List<StructuralParameter> typedefTypeParametersCopy =
freshTypeParameters.freshTypeParameters;
List<DartType> asTypeArguments = freshTypeParameters.freshTypeArguments;
TypedefType typedefType = new TypedefType(
typedef, libraryBuilder.library.nonNullable, asTypeArguments);
DartType unaliasedTypedef = typedefType.unalias;
assert(unaliasedTypedef is InterfaceType,
"[typedef] is assumed to resolve to an interface type");
InterfaceType targetType = unaliasedTypedef as InterfaceType;
Substitution substitution = Substitution.fromPairs(
classTypeParametersCopy, targetType.typeArguments);
List<DartType> positional = function.positionalParameters
.map((VariableDeclaration decl) =>
substitution.substituteType(decl.type))
.toList(growable: false);
List<NamedType> named = function.namedParameters
.map((VariableDeclaration decl) => new NamedType(
decl.name!, substitution.substituteType(decl.type),
isRequired: decl.isRequired))
.toList(growable: false);
named.sort();
return new FunctionType(
positional, typedefType.unalias, libraryBuilder.library.nonNullable,
namedParameters: named,
typeParameters: typedefTypeParametersCopy,
requiredParameterCount: function.requiredParameterCount);
}
ExpressionInferenceResult visitTypeAliasedConstructorInvocation(
TypeAliasedConstructorInvocation node, DartType typeContext) {
assert(getExplicitTypeArguments(node.arguments) == null);
Typedef typedef = node.typeAliasBuilder.typedef;
FunctionType calleeType =
_computeAliasedConstructorFunctionType(node.target, typedef);
calleeType = replaceReturnType(calleeType, calleeType.returnType.unalias);
InvocationInferenceResult result = inferInvocation(
this,
typeContext,
node.fileOffset,
new InvocationTargetFunctionType(calleeType),
node.arguments as ArgumentsImpl,
isConst: node.isConst,
staticTarget: node.target);
node.hasBeenInferred = true;
Expression resolvedExpression =
helper.unaliasSingleTypeAliasedConstructorInvocation(node);
Expression resultingExpression = result.applyResult(resolvedExpression);
return new ExpressionInferenceResult(
result.inferredType, resultingExpression);
}
/// Returns the function type of [factory] when called through [typedef].
FunctionType _computeAliasedFactoryFunctionType(
Procedure factory, Typedef typedef) {
assert(
factory.isFactory || factory.isExtensionTypeMember,
// Coverage-ignore(suite): Not run.
"Only run this method on a factory: $factory");
ensureMemberType(factory);
FunctionNode function = factory.function;
// We need create a copy of the list of type parameters, otherwise
// transformations like erasure don't work.
List<TypeParameter> classTypeParametersCopy =
new List.of(function.typeParameters);
FreshStructuralParametersFromTypeParameters freshTypeParameters =
getFreshStructuralParametersFromTypeParameters(typedef.typeParameters);
List<StructuralParameter> typedefTypeParametersCopy =
freshTypeParameters.freshTypeParameters;
List<DartType> asTypeArguments = freshTypeParameters.freshTypeArguments;
TypedefType typedefType = new TypedefType(
typedef, libraryBuilder.library.nonNullable, asTypeArguments);
DartType unaliasedTypedef = typedefType.unalias;
assert(unaliasedTypedef is TypeDeclarationType,
"[typedef] is assumed to resolve to a type declaration type");
TypeDeclarationType targetType = unaliasedTypedef as TypeDeclarationType;
Substitution substitution = Substitution.fromPairs(
classTypeParametersCopy, targetType.typeArguments);
List<DartType> positional = function.positionalParameters
.map((VariableDeclaration decl) =>
substitution.substituteType(decl.type))
.toList(growable: false);
List<NamedType> named = function.namedParameters
.map(
// Coverage-ignore(suite): Not run.
(VariableDeclaration decl) => new NamedType(
decl.name!, substitution.substituteType(decl.type),
isRequired: decl.isRequired))
.toList(growable: false);
named.sort();
return new FunctionType(
positional, typedefType.unalias, libraryBuilder.library.nonNullable,
namedParameters: named,
typeParameters: typedefTypeParametersCopy,
requiredParameterCount: function.requiredParameterCount);
}
ExpressionInferenceResult visitTypeAliasedFactoryInvocation(
TypeAliasedFactoryInvocation node, DartType typeContext) {
assert(getExplicitTypeArguments(node.arguments) == null);
Typedef typedef = node.typeAliasBuilder.typedef;
FunctionType calleeType =
_computeAliasedFactoryFunctionType(node.target, typedef);
calleeType = replaceReturnType(calleeType, calleeType.returnType.unalias);
InvocationInferenceResult result = inferInvocation(
this,
typeContext,
node.fileOffset,
new InvocationTargetFunctionType(calleeType),
node.arguments as ArgumentsImpl,
isConst: node.isConst,
staticTarget: node.target);
Expression resolvedExpression =
helper.unaliasSingleTypeAliasedFactoryInvocation(node)!;
Expression resultExpression = result.applyResult(resolvedExpression);
node.hasBeenInferred = true;
return new ExpressionInferenceResult(result.inferredType, resultExpression);
}
@override
InitializerInferenceResult visitFieldInitializer(FieldInitializer node) {
DartType fieldType = node.field.type;
fieldType = constructorDeclaration!.substituteFieldType(fieldType);
ExpressionInferenceResult initializerResult =
inferExpression(node.value, fieldType);
Expression initializer = ensureAssignableResult(
fieldType, initializerResult,
fileOffset: node.fileOffset)
.expression;
node.value = initializer..parent = node;
return const SuccessfulInitializerInferenceResult();
}
ForInResult handleForInDeclaringVariable(
TreeNode node,
VariableDeclaration variable,
Expression iterable,
Statement? expressionEffects,
{bool isAsync = false}) {
DartType elementType;
bool isVariableTypeNeeded = false;
if (variable is VariableDeclarationImpl && variable.isImplicitlyTyped) {
isVariableTypeNeeded = true;
elementType = const UnknownType();
} else {
elementType = variable.type;
}
ExpressionInferenceResult iterableResult =
inferForInIterable(iterable, elementType, isAsync: isAsync);
DartType inferredType = iterableResult.inferredType;
if (isVariableTypeNeeded) {
instrumentation?.record(uriForInstrumentation, variable.fileOffset,
'type', new InstrumentationValueForType(inferredType));
variable.type = inferredType;
}
// This is matched by the call to [forEach_end] in
// [inferElement], [inferMapEntry] or [inferForInStatement].
flowAnalysis.declare(variable, variable.type, initialized: true);
flowAnalysis.forEach_bodyBegin(node);
VariableDeclaration tempVariable = new VariableDeclaration(null,
type: inferredType, isFinal: true, isSynthesized: true);
VariableGet variableGet = new VariableGet(tempVariable)
..fileOffset = variable.fileOffset;
TreeNode parent = variable.parent!;
Expression implicitDowncast = ensureAssignable(
variable.type, inferredType, variableGet,
isVoidAllowed: true,
fileOffset: parent.fileOffset,
errorTemplate: templateForInLoopElementTypeNotAssignable,
nullabilityErrorTemplate:
templateForInLoopElementTypeNotAssignableNullability,
nullabilityPartErrorTemplate:
templateForInLoopElementTypeNotAssignablePartNullability);
Statement? expressionEffect;
if (!identical(implicitDowncast, variableGet)) {
variable.initializer = implicitDowncast..parent = variable;
expressionEffect = variable;
variable = tempVariable;
}
if (expressionEffects != null) {
// Coverage-ignore-block(suite): Not run.
StatementInferenceResult bodyResult = inferStatement(expressionEffects);
if (bodyResult.hasChanged) {
expressionEffects = bodyResult.statement;
}
if (expressionEffect != null) {
expressionEffects =
combineStatements(expressionEffect, expressionEffects);
}
} else {
expressionEffects = expressionEffect;
}
return new ForInResult(
variable, iterableResult.expression, null, expressionEffects);
}
ExpressionInferenceResult inferForInIterable(
Expression iterable, DartType elementType,
{bool isAsync = false}) {
Class iterableClass =
isAsync ? coreTypes.streamClass : coreTypes.iterableClass;
DartType context =
wrapType(elementType, iterableClass, Nullability.nonNullable);
ExpressionInferenceResult iterableResult =
inferExpression(iterable, context, isVoidAllowed: false);
DartType iterableType = iterableResult.inferredType;
iterable = iterableResult.expression;
DartType inferredExpressionType = iterableType.nonTypeVariableBound;
iterable = ensureAssignable(
wrapType(const DynamicType(), iterableClass, Nullability.nonNullable),
inferredExpressionType,
iterable,
errorTemplate: templateForInLoopTypeNotIterable,
nullabilityErrorTemplate: templateForInLoopTypeNotIterableNullability,
nullabilityPartErrorTemplate:
templateForInLoopTypeNotIterablePartNullability);
DartType inferredType = const DynamicType();
if (inferredExpressionType is TypeDeclarationType) {
// TODO(johnniwinther): Should we use the type of
// `iterable.iterator.current` instead?
List<DartType>? supertypeArguments = hierarchyBuilder
.getTypeArgumentsAsInstanceOf(inferredExpressionType, iterableClass);
if (supertypeArguments != null) {
inferredType = supertypeArguments[0];
}
}
return new ExpressionInferenceResult(inferredType, iterable);
}
ForInVariable computeForInVariable(
Expression? syntheticAssignment, bool hasProblem) {
if (syntheticAssignment is VariableSet) {
return new LocalForInVariable(syntheticAssignment);
} else if (syntheticAssignment is PropertySet) {
return new PropertyForInVariable(syntheticAssignment);
} else if (syntheticAssignment is AbstractSuperPropertySet) {
// Coverage-ignore-block(suite): Not run.
return new AbstractSuperPropertyForInVariable(syntheticAssignment);
} else if (syntheticAssignment is SuperPropertySet) {
return new SuperPropertyForInVariable(syntheticAssignment);
} else if (syntheticAssignment is StaticSet) {
return new StaticForInVariable(syntheticAssignment);
} else if (syntheticAssignment is InvalidExpression || hasProblem) {
return new InvalidForInVariable(syntheticAssignment);
} else {
// Coverage-ignore-block(suite): Not run.
UriOffset uriOffset = _computeUriOffset(syntheticAssignment!);
return problems.unhandled(
"${syntheticAssignment.runtimeType}",
"handleForInStatementWithoutVariable",
uriOffset.fileOffset,
uriOffset.uri);
}
}
ForInResult _handleForInWithoutVariable(
TreeNode node,
VariableDeclaration variable,
Expression iterable,
Expression? syntheticAssignment,
Statement? expressionEffects,
{bool isAsync = false,
required bool hasProblem}) {
ForInVariable forInVariable =
computeForInVariable(syntheticAssignment, hasProblem);
DartType elementType = forInVariable.computeElementType(this);
ExpressionInferenceResult iterableResult =
inferForInIterable(iterable, elementType, isAsync: isAsync);
DartType inferredType = iterableResult.inferredType;
variable.type = inferredType;
// This is matched by the call to [forEach_end] in
// [inferElement], [inferMapEntry] or [inferForInStatement].
flowAnalysis.forEach_bodyBegin(node);
syntheticAssignment = forInVariable.inferAssignment(this, inferredType);
if (syntheticAssignment is VariableSet) {
flowAnalysis.write(node, variable, inferredType, null);
}
if (expressionEffects != null) {
StatementInferenceResult result = inferStatement(expressionEffects);
expressionEffects = result.hasChanged
?
// Coverage-ignore(suite): Not run.
result.statement
: expressionEffects;
}
return new ForInResult(variable, iterableResult.expression,
syntheticAssignment, expressionEffects);
}
ForInResult _handlePatternForIn(
TreeNode node,
VariableDeclaration variable,
Expression iterable,
Expression? syntheticAssignment,
PatternVariableDeclaration patternVariableDeclaration,
{bool isAsync = false,
required bool hasProblem}) {
int? stackBase;
assert(checkStackBase(node, stackBase = stackHeight));
PatternForInResult<DartType, InvalidExpression> result =
analyzePatternForIn(
node: node,
hasAwait: isAsync,
pattern: patternVariableDeclaration.pattern,
expression: iterable,
dispatchBody: () {});
patternVariableDeclaration.matchedValueType = result.elementType;
if (result.patternForInExpressionIsNotIterableError != null) {
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"InferenceVisitorImpl._handlePatternForIn: "
"can't infer expression in a for-in pattern.",
expectedPhase: CompilationPhaseForProblemReporting.bodyBuilding));
}
assert(checkStack(node, stackBase, [
/* pattern = */ ValueKinds.Pattern,
/* initializer = */ ValueKinds.Expression,
]));
Object? rewrite = popRewrite();
if (!identical(rewrite, patternVariableDeclaration.pattern)) {
// Coverage-ignore-block(suite): Not run.
patternVariableDeclaration.pattern = (rewrite as Pattern)
..parent = patternVariableDeclaration;
}
rewrite = popRewrite();
if (!identical(rewrite, patternVariableDeclaration.initializer)) {
iterable = (rewrite as Expression)..parent = node;
}
ForInVariable forInVariable =
new PatternVariableDeclarationForInVariable(patternVariableDeclaration);
variable.type = result.elementType;
iterable = ensureAssignable(
wrapType(
const DynamicType(),
isAsync ? coreTypes.streamClass : coreTypes.iterableClass,
Nullability.nonNullable),
result.expressionType,
iterable,
errorTemplate: templateForInLoopTypeNotIterable,
nullabilityErrorTemplate: templateForInLoopTypeNotIterableNullability,
nullabilityPartErrorTemplate:
templateForInLoopTypeNotIterablePartNullability);
// This is matched by the call to [forEach_end] in
// [inferElement], [inferMapEntry] or [inferForInStatement].
flowAnalysis.forEach_bodyBegin(node);
syntheticAssignment =
forInVariable.inferAssignment(this, result.elementType);
if (syntheticAssignment is VariableSet) {
// Coverage-ignore-block(suite): Not run.
flowAnalysis.write(node, variable, result.elementType, null);
}
return new ForInResult(variable, /*iterableResult.expression*/ iterable,
syntheticAssignment, patternVariableDeclaration);
}
ForInResult handleForInWithoutVariable(
TreeNode node,
VariableDeclaration variable,
Expression iterable,
Expression? syntheticAssignment,
Statement? expressionEffects,
{bool isAsync = false,
required bool hasProblem}) {
if (expressionEffects is PatternVariableDeclaration) {
return _handlePatternForIn(
node, variable, iterable, syntheticAssignment, expressionEffects,
isAsync: isAsync, hasProblem: hasProblem);
} else {
return _handleForInWithoutVariable(
node, variable, iterable, syntheticAssignment, expressionEffects,
isAsync: isAsync, hasProblem: hasProblem);
}
}
@override
StatementInferenceResult visitForInStatement(ForInStatement node) {
assert(node.variable.name != null);
ForInResult result = handleForInDeclaringVariable(
node, node.variable, node.iterable, null,
isAsync: node.isAsync);
StatementInferenceResult bodyResult = inferStatement(node.body);
// This is matched by the call to [forEach_bodyBegin] in
// [handleForInWithoutVariable] or [handleForInDeclaringVariable].
flowAnalysis.forEach_end();
Statement body = bodyResult.hasChanged ? bodyResult.statement : node.body;
if (result.expressionSideEffects != null) {
body = combineStatements(result.expressionSideEffects!, body);
}
if (result.syntheticAssignment != null) {
// Coverage-ignore-block(suite): Not run.
body = combineStatements(
createExpressionStatement(result.syntheticAssignment!), body);
}
node.variable = result.variable..parent = node;
node.iterable = result.iterable..parent = node;
node.body = body..parent = node;
return const StatementInferenceResult();
}
StatementInferenceResult visitForInStatementWithSynthesizedVariable(
ForInStatementWithSynthesizedVariable node) {
assert(node.variable!.name == null);
ForInResult result = handleForInWithoutVariable(node, node.variable!,
node.iterable, node.syntheticAssignment, node.expressionEffects,
isAsync: node.isAsync, hasProblem: node.hasProblem);
StatementInferenceResult bodyResult = inferStatement(node.body);
// This is matched by the call to [forEach_bodyBegin] in
// [handleForInWithoutVariable] or [handleForInDeclaringVariable].
flowAnalysis.forEach_end();
Statement body = bodyResult.hasChanged
?
// Coverage-ignore(suite): Not run.
bodyResult.statement
: node.body;
if (result.expressionSideEffects != null) {
body = combineStatements(result.expressionSideEffects!, body);
}
if (result.syntheticAssignment != null) {
body = combineStatements(
createExpressionStatement(result.syntheticAssignment!), body);
}
Statement replacement = new ForInStatement(
result.variable, result.iterable, body,
isAsync: node.isAsync)
..fileOffset = node.fileOffset
..bodyOffset = node.bodyOffset;
libraryBuilder.loader.dataForTesting
// Coverage-ignore(suite): Not run.
?.registerAlias(node, replacement);
return new StatementInferenceResult.single(replacement);
}
@override
StatementInferenceResult visitForStatement(ForStatement node) {
List<VariableDeclaration>? variables;
for (int index = 0; index < node.variables.length; index++) {
VariableDeclaration variable = node.variables[index];
if (variable.name == null) {
if (variable.initializer != null) {
ExpressionInferenceResult result = inferExpression(
variable.initializer!, const UnknownType(),
isVoidAllowed: true);
variable.initializer = result.expression..parent = variable;
variable.type = result.inferredType;
}
} else {
StatementInferenceResult variableResult = inferStatement(variable);
if (variableResult.hasChanged) {
// Coverage-ignore-block(suite): Not run.
if (variables == null) {
variables = <VariableDeclaration>[];
variables.addAll(node.variables.sublist(0, index));
}
if (variableResult.statementCount == 1) {
variables.add(variableResult.statement as VariableDeclaration);
} else {
for (Statement variable in variableResult.statements) {
variables.add(variable as VariableDeclaration);
}
}
}
// Coverage-ignore(suite): Not run.
else if (variables != null) {
variables.add(variable);
}
}
}
if (variables != null) {
// Coverage-ignore-block(suite): Not run.
node.variables.clear();
node.variables.addAll(variables);
setParents(variables, node);
}
flowAnalysis.for_conditionBegin(node);
if (node.condition != null) {
InterfaceType expectedType =
coreTypes.boolRawType(Nullability.nonNullable);
ExpressionInferenceResult conditionResult =
inferExpression(node.condition!, expectedType, isVoidAllowed: true);
Expression condition =
ensureAssignableResult(expectedType, conditionResult).expression;
node.condition = condition..parent = node;
}
flowAnalysis.for_bodyBegin(node, node.condition);
StatementInferenceResult bodyResult = inferStatement(node.body);
if (bodyResult.hasChanged) {
// Coverage-ignore-block(suite): Not run.
node.body = bodyResult.statement..parent = node;
}
flowAnalysis.for_updaterBegin();
for (int index = 0; index < node.updates.length; index++) {
ExpressionInferenceResult updateResult = inferExpression(
node.updates[index], const UnknownType(),
isVoidAllowed: true);
node.updates[index] = updateResult.expression..parent = node;
}
flowAnalysis.for_end();
return const StatementInferenceResult();
}
FunctionType visitFunctionNode(FunctionNode node, DartType? typeContext,
DartType? returnContext, int returnTypeInstrumentationOffset) {
return inferLocalFunction(this, node, typeContext,
returnTypeInstrumentationOffset, returnContext);
}
@override
StatementInferenceResult visitFunctionDeclaration(
covariant FunctionDeclarationImpl node) {
bool oldInTryOrLocalFunction = _inTryOrLocalFunction;
_inTryOrLocalFunction = true;
VariableDeclaration variable = node.variable;
flowAnalysis.functionExpression_begin(node);
inferMetadata(this, variable, variable.annotations);
DartType? returnContext =
node.hasImplicitReturnType ? null : node.function.returnType;
FunctionType inferredType =
visitFunctionNode(node.function, null, returnContext, node.fileOffset);
if (dataForTesting != null &&
// Coverage-ignore(suite): Not run.
node.hasImplicitReturnType) {
// Coverage-ignore-block(suite): Not run.
dataForTesting!.typeInferenceResult.inferredVariableTypes[node] =
inferredType.returnType;
}
variable.type = inferredType;
flowAnalysis.declare(variable, variable.type, initialized: true);
flowAnalysis.functionExpression_end();
_inTryOrLocalFunction = oldInTryOrLocalFunction;
return const StatementInferenceResult();
}
@override
ExpressionInferenceResult visitFunctionExpression(
FunctionExpression node, DartType typeContext) {
bool oldInTryOrLocalFunction = _inTryOrLocalFunction;
_inTryOrLocalFunction = true;
flowAnalysis.functionExpression_begin(node);
FunctionType inferredType =
visitFunctionNode(node.function, typeContext, null, node.fileOffset);
if (dataForTesting != null) {
// Coverage-ignore-block(suite): Not run.
dataForTesting!.typeInferenceResult.inferredVariableTypes[node] =
inferredType.returnType;
}
flowAnalysis.functionExpression_end();
_inTryOrLocalFunction = oldInTryOrLocalFunction;
return new ExpressionInferenceResult(inferredType, node);
}
ExpressionInferenceResult visitIfNullExpression(
IfNullExpression node, DartType typeContext) {
// An if-null expression `E` of the form `e1 ?? e2` with context type `K` is
// analyzed as follows:
//
// - Let `T1` be the type of `e1` inferred with context type `K?`.
ExpressionInferenceResult lhsResult = inferExpression(
node.left, computeNullable(typeContext),
isVoidAllowed: false);
DartType t1 = lhsResult.inferredType;
// This ends any shorting in `node.left`.
Expression left = lhsResult.expression;
flowAnalysis.ifNullExpression_rightBegin(node.left, t1);
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If `K` is `_` or `dynamic`, `J = T1`.
DartType j;
if (typeContext is UnknownType || typeContext is DynamicType) {
j = t1;
} else
// - Otherwise, `J = K`.
{
j = typeContext;
}
ExpressionInferenceResult rhsResult =
inferExpression(node.right, j, isVoidAllowed: true);
DartType t2 = rhsResult.inferredType;
flowAnalysis.ifNullExpression_end();
// - Let `T` be `UP(NonNull(T1), T2)`.
DartType nonNullT1 = t1.toNonNull();
DartType t = typeSchemaEnvironment.getStandardUpperBound(nonNullT1, t2);
// - Let `S` be the greatest closure of `K`.
DartType s = computeGreatestClosure(typeContext);
DartType inferredType;
// If `inferenceUpdate3` is not enabled, then the type of `E` is `T`.
if (!libraryBuilder.libraryFeatures.inferenceUpdate3.isEnabled) {
inferredType = t;
} else
// - If `T <: S`, then the type of `E` is `T`.
if (typeSchemaEnvironment.isSubtypeOf(
t, s, SubtypeCheckMode.withNullabilities)) {
inferredType = t;
} else
// - Otherwise, if `NonNull(T1) <: S` and `T2 <: S`, then the type of `E` is
// `S`.
if (typeSchemaEnvironment.isSubtypeOf(
nonNullT1, s, SubtypeCheckMode.withNullabilities) &&
typeSchemaEnvironment.isSubtypeOf(
t2, s, SubtypeCheckMode.withNullabilities)) {
inferredType = s;
} else
// - Otherwise, the type of `E` is `T`.
{
inferredType = t;
}
Expression replacement;
if (left is ThisExpression) {
replacement = left;
} else {
VariableDeclaration variable = createVariable(left, t1);
Expression equalsNull = createEqualsNull(createVariableGet(variable),
fileOffset: lhsResult.expression.fileOffset);
VariableGet variableGet = createVariableGet(variable);
if (!identical(nonNullT1, t1)) {
variableGet.promotedType = nonNullT1;
}
ConditionalExpression conditional = new ConditionalExpression(
equalsNull, rhsResult.expression, variableGet, inferredType)
..fileOffset = node.fileOffset;
replacement = new Let(variable, conditional)
..fileOffset = node.fileOffset;
}
return new ExpressionInferenceResult(inferredType, replacement);
}
@override
StatementInferenceResult visitIfStatement(IfStatement node) {
flowAnalysis.ifStatement_conditionBegin();
InterfaceType expectedType = coreTypes.boolRawType(Nullability.nonNullable);
ExpressionInferenceResult conditionResult =
inferExpression(node.condition, expectedType, isVoidAllowed: true);
Expression condition =
ensureAssignableResult(expectedType, conditionResult).expression;
node.condition = condition..parent = node;
flowAnalysis.ifStatement_thenBegin(condition, node);
StatementInferenceResult thenResult = inferStatement(node.then);
if (thenResult.hasChanged) {
node.then = thenResult.statement..parent = node;
}
if (node.otherwise != null) {
flowAnalysis.ifStatement_elseBegin();
StatementInferenceResult otherwiseResult =
inferStatement(node.otherwise!);
if (otherwiseResult.hasChanged) {
// Coverage-ignore-block(suite): Not run.
node.otherwise = otherwiseResult.statement..parent = node;
}
}
flowAnalysis.ifStatement_end(node.otherwise != null);
return const StatementInferenceResult();
}
@override
StatementInferenceResult visitIfCaseStatement(IfCaseStatement node) {
int? stackBase;
assert(checkStackBase(node, stackBase = stackHeight));
IfCaseStatementResult<DartType, InvalidExpression> analysisResult =
analyzeIfCaseStatement(node, node.expression, node.patternGuard.pattern,
node.patternGuard.guard, node.then, node.otherwise, {
for (VariableDeclaration variable
in node.patternGuard.pattern.declaredVariables)
variable.name!: variable
});
node.matchedValueType = analysisResult.matchedExpressionType;
assert(checkStack(node, stackBase, [
/* ifFalse = */ ValueKinds.StatementOrNull,
/* ifTrue = */ ValueKinds.Statement,
/* guard = */ ValueKinds.ExpressionOrNull,
/* pattern = */ ValueKinds.Pattern,
/* scrutinee = */ ValueKinds.Expression,
]));
Object? rewrite = popRewrite(NullValues.Statement);
if (!identical(node.otherwise, rewrite)) {
// Coverage-ignore-block(suite): Not run.
node.otherwise = (rewrite as Statement)..parent = node;
}
rewrite = popRewrite();
if (!identical(node.then, rewrite)) {
// Coverage-ignore-block(suite): Not run.
node.then = (rewrite as Statement)..parent = node;
}
rewrite = popRewrite(NullValues.Expression);
InvalidExpression? guardError = analysisResult.nonBooleanGuardError;
if (guardError != null) {
node.patternGuard.guard = guardError..parent = node.patternGuard;
} else {
if (!identical(node.patternGuard.guard, rewrite)) {
node.patternGuard.guard = (rewrite as Expression)
..parent = node.patternGuard;
}
if (analysisResult.guardType is DynamicType) {
node.patternGuard.guard = _createImplicitAs(
node.patternGuard.guard!.fileOffset,
node.patternGuard.guard!,
coreTypes.boolNonNullableRawType)
..parent = node.patternGuard;
}
}
rewrite = popRewrite();
if (!identical(node.patternGuard.pattern, rewrite)) {
node.patternGuard.pattern = (rewrite as Pattern)
..parent = node.patternGuard;
}
rewrite = popRewrite();
if (!identical(node.expression, rewrite)) {
node.expression = (rewrite as Expression)..parent = node;
}
assert(checkStack(node, stackBase, [/*empty*/]));
return const StatementInferenceResult();
}
ExpressionInferenceResult visitIntJudgment(
IntJudgment node, DartType typeContext) {
if (isDoubleContext(typeContext)) {
double? doubleValue = node.asDouble();
if (doubleValue != null) {
Expression replacement = new DoubleLiteral(doubleValue)
..fileOffset = node.fileOffset;
DartType inferredType =
coreTypes.doubleRawType(Nullability.nonNullable);
return new ExpressionInferenceResult(inferredType, replacement);
}
}
Expression? error = checkWebIntLiteralsErrorIfUnexact(
node.value, node.literal, node.fileOffset);
if (error != null) {
// Coverage-ignore-block(suite): Not run.
return new ExpressionInferenceResult(const DynamicType(), error);
}
DartType inferredType = coreTypes.intRawType(Nullability.nonNullable);
return new ExpressionInferenceResult(inferredType, node);
}
ExpressionInferenceResult visitShadowLargeIntLiteral(
ShadowLargeIntLiteral node, DartType typeContext) {
if (isDoubleContext(typeContext)) {
// Coverage-ignore-block(suite): Not run.
double? doubleValue = node.asDouble();
if (doubleValue != null) {
Expression replacement = new DoubleLiteral(doubleValue)
..fileOffset = node.fileOffset;
DartType inferredType =
coreTypes.doubleRawType(Nullability.nonNullable);
return new ExpressionInferenceResult(inferredType, replacement);
}
}
int? intValue = node.asInt64();
if (intValue == null) {
Expression replacement = helper.buildProblem(
templateIntegerLiteralIsOutOfRange.withArguments(node.literal),
node.fileOffset,
node.literal.length);
return new ExpressionInferenceResult(const DynamicType(), replacement);
}
Expression? error = checkWebIntLiteralsErrorIfUnexact(
intValue, node.literal, node.fileOffset);
if (error != null) {
// Coverage-ignore-block(suite): Not run.
return new ExpressionInferenceResult(const DynamicType(), error);
}
Expression replacement = new IntLiteral(intValue);
DartType inferredType = coreTypes.intRawType(Nullability.nonNullable);
return new ExpressionInferenceResult(inferredType, replacement);
}
InitializerInferenceResult visitShadowInvalidInitializer(
ShadowInvalidInitializer node) {
ExpressionInferenceResult initializerResult = inferExpression(
node.variable.initializer!, const UnknownType(),
isVoidAllowed: false);
node.variable.initializer = initializerResult.expression
..parent = node.variable;
return const SuccessfulInitializerInferenceResult();
}
InitializerInferenceResult visitShadowInvalidFieldInitializer(
ShadowInvalidFieldInitializer node) {
ExpressionInferenceResult initializerResult =
inferExpression(node.value, node.fieldType, isVoidAllowed: false);
node.value = initializerResult.expression..parent = node;
return const SuccessfulInitializerInferenceResult();
}
@override
ExpressionInferenceResult visitIsExpression(
IsExpression node, DartType typeContext) {
ExpressionInferenceResult operandResult = inferExpression(
node.operand, const UnknownType(),
isVoidAllowed: false);
node.operand = operandResult.expression..parent = node;
flowAnalysis.isExpression_end(
node, node.operand, /*isNot:*/ false, node.type);
return new ExpressionInferenceResult(
coreTypes.boolRawType(Nullability.nonNullable), node);
}
@override
StatementInferenceResult visitLabeledStatement(LabeledStatement node) {
flowAnalysis.labeledStatement_begin(node);
StatementInferenceResult bodyResult = inferStatement(node.body);
flowAnalysis.labeledStatement_end();
if (bodyResult.hasChanged) {
// Coverage-ignore-block(suite): Not run.
node.body = bodyResult.statement..parent = node;
}
return const StatementInferenceResult();
}
DartType? getSpreadElementType(
DartType spreadType, DartType spreadTypeBound, bool isNullAware) {
if (coreTypes.isNull(spreadTypeBound)) {
return isNullAware ? const NeverType.nonNullable() : null;
}
if (spreadTypeBound is TypeDeclarationType) {
List<DartType>? supertypeArguments =
typeSchemaEnvironment.getTypeArgumentsAsInstanceOf(
spreadTypeBound, coreTypes.iterableClass);
if (supertypeArguments == null) {
return null;
}
return supertypeArguments.single;
} else if (spreadType is DynamicType) {
return const DynamicType();
} else if (coreTypes.isBottom(spreadType)) {
return const NeverType.nonNullable();
}
return null;
}
ExpressionInferenceResult _inferSpreadElement(
SpreadElement element,
DartType inferredTypeArgument,
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes) {
ExpressionInferenceResult spreadResult = inferExpression(
element.expression,
new InterfaceType(
coreTypes.iterableClass,
element.isNullAware
? Nullability.nullable
: Nullability.nonNullable,
<DartType>[inferredTypeArgument]),
isVoidAllowed: true);
element.expression = spreadResult.expression..parent = element;
DartType spreadType = spreadResult.inferredType;
inferredSpreadTypes[element.expression] = spreadType;
Expression replacement = element;
DartType spreadTypeBound = spreadType.nonTypeVariableBound;
DartType? spreadElementType =
getSpreadElementType(spreadType, spreadTypeBound, element.isNullAware);
if (spreadElementType == null) {
if (coreTypes.isNull(spreadTypeBound) && !element.isNullAware) {
replacement = helper.buildProblem(
templateNonNullAwareSpreadIsNull.withArguments(spreadType),
element.expression.fileOffset,
1);
} else {
if (spreadType.isPotentiallyNullable &&
spreadType is! DynamicType &&
spreadType is! NullType &&
!element.isNullAware) {
Expression receiver = element.expression;
replacement = helper.buildProblem(
messageNullableSpreadError, receiver.fileOffset, 1,
context: getWhyNotPromotedContext(
flowAnalysis.whyNotPromoted(receiver)(),
element,
// Coverage-ignore(suite): Not run.
(type) => !type.isPotentiallyNullable));
}
replacement = helper.buildProblem(
templateSpreadTypeMismatch.withArguments(spreadType),
element.expression.fileOffset,
1);
_copyNonPromotionReasonToReplacement(element, replacement);
}
} else if (spreadTypeBound is InterfaceType) {
if (!isAssignable(inferredTypeArgument, spreadElementType)) {
IsSubtypeOf subtypeCheckResult =
typeSchemaEnvironment.performNullabilityAwareSubtypeCheck(
spreadElementType, inferredTypeArgument);
if (subtypeCheckResult.isSubtypeWhenIgnoringNullabilities()) {
if (spreadElementType == subtypeCheckResult.subtype &&
inferredTypeArgument == subtypeCheckResult.supertype) {
replacement = helper.buildProblem(
templateSpreadElementTypeMismatchNullability.withArguments(
spreadElementType, inferredTypeArgument),
element.expression.fileOffset,
1);
} else {
replacement = helper.buildProblem(
templateSpreadElementTypeMismatchPartNullability.withArguments(
spreadElementType,
inferredTypeArgument,
subtypeCheckResult.subtype!,
subtypeCheckResult.supertype!),
element.expression.fileOffset,
1);
}
} else {
replacement = helper.buildProblem(
templateSpreadElementTypeMismatch.withArguments(
spreadElementType, inferredTypeArgument),
element.expression.fileOffset,
1);
}
}
if (spreadType.isPotentiallyNullable &&
spreadType is! DynamicType &&
spreadType is! NullType &&
!element.isNullAware) {
Expression receiver = element.expression;
replacement = helper.buildProblem(
messageNullableSpreadError, receiver.fileOffset, 1,
context: getWhyNotPromotedContext(
flowAnalysis.whyNotPromoted(receiver)(),
element,
// Coverage-ignore(suite): Not run.
(type) => !type.isPotentiallyNullable));
_copyNonPromotionReasonToReplacement(element, replacement);
}
}
// Use 'dynamic' for error recovery.
element.elementType = spreadElementType ?? const DynamicType();
return new ExpressionInferenceResult(element.elementType!, replacement);
}
ExpressionInferenceResult _inferNullAwareElement(
NullAwareElement element,
DartType inferredTypeArgument,
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes) {
// TODO(cstefantsova): Ensure the flow analysis is properly invoked when it
// supports null-aware elements.
ExpressionInferenceResult expressionResult = inferElement(
element.expression,
inferredTypeArgument.withDeclaredNullability(Nullability.nullable),
inferredSpreadTypes,
inferredConditionTypes);
element.expression = expressionResult.expression..parent = element;
return new ExpressionInferenceResult(
computeNonNull(expressionResult.inferredType), element);
}
ExpressionInferenceResult _inferIfElement(
IfElement element,
DartType inferredTypeArgument,
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes) {
flowAnalysis.ifStatement_conditionBegin();
DartType boolType = coreTypes.boolRawType(Nullability.nonNullable);
ExpressionInferenceResult conditionResult =
inferExpression(element.condition, boolType, isVoidAllowed: false);
Expression condition =
ensureAssignableResult(boolType, conditionResult).expression;
element.condition = condition..parent = element;
flowAnalysis.ifStatement_thenBegin(condition, element);
ExpressionInferenceResult thenResult = inferElement(element.then,
inferredTypeArgument, inferredSpreadTypes, inferredConditionTypes);
element.then = thenResult.expression..parent = element;
ExpressionInferenceResult? otherwiseResult;
if (element.otherwise != null) {
flowAnalysis.ifStatement_elseBegin();
otherwiseResult = inferElement(element.otherwise!, inferredTypeArgument,
inferredSpreadTypes, inferredConditionTypes);
element.otherwise = otherwiseResult.expression..parent = element;
}
flowAnalysis.ifStatement_end(element.otherwise != null);
return new ExpressionInferenceResult(
otherwiseResult == null
? thenResult.inferredType
: typeSchemaEnvironment.getStandardUpperBound(
thenResult.inferredType, otherwiseResult.inferredType),
element);
}
ExpressionInferenceResult _inferIfCaseElement(
IfCaseElement element,
DartType inferredTypeArgument,
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes) {
int? stackBase;
assert(checkStackBase(element, stackBase = stackHeight));
ListAndSetElementInferenceContext context =
new ListAndSetElementInferenceContext(
inferredTypeArgument: inferredTypeArgument,
inferredSpreadTypes: inferredSpreadTypes,
inferredConditionTypes: inferredConditionTypes);
IfCaseStatementResult<DartType, InvalidExpression> analysisResult =
analyzeIfCaseElement(
node: element,
expression: element.expression,
pattern: element.patternGuard.pattern,
variables: {
for (VariableDeclaration variable
in element.patternGuard.pattern.declaredVariables)
variable.name!: variable
},
guard: element.patternGuard.guard,
ifTrue: element.then,
ifFalse: element.otherwise,
context: context);
element.matchedValueType = analysisResult.matchedExpressionType;
assert(checkStack(element, stackBase, [
/* ifFalse = */ ValueKinds.ExpressionOrNull,
/* ifTrue = */ ValueKinds.Expression,
/* guard = */ ValueKinds.ExpressionOrNull,
/* pattern = */ ValueKinds.Pattern,
/* scrutinee = */ ValueKinds.Expression,
]));
Object? rewrite = popRewrite(NullValues.Expression);
if (!identical(element.otherwise, rewrite)) {
// Coverage-ignore-block(suite): Not run.
element.otherwise = (rewrite as Expression?)?..parent = element;
}
rewrite = popRewrite();
if (!identical(element.then, rewrite)) {
// Coverage-ignore-block(suite): Not run.
element.then = (rewrite as Expression)..parent = element;
}
PatternGuard patternGuard = element.patternGuard;
rewrite = popRewrite(NullValues.Expression);
InvalidExpression? guardError = analysisResult.nonBooleanGuardError;
if (guardError != null) {
// Coverage-ignore-block(suite): Not run.
patternGuard.guard = guardError..parent = patternGuard;
} else {
if (!identical(patternGuard.guard, rewrite)) {
patternGuard.guard = (rewrite as Expression?)?..parent = patternGuard;
}
if (analysisResult.guardType is DynamicType) {
patternGuard.guard = _createImplicitAs(patternGuard.guard!.fileOffset,
patternGuard.guard!, coreTypes.boolNonNullableRawType)
..parent = patternGuard;
}
}
rewrite = popRewrite();
if (!identical(patternGuard.pattern, rewrite)) {
// Coverage-ignore-block(suite): Not run.
patternGuard.pattern = (rewrite as Pattern)..parent = patternGuard;
}
rewrite = popRewrite();
if (!identical(element.expression, rewrite)) {
// Coverage-ignore-block(suite): Not run.
element.expression = (rewrite as Expression)..parent = patternGuard;
}
DartType thenType = context.inferredConditionTypes[element.then]!;
DartType? otherwiseType = element.otherwise == null
? null
: context.inferredConditionTypes[element.otherwise!]!;
return new ExpressionInferenceResult(
otherwiseType == null
? thenType
: typeSchemaEnvironment.getStandardUpperBound(
thenType, otherwiseType),
element);
}
ExpressionInferenceResult _inferPatternForElement(
PatternForElement element,
DartType inferredTypeArgument,
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes) {
int? stackBase;
assert(checkStackBase(element, stackBase = stackHeight));
PatternVariableDeclaration patternVariableDeclaration =
element.patternVariableDeclaration;
PatternVariableDeclarationAnalysisResult<DartType, DartType>
analysisResult = analyzePatternVariableDeclaration(
patternVariableDeclaration,
patternVariableDeclaration.pattern,
patternVariableDeclaration.initializer,
isFinal: patternVariableDeclaration.isFinal);
patternVariableDeclaration.matchedValueType =
analysisResult.initializerType;
assert(checkStack(element, stackBase, [
/* pattern = */ ValueKinds.Pattern,
/* initializer = */ ValueKinds.Expression,
]));
Object? rewrite = popRewrite(NullValues.Expression);
if (!identical(patternVariableDeclaration.pattern, rewrite)) {
// Coverage-ignore-block(suite): Not run.
patternVariableDeclaration.pattern = (rewrite as Pattern)
..parent = patternVariableDeclaration;
}
rewrite = popRewrite();
if (!identical(patternVariableDeclaration.initializer, rewrite)) {
patternVariableDeclaration.initializer = (rewrite as Expression)
..parent = patternVariableDeclaration;
}
List<VariableDeclaration> declaredVariables =
patternVariableDeclaration.pattern.declaredVariables;
assert(declaredVariables.length == element.intermediateVariables.length);
assert(declaredVariables.length == element.variables.length);
for (int i = 0; i < declaredVariables.length; i++) {
DartType type = declaredVariables[i].type;
element.intermediateVariables[i].type = type;
element.variables[i].type = type;
}
return _inferForElementBase(element, inferredTypeArgument,
inferredSpreadTypes, inferredConditionTypes);
}
ExpressionInferenceResult _inferForElement(
ForElement element,
DartType inferredTypeArgument,
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes) {
return _inferForElementBase(element, inferredTypeArgument,
inferredSpreadTypes, inferredConditionTypes);
}
ExpressionInferenceResult _inferForElementBase(
ForElementBase element,
DartType inferredTypeArgument,
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes) {
// TODO(johnniwinther): Use _visitStatements instead.
List<VariableDeclaration>? variables;
for (int index = 0; index < element.variables.length; index++) {
VariableDeclaration variable = element.variables[index];
if (variable.name == null) {
if (variable.initializer != null) {
ExpressionInferenceResult initializerResult = inferExpression(
variable.initializer!, variable.type,
isVoidAllowed: true);
variable.initializer = initializerResult.expression
..parent = variable;
variable.type = initializerResult.inferredType;
}
} else {
StatementInferenceResult variableResult = inferStatement(variable);
if (variableResult.hasChanged) {
// Coverage-ignore-block(suite): Not run.
if (variables == null) {
variables = <VariableDeclaration>[];
variables.addAll(element.variables.sublist(0, index));
}
if (variableResult.statementCount == 1) {
variables.add(variableResult.statement as VariableDeclaration);
} else {
for (Statement variable in variableResult.statements) {
variables.add(variable as VariableDeclaration);
}
}
}
// Coverage-ignore(suite): Not run.
else if (variables != null) {
variables.add(variable);
}
}
}
if (variables != null) {
// Coverage-ignore-block(suite): Not run.
element.variables.clear();
element.variables.addAll(variables);
setParents(variables, element);
}
flowAnalysis.for_conditionBegin(element);
if (element.condition != null) {
ExpressionInferenceResult conditionResult = inferExpression(
element.condition!, coreTypes.boolRawType(Nullability.nonNullable),
isVoidAllowed: false);
element.condition = conditionResult.expression..parent = element;
inferredConditionTypes[element.condition!] = conditionResult.inferredType;
}
flowAnalysis.for_bodyBegin(null, element.condition);
ExpressionInferenceResult bodyResult = inferElement(element.body,
inferredTypeArgument, inferredSpreadTypes, inferredConditionTypes);
element.body = bodyResult.expression..parent = element;
flowAnalysis.for_updaterBegin();
for (int index = 0; index < element.updates.length; index++) {
ExpressionInferenceResult updateResult = inferExpression(
element.updates[index], const UnknownType(),
isVoidAllowed: true);
element.updates[index] = updateResult.expression..parent = element;
}
flowAnalysis.for_end();
return new ExpressionInferenceResult(bodyResult.inferredType, element);
}
ExpressionInferenceResult _inferForInElement(
ForInElement element,
DartType inferredTypeArgument,
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes) {
ForInResult result;
if (element.variable.name == null) {
result = handleForInWithoutVariable(
element,
element.variable,
element.iterable,
element.syntheticAssignment,
element.expressionEffects,
isAsync: element.isAsync,
hasProblem: element.problem != null);
} else {
result = handleForInDeclaringVariable(element, element.variable,
element.iterable, element.expressionEffects,
isAsync: element.isAsync);
}
element.variable = result.variable..parent = element;
element.iterable = result.iterable..parent = element;
// TODO(johnniwinther): Use ?.. here instead.
element.syntheticAssignment = result.syntheticAssignment;
result.syntheticAssignment?.parent = element;
// TODO(johnniwinther): Use ?.. here instead.
element.expressionEffects = result.expressionSideEffects;
result.expressionSideEffects?.parent = element;
if (element.problem != null) {
// Coverage-ignore-block(suite): Not run.
ExpressionInferenceResult problemResult = inferExpression(
element.problem!, const UnknownType(),
isVoidAllowed: true);
element.problem = problemResult.expression..parent = element;
}
ExpressionInferenceResult bodyResult = inferElement(element.body,
inferredTypeArgument, inferredSpreadTypes, inferredConditionTypes);
element.body = bodyResult.expression..parent = element;
// This is matched by the call to [forEach_bodyBegin] in
// [handleForInWithoutVariable] or [handleForInDeclaringVariable].
flowAnalysis.forEach_end();
return new ExpressionInferenceResult(bodyResult.inferredType, element);
}
ExpressionInferenceResult inferElement(
Expression element,
DartType inferredTypeArgument,
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes) {
if (element is ControlFlowElement) {
switch (element) {
case SpreadElement():
return _inferSpreadElement(element, inferredTypeArgument,
inferredSpreadTypes, inferredConditionTypes);
case NullAwareElement():
return _inferNullAwareElement(element, inferredTypeArgument,
inferredSpreadTypes, inferredConditionTypes);
case IfElement():
return _inferIfElement(element, inferredTypeArgument,
inferredSpreadTypes, inferredConditionTypes);
case IfCaseElement():
return _inferIfCaseElement(element, inferredTypeArgument,
inferredSpreadTypes, inferredConditionTypes);
case ForElement():
return _inferForElement(element, inferredTypeArgument,
inferredSpreadTypes, inferredConditionTypes);
case PatternForElement():
return _inferPatternForElement(element, inferredTypeArgument,
inferredSpreadTypes, inferredConditionTypes);
case ForInElement():
return _inferForInElement(element, inferredTypeArgument,
inferredSpreadTypes, inferredConditionTypes);
}
} else {
ExpressionInferenceResult result =
inferExpression(element, inferredTypeArgument, isVoidAllowed: true);
if (inferredTypeArgument is! UnknownType) {
result = ensureAssignableResult(inferredTypeArgument, result,
isVoidAllowed: inferredTypeArgument is VoidType);
}
return result;
}
}
void _copyNonPromotionReasonToReplacement(
TreeNode oldNode, TreeNode replacement) {
if (!identical(oldNode, replacement) &&
dataForTesting
// Coverage-ignore(suite): Not run.
?.flowAnalysisResult !=
null) {
// Coverage-ignore-block(suite): Not run.
dataForTesting!.flowAnalysisResult.nonPromotionReasons[replacement] =
dataForTesting!.flowAnalysisResult.nonPromotionReasons[oldNode]!;
}
}
void checkElement(
ControlFlowElement item,
Expression parent,
DartType typeArgument,
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes) {
switch (item) {
case SpreadElement():
DartType? spreadType = inferredSpreadTypes[item.expression];
if (spreadType is DynamicType) {
Expression expression = ensureAssignable(
coreTypes.iterableRawType(item.isNullAware
? Nullability.nullable
: Nullability.nonNullable),
spreadType,
item.expression);
item.expression = expression..parent = item;
}
case NullAwareElement(:Expression expression):
if (expression is ControlFlowElement) {
// Coverage-ignore-block(suite): Not run.
checkElement(expression, item, typeArgument, inferredSpreadTypes,
inferredConditionTypes);
}
case IfElement(:Expression then, :Expression? otherwise):
if (then is ControlFlowElement) {
checkElement(then, item, typeArgument, inferredSpreadTypes,
inferredConditionTypes);
}
if (otherwise is ControlFlowElement) {
checkElement(otherwise, item, typeArgument, inferredSpreadTypes,
inferredConditionTypes);
}
case IfCaseElement(:Expression then, :Expression? otherwise):
if (then is ControlFlowElement) {
checkElement(then, item, typeArgument, inferredSpreadTypes,
inferredConditionTypes);
}
if (otherwise is ControlFlowElement) {
checkElement(otherwise, item, typeArgument, inferredSpreadTypes,
inferredConditionTypes);
}
case ForElement(:Expression? condition, :Expression body):
if (condition != null) {
DartType conditionType = inferredConditionTypes[condition]!;
Expression assignableCondition = ensureAssignable(
coreTypes.boolRawType(Nullability.nonNullable),
conditionType,
condition);
item.condition = assignableCondition..parent = item;
}
if (body is ControlFlowElement) {
checkElement(body, item, typeArgument, inferredSpreadTypes,
inferredConditionTypes);
}
case PatternForElement(:Expression? condition, :Expression body):
if (condition != null) {
DartType conditionType = inferredConditionTypes[condition]!;
Expression assignableCondition = ensureAssignable(
coreTypes.boolRawType(Nullability.nonNullable),
conditionType,
condition);
item.condition = assignableCondition..parent = item;
}
if (body is ControlFlowElement) {
checkElement(body, item, typeArgument, inferredSpreadTypes,
inferredConditionTypes);
}
case ForInElement(:Expression body):
if (body is ControlFlowElement) {
checkElement(body, item, typeArgument, inferredSpreadTypes,
inferredConditionTypes);
}
}
}
@override
ExpressionInferenceResult visitListLiteral(
ListLiteral node, DartType typeContext) {
Class listClass = coreTypes.listClass;
InterfaceType listType =
coreTypes.thisInterfaceType(listClass, Nullability.nonNullable);
List<DartType>? inferredTypes;
DartType inferredTypeArgument;
bool inferenceNeeded = node.typeArgument is ImplicitTypeArgument;
List<DartType> formalTypes = [];
List<DartType> actualTypes = [];
Map<TreeNode, DartType> inferredSpreadTypes =
new Map<TreeNode, DartType>.identity();
Map<Expression, DartType> inferredConditionTypes =
new Map<Expression, DartType>.identity();
TypeConstraintGatherer? gatherer;
FreshStructuralParametersFromTypeParameters freshTypeParameters =
getFreshStructuralParametersFromTypeParameters(
listClass.typeParameters);
List<StructuralParameter> typeParametersToInfer =
freshTypeParameters.freshTypeParameters;
listType = freshTypeParameters.substitute(listType) as InterfaceType;
if (inferenceNeeded) {
gatherer = typeSchemaEnvironment.setupGenericTypeInference(
listType, typeParametersToInfer, typeContext,
isConst: node.isConst,
typeOperations: operations,
inferenceResultForTesting: dataForTesting
// Coverage-ignore(suite): Not run.
?.typeInferenceResult,
treeNodeForTesting: node);
inferredTypes = typeSchemaEnvironment.choosePreliminaryTypes(
gatherer, typeParametersToInfer, null);
inferredTypeArgument = inferredTypes[0];
} else {
inferredTypeArgument = node.typeArgument;
}
for (int index = 0; index < node.expressions.length; ++index) {
ExpressionInferenceResult result = inferElement(node.expressions[index],
inferredTypeArgument, inferredSpreadTypes, inferredConditionTypes);
node.expressions[index] = result.expression..parent = node;
actualTypes.add(result.inferredType);
if (inferenceNeeded) {
formalTypes.add(listType.typeArguments[0]);
}
}
if (inferenceNeeded) {
gatherer!.constrainArguments(formalTypes, actualTypes,
treeNodeForTesting: node);
inferredTypes = typeSchemaEnvironment.chooseFinalTypes(
gatherer, typeParametersToInfer, inferredTypes!);
if (dataForTesting != null) {
// Coverage-ignore-block(suite): Not run.
dataForTesting!.typeInferenceResult.inferredTypeArguments[node] =
inferredTypes;
}
inferredTypeArgument = inferredTypes[0];
instrumentation?.record(
uriForInstrumentation,
node.fileOffset,
'typeArgs',
new Ins