Prototype integration of shared type analysis logic with CFE.

This change replaces the InferenceVisitorImpl.visitSwitchStatement
with a call to the new shared type analyzer, and implements the
necessary callbacks to allow analysis to work end-to-end.

Change-Id: I73a9514428b4ce092762d3ea450545986d0e1ea9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/257491
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
Paul Berry
2022-10-18 14:33:18 +00:00
committed by Commit Queue
parent 39168c9ef0
commit cf50ee8eb4
13 changed files with 559 additions and 132 deletions
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
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';
import 'package:_fe_analyzer_shared/src/type_inference/type_operations.dart';
import 'package:_fe_analyzer_shared/src/util/link.dart';
import 'package:front_end/src/api_prototype/lowering_predicates.dart';
import 'package:kernel/ast.dart';
@@ -42,6 +45,7 @@ import 'inference_helper.dart';
import 'inference_results.dart';
import 'inference_visitor_base.dart';
import 'object_access_target.dart';
import 'shared_type_analyzer.dart';
import 'type_constraint_gatherer.dart';
import 'type_inference_engine.dart';
import 'type_inferrer.dart' show TypeInferrerImpl;
@@ -74,19 +78,62 @@ abstract class InferenceVisitor {
}
class InferenceVisitorImpl extends InferenceVisitorBase
with
TypeAnalyzer<Node, Statement, Expression, VariableDeclaration, DartType>
implements
ExpressionVisitor1<ExpressionInferenceResult, DartType>,
StatementVisitor<StatementInferenceResult>,
InitializerVisitor<InitializerInferenceResult>,
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 TypeOperations2<DartType> typeOperations;
/// Context information for the current closure, or `null` if we are not
/// inside a closure.
ClosureContext? _closureContext;
InferenceVisitorImpl(TypeInferrerImpl inferrer, InferenceHelper? helper)
: super(inferrer, helper);
/// 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<Node?> _rewriteStack = [];
@override
final TypeAnalyzerOptions options;
@override
late final SharedTypeAnalyzerErrors? errors = isTopLevel
? null
: new SharedTypeAnalyzerErrors(
helper: helper, uriForInstrumentation: uriForInstrumentation);
InferenceVisitorImpl(
TypeInferrerImpl inferrer, InferenceHelper? helper, this.typeOperations)
: options = new TypeAnalyzerOptions(
nullSafetyEnabled: inferrer.libraryBuilder.isNonNullableByDefault,
patternsEnabled: false),
super(inferrer, helper);
ClosureContext get closureContext => _closureContext!;
@@ -6580,111 +6627,32 @@ class InferenceVisitorImpl extends InferenceVisitorBase
@override
StatementInferenceResult visitSwitchStatement(SwitchStatement node) {
ExpressionInferenceResult expressionResult = inferExpression(
node.expression, const UnknownType(), true,
isVoidAllowed: false);
node.expression = expressionResult.expression..parent = node;
DartType expressionType = expressionResult.inferredType;
Set<Field?>? enumFields;
if (expressionType is InterfaceType && expressionType.classNode.isEnum) {
enumFields = <Field?>{
...expressionType.classNode.fields
.where((Field field) => field.isEnumElement)
};
if (expressionType.isPotentiallyNullable) {
enumFields.add(null);
}
// Stack: ()
Set<Field?>? previousEnumFields = _enumFields;
Expression expression = node.expression;
SwitchStatementTypeAnalysisResult<DartType> analysisResult =
analyzeSwitchStatement(node, expression, node.cases.length);
// Note that a switch statement with a `default` clause is always considered
// exhaustive, but the kernel format also keeps track of whether the switch
// statement is "explicitly exhaustive", meaning that it has a `case` clause
// for every possible enum value. So if there's a `default` clause we need
// to call `isSwitchExhaustive` to figure out whether the switch is
// *explicitly* exhaustive.
node.isExplicitlyExhaustive = analysisResult.hasDefault
? isSwitchExhaustive(node, analysisResult.scrutineeType)
: analysisResult.isExhaustive;
_enumFields = previousEnumFields;
// Stack: (Expression)
Node? rewrite = popRewrite();
if (!identical(expression, rewrite)) {
expression = rewrite as Expression;
node.expression = expression..parent = node;
}
flowAnalysis.switchStatement_expressionEnd(node);
bool hasDefault = false;
bool lastCaseTerminates = true;
for (int caseIndex = 0; caseIndex < node.cases.length; ++caseIndex) {
SwitchCaseImpl switchCase = node.cases[caseIndex] as SwitchCaseImpl;
hasDefault = hasDefault || switchCase.isDefault;
flowAnalysis.switchStatement_beginCase();
flowAnalysis.switchStatement_beginAlternatives();
flowAnalysis.switchStatement_endAlternative();
flowAnalysis.switchStatement_endAlternatives(node,
hasLabels: switchCase.hasLabel);
for (int index = 0; index < switchCase.expressions.length; index++) {
ExpressionInferenceResult caseExpressionResult = inferExpression(
switchCase.expressions[index], expressionType, true,
isVoidAllowed: false);
Expression caseExpression = caseExpressionResult.expression;
switchCase.expressions[index] = caseExpression..parent = switchCase;
DartType caseExpressionType = caseExpressionResult.inferredType;
if (enumFields != null) {
if (caseExpression is StaticGet) {
enumFields.remove(caseExpression.target);
} else if (caseExpression is NullLiteral) {
enumFields.remove(null);
}
}
if (!isTopLevel) {
if (libraryBuilder.isNonNullableByDefault) {
if (!typeSchemaEnvironment.isSubtypeOf(caseExpressionType,
expressionType, SubtypeCheckMode.withNullabilities)) {
helper.addProblem(
templateSwitchExpressionNotSubtype.withArguments(
caseExpressionType,
expressionType,
isNonNullableByDefault),
caseExpression.fileOffset,
noLength,
context: [
messageSwitchExpressionNotAssignableCause.withLocation(
uriForInstrumentation,
node.expression.fileOffset,
noLength)
]);
}
} else {
// Check whether the expression type is assignable to the case
// expression type.
if (!isAssignable(expressionType, caseExpressionType)) {
helper.addProblem(
templateSwitchExpressionNotAssignable.withArguments(
expressionType,
caseExpressionType,
isNonNullableByDefault),
caseExpression.fileOffset,
noLength,
context: [
messageSwitchExpressionNotAssignableCause.withLocation(
uriForInstrumentation,
node.expression.fileOffset,
noLength)
]);
}
}
}
}
StatementInferenceResult bodyResult = inferStatement(switchCase.body);
if (bodyResult.hasChanged) {
switchCase.body = bodyResult.statement..parent = switchCase;
}
if (isNonNullableByDefault) {
lastCaseTerminates = !flowAnalysis.isReachable;
if (!isTopLevel) {
// The last case block is allowed to complete normally.
if (caseIndex < node.cases.length - 1 && flowAnalysis.isReachable) {
libraryBuilder.addProblem(messageSwitchCaseFallThrough,
switchCase.fileOffset, noLength, helper.uri);
}
}
}
}
node.isExplicitlyExhaustive = enumFields != null && enumFields.isEmpty;
bool isExhaustive = node.isExplicitlyExhaustive || hasDefault;
flowAnalysis.switchStatement_end(isExhaustive);
Statement? replacement;
if (isExhaustive && !hasDefault && shouldThrowUnsoundnessException) {
if (!lastCaseTerminates) {
if (analysisResult.isExhaustive &&
!analysisResult.hasDefault &&
shouldThrowUnsoundnessException) {
if (!analysisResult.lastCaseTerminates) {
LabeledStatement breakTarget;
if (node.parent is LabeledStatement) {
breakTarget = node.parent as LabeledStatement;
@@ -7569,6 +7537,219 @@ class InferenceVisitorImpl extends InferenceVisitorBase
}
}
}
/// Pops the top entry off of [_rewriteStack].
Node? popRewrite() {
Node? expression = _rewriteStack.removeLast();
if (_debugRewriteStack) {
assert(_debugPrint('POP ${expression.runtimeType} $expression'));
}
return expression;
}
/// Pushes an entry onto [_rewriteStack].
void pushRewrite(Node? node) {
if (_debugRewriteStack) {
assert(_debugPrint('PUSH ${node.runtimeType} $node'));
}
_rewriteStack.add(node);
}
/// Helper function used to print information to the console in debug mode.
/// This method returns `true` so that it can be conveniently called inside of
/// an `assert` statement.
bool _debugPrint(String s) {
print(s);
return true;
}
@override
DartType get boolType => throw new UnimplementedError('TODO(paulberry)');
@override
ExpressionTypeAnalysisResult<DartType> dispatchExpression(
Expression node, DartType context) {
ExpressionInferenceResult expressionResult =
inferExpression(node, context, true).stopShorting();
pushRewrite(expressionResult.expression);
return new SimpleTypeAnalysisResult(type: expressionResult.inferredType);
}
@override
void dispatchPattern(
DartType matchedType,
Map<VariableDeclaration, VariableTypeInfo<Node, DartType>> typeInfos,
MatchContext<Node, Expression> context,
Node node) {
// The front end's representation of a switch cases currently doesn't have
// any support for patterns; each case is represented as an expression. So
// analyze it as a constant pattern.
return analyzeConstantPattern(
matchedType, typeInfos, context, node, node as Expression);
}
@override
DartType dispatchPatternSchema(Node node) {
// The front end's representation of a switch cases currently doesn't have
// any support for patterns; each case is represented as an expression. So
// analyze it as a constant pattern.
return analyzeConstantPatternSchema();
}
@override
void dispatchStatement(Statement statement) {
StatementInferenceResult result = inferStatement(statement);
pushRewrite(result.hasChanged ? result.statement : statement);
}
@override
DartType get doubleType => throw new UnimplementedError('TODO(paulberry)');
@override
DartType get dynamicType => throw new UnimplementedError('TODO(paulberry)');
@override
void finishExpressionCase(Expression node, int caseIndex) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
void handleMergedStatementCase(covariant SwitchStatement node,
{required int caseIndex,
required int executionPathIndex,
required int numStatements}) {
assert(numStatements == 1 || numStatements == 0);
if (numStatements == 1) {
// Stack: (Statement)
SwitchCase case_ = node.cases[executionPathIndex];
Statement body = case_.body;
Node? rewrite = popRewrite();
// Stack: ()
if (!identical(body, rewrite)) {
body = rewrite as Statement;
case_.body = body..parent = case_;
}
}
}
@override
FlowAnalysis<Node, Statement, Expression, VariableDeclaration, DartType>?
get flow => flowAnalysis;
@override
SwitchExpressionMemberInfo<Node, Expression> getSwitchExpressionMemberInfo(
Expression node, int index) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
SwitchStatementMemberInfo<Node, Statement, Expression>
getSwitchStatementMemberInfo(
covariant SwitchStatement node, int caseIndex) {
SwitchCaseImpl case_ = node.cases[caseIndex] as SwitchCaseImpl;
return new SwitchStatementMemberInfo([
for (Expression expression in case_.expressions)
new CaseHeadOrDefaultInfo(pattern: expression),
if (case_.isDefault) new CaseHeadOrDefaultInfo(pattern: null)
], [
case_.body
], labels: case_.hasLabel ? [case_] : const []);
}
@override
void handleCaseHead(covariant SwitchStatement node,
{required int caseIndex, required int subIndex}) {
// Stack: (Pattern, Expression)
popRewrite(); // "when" expression
// Stack: (Pattern)
SwitchCaseImpl case_ = node.cases[caseIndex] as SwitchCaseImpl;
Expression expression = case_.expressions[subIndex];
Node? rewrite = popRewrite();
// Stack: ()
if (!identical(expression, rewrite)) {
expression = rewrite as Expression;
case_.expressions[subIndex] = expression..parent = case_;
}
Set<Field?>? enumFields = _enumFields;
if (enumFields != null) {
if (expression is StaticGet) {
enumFields.remove(expression.target);
} else if (expression is NullLiteral) {
enumFields.remove(null);
}
}
}
@override
void handleCase_afterCaseHeads(Statement node, int caseIndex, int numHeads) {}
@override
void handleDefault(Node node, int caseIndex) {}
@override
void handleNoStatement(Statement node) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
void handleNoGuard(Node node, int caseIndex) {
// Stack: ()
pushRewrite(null);
// Stack: (Expression)
}
@override
void handleSwitchScrutinee(DartType type) {
if (type is InterfaceType && type.classNode.isEnum) {
_enumFields = <Field?>{
...type.classNode.fields.where((Field field) => field.isEnumElement),
if (type.isPotentiallyNullable) null
};
} else {
_enumFields = null;
}
}
@override
DartType get intType => throw new UnimplementedError('TODO(paulberry)');
@override
bool isSwitchExhaustive(Node node, DartType expressionType) {
Set<Field?>? enumFields = _enumFields;
return enumFields != null && enumFields.isEmpty;
}
@override
bool isVariablePattern(Node node) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
DartType listType(DartType elementType) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
DartType get objectQuestionType =>
throw new UnimplementedError('TODO(paulberry)');
@override
void setVariableType(VariableDeclaration variable, DartType type) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
DartType get unknownType => const UnknownType();
@override
DartType variableTypeFromInitializerType(DartType type) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
void checkCleanState() {
assert(_rewriteStack.isEmpty);
}
}
/// Offset and type information collection in [InferenceVisitor.inferMapEntry].
@@ -179,6 +179,10 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
FlowAnalysis<TreeNode, Statement, Expression, VariableDeclaration, DartType>
get flowAnalysis => _inferrer.flowAnalysis;
/// Provides access to the [OperationsCfe] object. This is needed by
/// [isAssignable].
OperationsCfe get operations => _inferrer.operations;
TypeSchemaEnvironment get typeSchemaEnvironment =>
_inferrer.typeSchemaEnvironment;
@@ -386,18 +390,8 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
typeContext.classNode == coreTypes.doubleClass;
}
bool isAssignable(DartType contextType, DartType expressionType) {
if (isNonNullableByDefault) {
if (expressionType is DynamicType) return true;
return typeSchemaEnvironment
.performNullabilityAwareSubtypeCheck(expressionType, contextType)
.isSubtypeWhenUsingNullabilities();
}
return typeSchemaEnvironment
.performNullabilityAwareSubtypeCheck(expressionType, contextType)
.orSubtypeCheckFor(contextType, expressionType, typeSchemaEnvironment)
.isSubtypeWhenIgnoringNullabilities();
}
bool isAssignable(DartType contextType, DartType expressionType) =>
operations.isAssignableTo(expressionType, contextType);
/// Ensures that [expressionType] is assignable to [contextType].
///
@@ -4098,6 +4092,11 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
charOffset,
length);
}
/// The client of type inference should call this method after asking
/// inference to visit a node. This performs assertions to make sure that
/// temporary type inference state has been properly cleaned up.
void checkCleanState();
}
/// Describes assignability kind of one type to another.
@@ -0,0 +1,95 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart';
import 'package:kernel/ast.dart';
import '../fasta_codes.dart';
import 'inference_helper.dart';
/// Implementation of [TypeAnalyzerErrors] that reports errors using the
/// front end's [InferenceHelper] class.
class SharedTypeAnalyzerErrors
implements
TypeAnalyzerErrors<Node, Statement, Expression, VariableDeclaration,
DartType> {
final InferenceHelper helper;
final Uri uriForInstrumentation;
SharedTypeAnalyzerErrors(
{required this.helper, required this.uriForInstrumentation});
@override
void assertInErrorRecovery() {
// TODO(paulberry): figure out how to do this.
}
@override
void caseExpressionTypeMismatch(
{required Expression scrutinee,
required Expression caseExpression,
required caseExpressionType,
required scrutineeType,
required bool nullSafetyEnabled}) {
helper.addProblem(
nullSafetyEnabled
? templateSwitchExpressionNotSubtype.withArguments(
caseExpressionType, scrutineeType, nullSafetyEnabled)
: templateSwitchExpressionNotAssignable.withArguments(
scrutineeType, caseExpressionType, nullSafetyEnabled),
caseExpression.fileOffset,
noLength,
context: [
messageSwitchExpressionNotAssignableCause.withLocation(
uriForInstrumentation, scrutinee.fileOffset, noLength)
]);
}
@override
void inconsistentMatchVar(
{required Node pattern,
required DartType type,
required Node previousPattern,
required DartType previousType}) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
void inconsistentMatchVarExplicitness(
{required Node pattern, required Node previousPattern}) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
void nonBooleanCondition(Expression node) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
void patternDoesNotAllowLate(Node pattern) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
void patternTypeMismatchInIrrefutableContext(
{required Node pattern,
required Node context,
required DartType matchedType,
required DartType requiredType}) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
void refutablePatternInIrrefutableContext(Node pattern, Node context) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
void switchCaseCompletesNormally(
covariant SwitchStatement node, int caseIndex, int numMergedCases) {
helper.addProblem(messageSwitchCaseFallThrough,
node.cases[caseIndex].fileOffset, noLength);
}
}
@@ -300,11 +300,13 @@ class FlowAnalysisResult {
/// CFE-specific implementation of [TypeOperations].
class OperationsCfe
with TypeOperations<DartType>
with TypeOperations<DartType>, TypeOperations2<DartType>
implements Operations<VariableDeclaration, DartType> {
final TypeEnvironment typeEnvironment;
OperationsCfe(this.typeEnvironment);
final bool isNonNullableByDefault;
OperationsCfe(this.typeEnvironment, {required this.isNonNullableByDefault});
@override
TypeClassification classifyType(DartType? type) {
@@ -382,6 +384,44 @@ class OperationsCfe
}
return from;
}
@override
DartType glb(DartType type1, DartType type2) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
bool isAssignableTo(DartType fromType, DartType toType) {
if (isNonNullableByDefault) {
if (fromType is DynamicType) return true;
return typeEnvironment
.performNullabilityAwareSubtypeCheck(fromType, toType)
.isSubtypeWhenUsingNullabilities();
} else {
return typeEnvironment
.performNullabilityAwareSubtypeCheck(fromType, toType)
.orSubtypeCheckFor(toType, fromType, typeEnvironment)
.isSubtypeWhenIgnoringNullabilities();
}
}
@override
bool isDynamic(DartType type) => type is DynamicType;
@override
DartType lub(DartType type1, DartType type2) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
DartType makeNullable(DartType type) {
throw new UnimplementedError('TODO(paulberry)');
}
@override
DartType? matchListType(DartType type) {
throw new UnimplementedError('TODO(paulberry)');
}
}
/// Type inference results used for testing.
@@ -94,16 +94,16 @@ class TypeInferrerImpl implements TypeInferrer {
final TypeInferenceEngine engine;
final OperationsCfe operations;
@override
late final FlowAnalysis<TreeNode, Statement, Expression, VariableDeclaration,
DartType> flowAnalysis = libraryBuilder
.isNonNullableByDefault
? new FlowAnalysis(
new OperationsCfe(engine.typeSchemaEnvironment), assignedVariables,
respectImplicitlyTypedVarInitializers:
libraryBuilder.libraryFeatures.constructorTearoffs.isEnabled)
: new FlowAnalysis.legacy(
new OperationsCfe(engine.typeSchemaEnvironment), assignedVariables);
DartType> flowAnalysis =
libraryBuilder.isNonNullableByDefault
? new FlowAnalysis(operations, assignedVariables,
respectImplicitlyTypedVarInitializers:
libraryBuilder.libraryFeatures.constructorTearoffs.isEnabled)
: new FlowAnalysis.legacy(operations, assignedVariables);
@override
final AssignedVariables<TreeNode, VariableDeclaration> assignedVariables;
@@ -142,12 +142,14 @@ class TypeInferrerImpl implements TypeInferrer {
const [], const DynamicType(), libraryBuilder.nonNullable),
classHierarchy = engine.classHierarchy,
instrumentation = isTopLevel ? null : engine.instrumentation,
typeSchemaEnvironment = engine.typeSchemaEnvironment {}
typeSchemaEnvironment = engine.typeSchemaEnvironment,
operations = new OperationsCfe(engine.typeSchemaEnvironment,
isNonNullableByDefault: libraryBuilder.isNonNullableByDefault);
InferenceVisitorBase _createInferenceVisitor(InferenceHelper? helper) {
// For full (non-top level) inference, we need access to the
// InferenceHelper so that we can perform error reporting.
return new InferenceVisitorImpl(this, helper);
return new InferenceVisitorImpl(this, helper, operations);
}
@override
@@ -157,7 +159,9 @@ class TypeInferrerImpl implements TypeInferrer {
ExpressionInferenceResult result = visitor.inferExpression(
initializer, const UnknownType(), true,
isVoidAllowed: true);
return visitor.inferDeclarationType(result.inferredType);
DartType type = visitor.inferDeclarationType(result.inferredType);
visitor.checkCleanState();
return type;
}
@override
@@ -170,6 +174,7 @@ class TypeInferrerImpl implements TypeInferrer {
initializerResult = visitor.ensureAssignableResult(
declaredType, initializerResult,
isVoidAllowed: declaredType is VoidType);
visitor.checkCleanState();
return initializerResult;
}
@@ -191,6 +196,7 @@ class TypeInferrerImpl implements TypeInferrer {
}
result =
closureContext.handleImplicitReturn(visitor, body, result, fileOffset);
visitor.checkCleanState();
DartType? futureValueType = closureContext.futureValueType;
assert(!(asyncMarker == AsyncMarker.Async && futureValueType == null),
"No future value type computed.");
@@ -231,6 +237,7 @@ class TypeInferrerImpl implements TypeInferrer {
InvocationInferenceResult result = visitor.inferInvocation(
visitor, typeContext, fileOffset, targetType, targetInvocationArguments,
staticTarget: target);
visitor.checkCleanState();
DartType resultType = result.inferredType;
if (resultType is InterfaceType) {
return resultType.typeArguments;
@@ -247,8 +254,10 @@ class TypeInferrerImpl implements TypeInferrer {
// TODO(paulberry): experiment to see if dynamic dispatch would be better,
// so that the type hierarchy will be simpler (which may speed up "is"
// checks).
InferenceVisitor visitor = _createInferenceVisitor(helper);
return visitor.inferInitializer(initializer);
InferenceVisitorBase visitor = _createInferenceVisitor(helper);
InitializerInferenceResult result = visitor.inferInitializer(initializer);
visitor.checkCleanState();
return result;
}
@override
@@ -260,6 +269,7 @@ class TypeInferrerImpl implements TypeInferrer {
// because inference on metadata requires the helper.
InferenceVisitorBase visitor = _createInferenceVisitor(helper);
visitor.inferMetadata(visitor, parent, annotations);
visitor.checkCleanState();
}
}
@@ -278,6 +288,7 @@ class TypeInferrerImpl implements TypeInferrer {
initializer =
visitor.ensureAssignableResult(declaredType, result).expression;
}
visitor.checkCleanState();
return initializer;
}
}
@@ -71,6 +71,7 @@ artifacts
artificial
asgerf
askesc
asking
aspect
aspx
asserting
@@ -101,6 +102,7 @@ backping
backstop
badly
bail
balance
bang
bar
basically
@@ -274,6 +276,7 @@ containers
continuations
contra
contribute
conveniently
convention
conversely
coordinated
@@ -437,6 +440,7 @@ emitting
en
encapsulation
end'ed
end's
enforce
enforced
enforces
@@ -634,6 +638,7 @@ ideal
identifies
identifying
ideographic
idle
idn
ids
idx
@@ -826,6 +831,7 @@ malbounded
manage
mangled
manipulation
manipulations
manner
markdown
masking
@@ -918,6 +924,7 @@ obligated
observatory
observe
obstruct
obtaining
occasionally
occupied
offending
@@ -1402,6 +1409,7 @@ svg
sw
swapped
sweep
switched
symbolic
synchronously
syncs
@@ -0,0 +1,11 @@
// Copyright (c) 2022, 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.
class C<X, Y> {}
void main() {
switch (1) {
C<int, int> case 1: break;
}
}
@@ -0,0 +1,3 @@
class C<X, Y> {}
void main() {}
@@ -0,0 +1,3 @@
class C<X, Y> {}
void main() {}
@@ -0,0 +1,22 @@
library /*isNonNullableByDefault*/;
//
// Problems in library:
//
// pkg/front_end/testcases/general/switch_error_recovery.dart:9:5: Error: Expected to find 'case'.
// C<int, int> case 1: break;
// ^
//
import self as self;
import "dart:core" as core;
class C<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> extends core::Object {
synthetic constructor •() → self::C<self::C::X%, self::C::Y%>
: super core::Object::•()
;
}
static method main() → void {
switch(1) {
#L1:
{}
}
}
@@ -0,0 +1,22 @@
library /*isNonNullableByDefault*/;
//
// Problems in library:
//
// pkg/front_end/testcases/general/switch_error_recovery.dart:9:5: Error: Expected to find 'case'.
// C<int, int> case 1: break;
// ^
//
import self as self;
import "dart:core" as core;
class C<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> extends core::Object {
synthetic constructor •() → self::C<self::C::X%, self::C::Y%>
: super core::Object::•()
;
}
static method main() → void {
switch(1) {
#L1:
{}
}
}
@@ -0,0 +1,10 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
class C<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> extends core::Object {
synthetic constructor •() → self::C<self::C::X%, self::C::Y%>
;
}
static method main() → void
;
@@ -0,0 +1,22 @@
library /*isNonNullableByDefault*/;
//
// Problems in library:
//
// pkg/front_end/testcases/general/switch_error_recovery.dart:9:5: Error: Expected to find 'case'.
// C<int, int> case 1: break;
// ^
//
import self as self;
import "dart:core" as core;
class C<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> extends core::Object {
synthetic constructor •() → self::C<self::C::X%, self::C::Y%>
: super core::Object::•()
;
}
static method main() → void {
switch(1) {
#L1:
{}
}
}