mirror of
https://github.com/dart-lang/sdk
synced 2024-07-20 06:24:42 +00:00
Shared type analysis: simplify handling of patterns.
Patterns may need to be visited twice during analysis: once to determine a type schema, and a second type to resolve the pattern match. Previously, the shared TypeAnalyzer had just a single `dispatchPattern` method, so it had to create temporary objects to record the structure of the patterns between the two visits. Now, there are two dispatch methods: `dispatchPatternSchema` and `dispatchPattern`. This avoids the creation of a bunch of temporary objects and makes the design much simpler. (Based on an idea from Brian Wilkerson) Change-Id: If10b6b7fb578594c3f660baa55d7e28123652638 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/260282 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
parent
1139ed3373
commit
8da75ede06
|
@ -106,40 +106,6 @@ class MatchContext<Node extends Object, Expression extends Node> {
|
|||
topPattern: topPattern);
|
||||
}
|
||||
|
||||
/// Data structure returned by the [TypeAnalyzer] `analyze` methods for
|
||||
/// patterns.
|
||||
abstract class PatternDispatchResult<Node extends Object,
|
||||
Expression extends Node, Variable extends Object, Type extends Object> {
|
||||
/// The AST node for this pattern.
|
||||
Node get node;
|
||||
|
||||
/// The type schema for this pattern.
|
||||
Type get typeSchema;
|
||||
|
||||
/// Called by the [TypeAnalyzer] when an attempt is made to match this
|
||||
/// pattern.
|
||||
///
|
||||
/// [matchedType] is the type of the thing being matched (for a variable
|
||||
/// declaration, this is the type of the initializer or substructure thereof;
|
||||
/// for a switch statement this is the type of the scrutinee or substructure
|
||||
/// thereof).
|
||||
///
|
||||
/// [typeInfos] is a data structure keeping track of the variable patterns
|
||||
/// seen so far and their type information.
|
||||
///
|
||||
/// [context] keeps track of other contextual information pertinent to the
|
||||
/// match, such as whether it is late and/or final, whether there is an
|
||||
/// initializer expression (and if so, what it is), and whether the match is
|
||||
/// happening in an irrefutable context (and if so, what surrounding construct
|
||||
/// causes it to be irrefutable).
|
||||
///
|
||||
/// Stack effect (see [TypeAnalyzer] for explanation): pushes (Pattern).
|
||||
void match(
|
||||
Type matchedType,
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context);
|
||||
}
|
||||
|
||||
/// Container for the result of running type analysis on an expression that does
|
||||
/// not contain any null shorting.
|
||||
class SimpleTypeAnalysisResult<Type extends Object>
|
||||
|
|
|
@ -162,25 +162,79 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
|
|||
/// Returns the unknown type context (`?`) used in type inference.
|
||||
Type get unknownType;
|
||||
|
||||
/// Analyzes a cast pattern. [node] is the pattern itself, [innerPattern] is
|
||||
/// the sub-pattern, and [type] is the type to cast to.
|
||||
/// Analyzes a cast pattern. [innerPattern] is the sub-pattern] and [type] is
|
||||
/// the type to cast to.
|
||||
///
|
||||
/// See [dispatchPattern] for the meanings of [matchedType], [typeInfos], and
|
||||
/// [context].
|
||||
///
|
||||
/// Stack effect: pushes (Pattern innerPattern).
|
||||
void analyzeCastPattern(
|
||||
Type matchedType,
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context,
|
||||
Node innerPattern,
|
||||
Type type) {
|
||||
dispatchPattern(type, typeInfos, context, innerPattern);
|
||||
// Stack: (Pattern)
|
||||
}
|
||||
|
||||
/// Computes the type schema for a cast pattern.
|
||||
///
|
||||
/// Stack effect: none.
|
||||
PatternDispatchResult<Node, Expression, Variable, Type> analyzeCastPattern(
|
||||
Node node, Node innerPattern, Type type) {
|
||||
return new _CastPatternDispatchResult<Node, Expression, Variable, Type>(
|
||||
this, node, dispatchPattern(innerPattern), type);
|
||||
}
|
||||
Type analyzeCastPatternSchema() => objectQuestionType;
|
||||
|
||||
/// Analyzes a constant pattern. [node] is the pattern itself, and
|
||||
/// [expression] is the constant expression. Depending on the client's
|
||||
/// representation, [node] and [expression] might or might not be identical.
|
||||
///
|
||||
/// See [dispatchPattern] for the meanings of [matchedType], [typeInfos], and
|
||||
/// [context].
|
||||
///
|
||||
/// Stack effect: pushes (Expression).
|
||||
void analyzeConstantPattern(
|
||||
Type matchedType,
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context,
|
||||
Node node,
|
||||
Expression expression) {
|
||||
// Stack: ()
|
||||
TypeAnalyzerErrors<Node, Node, Expression, Variable, Type>? errors =
|
||||
this.errors;
|
||||
Node? irrefutableContext = context.irrefutableContext;
|
||||
if (irrefutableContext != null) {
|
||||
errors?.refutablePatternInIrrefutableContext(node, irrefutableContext);
|
||||
}
|
||||
Type staticType = analyzeExpression(expression, matchedType);
|
||||
// Stack: (Expression)
|
||||
if (errors != null && !options.patternsEnabled) {
|
||||
Expression? switchScrutinee = context.getSwitchScrutinee(node);
|
||||
if (switchScrutinee != null) {
|
||||
bool nullSafetyEnabled = options.nullSafetyEnabled;
|
||||
bool matches = nullSafetyEnabled
|
||||
? typeOperations.isSubtypeOf(staticType, matchedType)
|
||||
: typeOperations.isAssignableTo(staticType, matchedType);
|
||||
if (!matches) {
|
||||
errors.caseExpressionTypeMismatch(
|
||||
caseExpression: expression,
|
||||
scrutinee: switchScrutinee,
|
||||
caseExpressionType: staticType,
|
||||
scrutineeType: matchedType,
|
||||
nullSafetyEnabled: nullSafetyEnabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the type schema for a constant pattern.
|
||||
///
|
||||
/// Stack effect: none.
|
||||
PatternDispatchResult<Node, Expression, Variable, Type>
|
||||
analyzeConstantPattern(Node node, Expression expression) {
|
||||
return new _ConstantPatternDispatchResult<Node, Expression, Variable, Type>(
|
||||
this, node, expression);
|
||||
Type analyzeConstantPatternSchema() {
|
||||
// Constant patterns are only allowed in refutable contexts, and refutable
|
||||
// contexts don't propagate a type schema into the scrutinee. So this
|
||||
// code path is only reachable if the user's code contains errors.
|
||||
errors?.assertInErrorRecovery();
|
||||
return unknownType;
|
||||
}
|
||||
|
||||
/// Analyzes an expression. [node] is the expression to analyze, and
|
||||
|
@ -219,12 +273,10 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
|
|||
flow?.ifStatement_conditionBegin();
|
||||
Type initializerType = analyzeExpression(expression, unknownType);
|
||||
// Stack: (Expression)
|
||||
PatternDispatchResult<Node, Expression, Variable, Type>
|
||||
patternDispatchResult = dispatchPattern(pattern);
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos = {};
|
||||
// TODO(paulberry): rework handling of isFinal
|
||||
patternDispatchResult.match(initializerType, typeInfos,
|
||||
new MatchContext(isFinal: false, topPattern: pattern));
|
||||
dispatchPattern(initializerType, typeInfos,
|
||||
new MatchContext(isFinal: false, topPattern: pattern), pattern);
|
||||
// Stack: (Expression, Pattern)
|
||||
if (guard != null) {
|
||||
_checkGuardType(guard, analyzeExpression(guard, boolType));
|
||||
|
@ -273,24 +325,20 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
|
|||
Node node, Node pattern, Expression initializer,
|
||||
{required bool isFinal, required bool isLate}) {
|
||||
// Stack: ()
|
||||
PatternDispatchResult<Node, Expression, Variable, Type>
|
||||
patternDispatchResult = dispatchPattern(pattern);
|
||||
if (isLate &&
|
||||
patternDispatchResult is! _VariablePatternDispatchResult<Object, Object,
|
||||
Object, Object>) {
|
||||
if (isLate && !isVariablePattern(pattern)) {
|
||||
errors?.patternDoesNotAllowLate(pattern);
|
||||
}
|
||||
if (isLate) {
|
||||
flow?.lateInitializer_begin(node);
|
||||
}
|
||||
Type initializerType =
|
||||
analyzeExpression(initializer, patternDispatchResult.typeSchema);
|
||||
analyzeExpression(initializer, dispatchPatternSchema(pattern));
|
||||
// Stack: (Expression)
|
||||
if (isLate) {
|
||||
flow?.lateInitializer_end();
|
||||
}
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos = {};
|
||||
patternDispatchResult.match(
|
||||
dispatchPattern(
|
||||
initializerType,
|
||||
typeInfos,
|
||||
new MatchContext(
|
||||
|
@ -298,7 +346,8 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
|
|||
isLate: isLate,
|
||||
initializer: initializer,
|
||||
irrefutableContext: node,
|
||||
topPattern: pattern));
|
||||
topPattern: pattern),
|
||||
pattern);
|
||||
// Stack: (Expression, Pattern)
|
||||
}
|
||||
|
||||
|
@ -317,16 +366,61 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
|
|||
/// the list element type (if explicitly supplied), and [elements] is the
|
||||
/// list of subpatterns.
|
||||
///
|
||||
/// Stack effect: none.
|
||||
PatternDispatchResult<Node, Expression, Variable, Type> analyzeListPattern(
|
||||
/// See [dispatchPattern] for the meanings of [matchedType], [typeInfos], and
|
||||
/// [context].
|
||||
///
|
||||
/// Stack effect: pushes (n * Pattern) where n = elements.length.
|
||||
Type analyzeListPattern(
|
||||
Type matchedType,
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context,
|
||||
Node node,
|
||||
{Type? elementType,
|
||||
required List<Node> elements}) {
|
||||
return new _ListPatternDispatchResult<Node, Expression, Variable, Type>(
|
||||
this,
|
||||
node,
|
||||
elementType,
|
||||
[for (Node element in elements) dispatchPattern(element)]);
|
||||
// Stack: ()
|
||||
Type? matchedElementType = typeOperations.matchListType(matchedType);
|
||||
if (matchedElementType == null) {
|
||||
if (typeOperations.isDynamic(matchedType)) {
|
||||
matchedElementType = dynamicType;
|
||||
} else {
|
||||
matchedElementType = objectQuestionType;
|
||||
}
|
||||
}
|
||||
for (Node element in elements) {
|
||||
dispatchPattern(matchedElementType, typeInfos, context, element);
|
||||
}
|
||||
// Stack: (n * Pattern) where n = elements.length
|
||||
Type requiredType = listType(elementType ?? matchedElementType);
|
||||
Node? irrefutableContext = context.irrefutableContext;
|
||||
if (irrefutableContext != null &&
|
||||
!typeOperations.isAssignableTo(matchedType, requiredType)) {
|
||||
errors?.patternTypeMismatchInIrrefutableContext(
|
||||
pattern: node,
|
||||
context: irrefutableContext,
|
||||
matchedType: matchedType,
|
||||
requiredType: requiredType);
|
||||
}
|
||||
return requiredType;
|
||||
}
|
||||
|
||||
/// Computes the type schema for a list pattern. [elementType] is the list
|
||||
/// element type (if explicitly supplied), and [elements] is the list of
|
||||
/// subpatterns.
|
||||
///
|
||||
/// Stack effect: none.
|
||||
Type analyzeListPatternSchema(
|
||||
{Type? elementType, required List<Node> elements}) {
|
||||
if (elementType == null) {
|
||||
if (elements.isEmpty) {
|
||||
return objectQuestionType;
|
||||
}
|
||||
elementType = dispatchPatternSchema(elements[0]);
|
||||
for (int i = 1; i < elements.length; i++) {
|
||||
elementType = typeOperations.glb(
|
||||
elementType!, dispatchPatternSchema(elements[i]));
|
||||
}
|
||||
}
|
||||
return listType(elementType!);
|
||||
}
|
||||
|
||||
/// Analyzes a logical-or or logical-and pattern. [node] is the pattern
|
||||
|
@ -334,24 +428,94 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
|
|||
/// operator. [isAnd] indicates whether [node] is a logical-or or a
|
||||
/// logical-and.
|
||||
///
|
||||
/// Stack effect: none.
|
||||
PatternDispatchResult<Node, Expression, Variable, Type> analyzeLogicalPattern(
|
||||
Node node, Node lhs, Node rhs,
|
||||
/// See [dispatchPattern] for the meanings of [matchedType], [typeInfos], and
|
||||
/// [context].
|
||||
///
|
||||
/// Stack effect: pushes (Pattern left, Pattern right)
|
||||
void analyzeLogicalPattern(
|
||||
Type matchedType,
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context,
|
||||
Node node,
|
||||
Node lhs,
|
||||
Node rhs,
|
||||
{required bool isAnd}) {
|
||||
return new _LogicalPatternDispatchResult<Node, Expression, Variable, Type>(
|
||||
this, node, dispatchPattern(lhs), dispatchPattern(rhs), isAnd);
|
||||
// Stack: ()
|
||||
if (!isAnd) {
|
||||
Node? irrefutableContext = context.irrefutableContext;
|
||||
if (irrefutableContext != null) {
|
||||
errors?.refutablePatternInIrrefutableContext(node, irrefutableContext);
|
||||
// Avoid cascading errors
|
||||
context = context.makeRefutable();
|
||||
}
|
||||
}
|
||||
dispatchPattern(matchedType, typeInfos, context, lhs);
|
||||
// Stack: (Pattern left)
|
||||
dispatchPattern(matchedType, typeInfos, context, rhs);
|
||||
// Stack: (Pattern left, Pattern right)
|
||||
}
|
||||
|
||||
/// Computes the type schema for a logical-or or logical-and pattern. [lhs]
|
||||
/// and [rhs] are the left and right sides of the `|` or `&` operator.
|
||||
/// [isAnd] indicates whether [node] is a logical-or or a logical-and.
|
||||
///
|
||||
/// Stack effect: none.
|
||||
Type analyzeLogicalPatternSchema(Node lhs, Node rhs, {required bool isAnd}) {
|
||||
if (isAnd) {
|
||||
return typeOperations.glb(
|
||||
dispatchPatternSchema(lhs), dispatchPatternSchema(rhs));
|
||||
} else {
|
||||
// Logical-or patterns are only allowed in refutable contexts, and
|
||||
// refutable contexts don't propagate a type schema into the scrutinee.
|
||||
// So this code path is only reachable if the user's code contains errors.
|
||||
errors?.assertInErrorRecovery();
|
||||
return unknownType;
|
||||
}
|
||||
}
|
||||
|
||||
/// Analyzes a null-check or null-assert pattern. [node] is the pattern
|
||||
/// itself, [innerPattern] is the sub-pattern, and [isAssert] indicates
|
||||
/// whether this is a null-check or a null-assert pattern.
|
||||
///
|
||||
/// See [dispatchPattern] for the meanings of [matchedType], [typeInfos], and
|
||||
/// [context].
|
||||
///
|
||||
/// Stack effect: pushes (Pattern innerPattern).
|
||||
void analyzeNullCheckOrAssertPattern(
|
||||
Type matchedType,
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context,
|
||||
Node node,
|
||||
Node innerPattern,
|
||||
{required bool isAssert}) {
|
||||
// Stack: ()
|
||||
Type innerMatchedType = typeOperations.promoteToNonNull(matchedType);
|
||||
Node? irrefutableContext = context.irrefutableContext;
|
||||
if (irrefutableContext != null && !isAssert) {
|
||||
errors?.refutablePatternInIrrefutableContext(node, irrefutableContext);
|
||||
// Avoid cascading errors
|
||||
context = context.makeRefutable();
|
||||
}
|
||||
dispatchPattern(innerMatchedType, typeInfos, context, innerPattern);
|
||||
// Stack: (Pattern)
|
||||
}
|
||||
|
||||
/// Computes the type schema for a null-check or null-assert pattern.
|
||||
/// [innerPattern] is the sub-pattern and [isAssert] indicates whether this is
|
||||
/// a null-check or a null-assert pattern.
|
||||
///
|
||||
/// Stack effect: none.
|
||||
PatternDispatchResult<Node, Expression, Variable, Type>
|
||||
analyzeNullCheckOrAssertPattern(Node node, Node innerPattern,
|
||||
{required bool isAssert}) {
|
||||
return new _NullCheckOrAssertPatternDispatchResult(
|
||||
this, node, dispatchPattern(innerPattern), isAssert);
|
||||
Type analyzeNullCheckOrAssertPatternSchema(Node innerPattern,
|
||||
{required bool isAssert}) {
|
||||
if (isAssert) {
|
||||
return typeOperations.makeNullable(dispatchPatternSchema(innerPattern));
|
||||
} else {
|
||||
// Null-check patterns are only allowed in refutable contexts, and
|
||||
// refutable contexts don't propagate a type schema into the scrutinee.
|
||||
// So this code path is only reachable if the user's code contains errors.
|
||||
errors?.assertInErrorRecovery();
|
||||
return unknownType;
|
||||
}
|
||||
}
|
||||
|
||||
/// Analyzes an expression of the form `switch (expression) { cases }`.
|
||||
|
@ -374,13 +538,14 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
|
|||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos = {};
|
||||
Node? pattern = memberInfo.head.pattern;
|
||||
if (pattern != null) {
|
||||
dispatchPattern(pattern).match(
|
||||
dispatchPattern(
|
||||
expressionType,
|
||||
typeInfos,
|
||||
new MatchContext<Node, Expression>(
|
||||
isFinal: false,
|
||||
switchScrutinee: scrutinee,
|
||||
topPattern: pattern));
|
||||
topPattern: pattern),
|
||||
pattern);
|
||||
// Stack: (Expression, i * ExpressionCase, Pattern)
|
||||
Expression? guard = memberInfo.head.guard;
|
||||
bool hasGuard = guard != null;
|
||||
|
@ -449,13 +614,14 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
|
|||
CaseHeadOrDefaultInfo<Node, Expression> head = heads[j];
|
||||
Node? pattern = head.pattern;
|
||||
if (pattern != null) {
|
||||
dispatchPattern(pattern).match(
|
||||
dispatchPattern(
|
||||
scrutineeType,
|
||||
typeInfos,
|
||||
new MatchContext<Node, Expression>(
|
||||
isFinal: false,
|
||||
switchScrutinee: scrutinee,
|
||||
topPattern: pattern));
|
||||
topPattern: pattern),
|
||||
pattern);
|
||||
// Stack: (Expression, numExecutionPaths * StatementCase,
|
||||
// numHeads * CaseHead, Pattern),
|
||||
Expression? guard = head.guard;
|
||||
|
@ -543,17 +709,61 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
|
|||
/// the variable, [declaredType] is the explicitly declared type (if present),
|
||||
/// and [isFinal] indicates whether the variable is final.
|
||||
///
|
||||
/// See [dispatchPattern] for the meanings of [matchedType], [typeInfos], and
|
||||
/// [context].
|
||||
///
|
||||
/// If this is a wildcard pattern (it doesn't bind any variable), [variable]
|
||||
/// should be `null`.
|
||||
///
|
||||
/// Stack effect: none.
|
||||
PatternDispatchResult<Node, Expression, Variable, Type>
|
||||
analyzeVariablePattern(Node node, Variable? variable, Type? declaredType,
|
||||
{required bool isFinal}) {
|
||||
return new _VariablePatternDispatchResult<Node, Expression, Variable, Type>(
|
||||
this, node, variable, declaredType, isFinal);
|
||||
Type analyzeVariablePattern(
|
||||
Type matchedType,
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context,
|
||||
Node node,
|
||||
Variable? variable,
|
||||
Type? declaredType,
|
||||
{required bool isFinal}) {
|
||||
Type staticType =
|
||||
declaredType ?? variableTypeFromInitializerType(matchedType);
|
||||
Node? irrefutableContext = context.irrefutableContext;
|
||||
if (irrefutableContext != null &&
|
||||
!typeOperations.isAssignableTo(matchedType, staticType)) {
|
||||
errors?.patternTypeMismatchInIrrefutableContext(
|
||||
pattern: node,
|
||||
context: irrefutableContext,
|
||||
matchedType: matchedType,
|
||||
requiredType: staticType);
|
||||
}
|
||||
bool isImplicitlyTyped = declaredType == null;
|
||||
if (variable != null) {
|
||||
bool isFirstMatch = _recordTypeInfo(typeInfos,
|
||||
pattern: node,
|
||||
variable: variable,
|
||||
staticType: staticType,
|
||||
isImplicitlyTyped: isImplicitlyTyped);
|
||||
if (isFirstMatch) {
|
||||
flow?.declare(variable, false);
|
||||
setVariableType(variable, staticType);
|
||||
// TODO(paulberry): are we handling _isFinal correctly?
|
||||
// TODO(paulberry): do we need to verify that all instances of a
|
||||
// variable are final or all are not final?
|
||||
flow?.initialize(variable, matchedType, context.getInitializer(node),
|
||||
isFinal: context.isFinal || isFinal,
|
||||
isLate: context.isLate,
|
||||
isImplicitlyTyped: isImplicitlyTyped);
|
||||
}
|
||||
}
|
||||
return staticType;
|
||||
}
|
||||
|
||||
/// Computes the type schema for a variable pattern. [declaredType] is the
|
||||
/// explicitly declared type (if present).
|
||||
///
|
||||
/// Stack effect: none.
|
||||
Type analyzeVariablePatternSchema(Type? declaredType) =>
|
||||
declaredType ?? unknownType;
|
||||
|
||||
/// Calls the appropriate `analyze` method according to the form of
|
||||
/// [expression], and then adjusts the stack as needed to combine any
|
||||
/// sub-structures into a single expression.
|
||||
|
@ -567,9 +777,32 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
|
|||
|
||||
/// Calls the appropriate `analyze` method according to the form of [pattern].
|
||||
///
|
||||
/// [matchedType] is the type of the thing being matched (for a variable
|
||||
/// declaration, this is the type of the initializer or substructure thereof;
|
||||
/// for a switch statement this is the type of the scrutinee or substructure
|
||||
/// thereof).
|
||||
///
|
||||
/// [typeInfos] is a data structure keeping track of the variable patterns
|
||||
/// seen so far and their type information.
|
||||
///
|
||||
/// [context] keeps track of other contextual information pertinent to the
|
||||
/// match, such as whether it is late and/or final, whether there is an
|
||||
/// initializer expression (and if so, what it is), and whether the match is
|
||||
/// happening in an irrefutable context (and if so, what surrounding construct
|
||||
/// causes it to be irrefutable).
|
||||
///
|
||||
/// Stack effect: pushes (Pattern).
|
||||
void dispatchPattern(
|
||||
Type matchedType,
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context,
|
||||
Node node);
|
||||
|
||||
/// Calls the appropriate `analyze...Schema` method according to the form of
|
||||
/// [pattern].
|
||||
///
|
||||
/// Stack effect: none.
|
||||
PatternDispatchResult<Node, Expression, Variable, Type> dispatchPattern(
|
||||
Node pattern);
|
||||
Type dispatchPatternSchema(Node pattern);
|
||||
|
||||
/// Calls the appropriate `analyze` method according to the form of
|
||||
/// [statement], and then adjusts the stack as needed to combine any
|
||||
|
@ -629,22 +862,6 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
|
|||
void handleCaseHead(Node node,
|
||||
{required int caseIndex, required int subIndex});
|
||||
|
||||
/// Called when matching a cast pattern.
|
||||
///
|
||||
/// [node] is the AST node for the pattern and [matchedType] is the static
|
||||
/// type of the expression being matched.
|
||||
///
|
||||
/// Stack effect: pushes (Pattern).
|
||||
void handleCastPattern(Node node, {required Type matchedType});
|
||||
|
||||
/// Called when matching a constant pattern.
|
||||
///
|
||||
/// [node] is the AST node for the pattern and [matchedType] is the static
|
||||
/// type of the expression being matched.
|
||||
///
|
||||
/// Stack effect: pops (Expression) and pushes (Pattern).
|
||||
void handleConstantPattern(Node node, {required Type matchedType});
|
||||
|
||||
/// Called after visiting a `default` clause.
|
||||
///
|
||||
/// [node] is the enclosing switch statement or switch expression and
|
||||
|
@ -653,27 +870,6 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
|
|||
/// Stack effect: pushes (CaseHead).
|
||||
void handleDefault(Node node, int caseIndex);
|
||||
|
||||
/// Called when matching a list pattern.
|
||||
///
|
||||
/// [node] is the AST node for the pattern, [numElements] is the number of
|
||||
/// elements in the list pattern, [matchedType] is the static type of the
|
||||
/// expression being matched, and [staticType] is the static type of the list
|
||||
/// pattern.
|
||||
///
|
||||
/// Stack effect: pops (numElements * Pattern) and pushes (Pattern).
|
||||
void handleListPattern(Node node, int numElements,
|
||||
{required Type matchedType, required Type requiredType});
|
||||
|
||||
/// Called when matching a logical-and or logical-or pattern.
|
||||
///
|
||||
/// [node] is the AST node for the pattern, [isAnd] indicates whether it is a
|
||||
/// logical-and or a logical-or pattern, and [matchedType] is the static type
|
||||
/// of the expression being matched.
|
||||
///
|
||||
/// Stack effect: pops (Pattern left, Pattern right) and pushes (Pattern).
|
||||
void handleLogicalPattern(Node node,
|
||||
{required bool isAnd, required Type matchedType});
|
||||
|
||||
/// Called after visiting a merged statement case.
|
||||
///
|
||||
/// [node] is enclosing switch statement, [caseIndex] is the index of the last
|
||||
|
@ -704,16 +900,6 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
|
|||
/// Stack effect: pushes (Statement).
|
||||
void handleNoStatement(Statement node);
|
||||
|
||||
/// Called when matching a null-check or null-assert pattern.
|
||||
///
|
||||
/// [node] is the AST node for the pattern, [matchedType] is the static type
|
||||
/// of the expression being matched, and [isAssert] indicates whether this is
|
||||
/// a null-check or a null-assert pattern.
|
||||
///
|
||||
/// Stack effect: pops (Pattern) and pushes (Pattern).
|
||||
void handleNullCheckOrAssertPattern(Node node,
|
||||
{required Type matchedType, required bool isAssert});
|
||||
|
||||
/// Called after visiting the scrutinee part of a switch statement or switch
|
||||
/// expression. This is a hook to allow the client to start exhaustiveness
|
||||
/// analysis.
|
||||
|
@ -726,16 +912,6 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
|
|||
/// Stack effect: none.
|
||||
void handleSwitchScrutinee(Type type);
|
||||
|
||||
/// Called when matching a variable pattern.
|
||||
///
|
||||
/// [node] is the AST node for the pattern, [matchedType] is the static type
|
||||
/// of the expression being matched, and [staticType] is the static type of
|
||||
/// the variable.
|
||||
///
|
||||
/// Stack effect: pushes (Pattern).
|
||||
void handleVariablePattern(Node node,
|
||||
{required Type matchedType, required Type staticType});
|
||||
|
||||
/// Queries whether the switch statement or expression represented by [node]
|
||||
/// was exhaustive. [expressionType] is the static type of the scrutinee.
|
||||
///
|
||||
|
@ -743,6 +919,9 @@ mixin TypeAnalyzer<Node extends Object, Statement extends Node,
|
|||
/// `default` clause.
|
||||
bool isSwitchExhaustive(Node node, Type expressionType);
|
||||
|
||||
/// Queries whether [pattern] is a variable pattern.
|
||||
bool isVariablePattern(Node pattern);
|
||||
|
||||
/// Returns the type `List`, with type parameter [elementType].
|
||||
Type listType(Type elementType);
|
||||
|
||||
|
@ -956,332 +1135,3 @@ class VariableTypeInfo<Node extends Object, Type extends Object> {
|
|||
/// The static type of this variable.
|
||||
Type get staticType => _latestStaticType;
|
||||
}
|
||||
|
||||
/// Specialization of [PatternDispatchResult] returned by
|
||||
/// [TypeAnalyzer.analyzeCastPattern]
|
||||
class _CastPatternDispatchResult<Node extends Object, Expression extends Node,
|
||||
Variable extends Object, Type extends Object>
|
||||
extends _PatternDispatchResultImpl<Node, Expression, Variable, Type> {
|
||||
final PatternDispatchResult<Node, Expression, Variable, Type> _innerPattern;
|
||||
|
||||
final Type _type;
|
||||
|
||||
_CastPatternDispatchResult(
|
||||
super._typeAnalyzer, super.node, this._innerPattern, this._type);
|
||||
|
||||
@override
|
||||
Type get typeSchema => _typeAnalyzer.objectQuestionType;
|
||||
|
||||
@override
|
||||
void match(
|
||||
Type matchedType,
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context) {
|
||||
_innerPattern.match(_type, typeInfos, context);
|
||||
// Stack: (Pattern)
|
||||
_typeAnalyzer.handleCastPattern(node, matchedType: matchedType);
|
||||
// Stack: (Pattern)
|
||||
}
|
||||
}
|
||||
|
||||
/// Specialization of [PatternDispatchResult] returned by
|
||||
/// [TypeAnalyzer.analyzeConstantPattern]
|
||||
class _ConstantPatternDispatchResult<Node extends Object,
|
||||
Expression extends Node, Variable extends Object, Type extends Object>
|
||||
extends _PatternDispatchResultImpl<Node, Expression, Variable, Type> {
|
||||
/// The constant or literal expression.
|
||||
///
|
||||
/// Depending on the client's representation, this might or might not be
|
||||
/// identical to [node].
|
||||
final Expression _expression;
|
||||
|
||||
_ConstantPatternDispatchResult(
|
||||
super.typeAnalyzer, super.node, this._expression);
|
||||
|
||||
@override
|
||||
Type get typeSchema {
|
||||
// Constant patterns are only allowed in refutable contexts, and refutable
|
||||
// contexts don't propagate a type schema into the scrutinee. So this
|
||||
// code path is only reachable if the user's code contains errors.
|
||||
_typeAnalyzer.errors?.assertInErrorRecovery();
|
||||
return _typeAnalyzer.unknownType;
|
||||
}
|
||||
|
||||
@override
|
||||
void match(
|
||||
Type matchedType,
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context) {
|
||||
// Stack: ()
|
||||
Node? irrefutableContext = context.irrefutableContext;
|
||||
if (irrefutableContext != null) {
|
||||
_typeAnalyzer.errors
|
||||
?.refutablePatternInIrrefutableContext(node, irrefutableContext);
|
||||
}
|
||||
Type staticType = _typeAnalyzer.analyzeExpression(_expression, matchedType);
|
||||
// Stack: (Expression)
|
||||
TypeAnalyzerErrors<Node, Node, Expression, Variable, Type>? errors =
|
||||
_typeAnalyzer.errors;
|
||||
TypeAnalyzerOptions options = _typeAnalyzer.options;
|
||||
if (errors != null && !options.patternsEnabled) {
|
||||
Expression? switchScrutinee = context.getSwitchScrutinee(node);
|
||||
if (switchScrutinee != null) {
|
||||
TypeOperations2<Type> typeOperations = _typeAnalyzer.typeOperations;
|
||||
bool nullSafetyEnabled = options.nullSafetyEnabled;
|
||||
bool matches = nullSafetyEnabled
|
||||
? typeOperations.isSubtypeOf(staticType, matchedType)
|
||||
: typeOperations.isAssignableTo(staticType, matchedType);
|
||||
if (!matches) {
|
||||
errors.caseExpressionTypeMismatch(
|
||||
caseExpression: _expression,
|
||||
scrutinee: switchScrutinee,
|
||||
caseExpressionType: staticType,
|
||||
scrutineeType: matchedType,
|
||||
nullSafetyEnabled: nullSafetyEnabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
_typeAnalyzer.handleConstantPattern(node, matchedType: matchedType);
|
||||
// Stack: (Pattern)
|
||||
}
|
||||
}
|
||||
|
||||
/// Specialization of [PatternDispatchResult] returned by
|
||||
/// [TypeAnalyzer.analyzeListPattern]
|
||||
class _ListPatternDispatchResult<Node extends Object, Expression extends Node,
|
||||
Variable extends Object, Type extends Object>
|
||||
extends _PatternDispatchResultImpl<Node, Expression, Variable, Type> {
|
||||
final Type? _elementType;
|
||||
|
||||
final List<PatternDispatchResult<Node, Expression, Variable, Type>> _elements;
|
||||
|
||||
_ListPatternDispatchResult(
|
||||
super.typeAnalyzer, super._node, this._elementType, this._elements);
|
||||
|
||||
@override
|
||||
Type get typeSchema {
|
||||
Type? elementType = _elementType;
|
||||
if (elementType == null) {
|
||||
if (_elements.isEmpty) {
|
||||
return _typeAnalyzer.objectQuestionType;
|
||||
}
|
||||
elementType = _elements[0].typeSchema;
|
||||
for (int i = 1; i < _elements.length; i++) {
|
||||
elementType = _typeAnalyzer.typeOperations
|
||||
.glb(elementType!, _elements[i].typeSchema);
|
||||
}
|
||||
}
|
||||
return _typeAnalyzer.listType(elementType!);
|
||||
}
|
||||
|
||||
@override
|
||||
void match(
|
||||
Type matchedType,
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context) {
|
||||
// Stack: ()
|
||||
Type? elementType = _typeAnalyzer.typeOperations.matchListType(matchedType);
|
||||
if (elementType == null) {
|
||||
if (_typeAnalyzer.typeOperations.isDynamic(matchedType)) {
|
||||
elementType = _typeAnalyzer.dynamicType;
|
||||
} else {
|
||||
elementType = _typeAnalyzer.objectQuestionType;
|
||||
}
|
||||
}
|
||||
for (PatternDispatchResult<Node, Expression, Variable, Type> element
|
||||
in _elements) {
|
||||
element.match(elementType, typeInfos, context);
|
||||
}
|
||||
// Stack: (n * Pattern) where n = _elements.length
|
||||
Type? requiredType = _typeAnalyzer.listType(_elementType ?? elementType);
|
||||
Node? irrefutableContext = context.irrefutableContext;
|
||||
if (irrefutableContext != null &&
|
||||
!_typeAnalyzer.typeOperations
|
||||
.isAssignableTo(matchedType, requiredType)) {
|
||||
_typeAnalyzer.errors?.patternTypeMismatchInIrrefutableContext(
|
||||
pattern: node,
|
||||
context: irrefutableContext,
|
||||
matchedType: matchedType,
|
||||
requiredType: requiredType);
|
||||
}
|
||||
_typeAnalyzer.handleListPattern(node, _elements.length,
|
||||
matchedType: matchedType, requiredType: requiredType);
|
||||
// Stack: (Pattern)
|
||||
}
|
||||
}
|
||||
|
||||
/// Specialization of [PatternDispatchResult] returned by
|
||||
/// [TypeAnalyzer.analyzeLogicalPattern]
|
||||
class _LogicalPatternDispatchResult<Node extends Object,
|
||||
Expression extends Node, Variable extends Object, Type extends Object>
|
||||
extends _PatternDispatchResultImpl<Node, Expression, Variable, Type> {
|
||||
final PatternDispatchResult<Node, Expression, Variable, Type> _lhs;
|
||||
|
||||
final PatternDispatchResult<Node, Expression, Variable, Type> _rhs;
|
||||
|
||||
final bool _isAnd;
|
||||
|
||||
_LogicalPatternDispatchResult(
|
||||
super._typeAnalyzer, super.node, this._lhs, this._rhs, this._isAnd);
|
||||
|
||||
@override
|
||||
Type get typeSchema {
|
||||
if (_isAnd) {
|
||||
return _typeAnalyzer.typeOperations.glb(_lhs.typeSchema, _rhs.typeSchema);
|
||||
} else {
|
||||
// Logical-or patterns are only allowed in refutable contexts, and
|
||||
// refutable contexts don't propagate a type schema into the scrutinee.
|
||||
// So this code path is only reachable if the user's code contains errors.
|
||||
_typeAnalyzer.errors?.assertInErrorRecovery();
|
||||
return _typeAnalyzer.unknownType;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void match(
|
||||
Type matchedType,
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context) {
|
||||
// Stack: ()
|
||||
if (!_isAnd) {
|
||||
Node? irrefutableContext = context.irrefutableContext;
|
||||
if (irrefutableContext != null) {
|
||||
_typeAnalyzer.errors
|
||||
?.refutablePatternInIrrefutableContext(node, irrefutableContext);
|
||||
// Avoid cascading errors
|
||||
context = context.makeRefutable();
|
||||
}
|
||||
}
|
||||
_lhs.match(matchedType, typeInfos, context);
|
||||
// Stack: (Pattern left)
|
||||
_rhs.match(matchedType, typeInfos, context);
|
||||
// Stack: (Pattern left, Pattern right)
|
||||
_typeAnalyzer.handleLogicalPattern(node,
|
||||
isAnd: _isAnd, matchedType: matchedType);
|
||||
// Stack: (Pattern)
|
||||
}
|
||||
}
|
||||
|
||||
/// Specialization of [PatternDispatchResult] returned by
|
||||
/// [TypeAnalyzer.analyzeNullCheckOrAssertPattern]
|
||||
class _NullCheckOrAssertPatternDispatchResult<Node extends Object,
|
||||
Expression extends Node, Variable extends Object, Type extends Object>
|
||||
extends _PatternDispatchResultImpl<Node, Expression, Variable, Type> {
|
||||
final PatternDispatchResult<Node, Expression, Variable, Type> _innerPattern;
|
||||
|
||||
final bool _isAssert;
|
||||
|
||||
_NullCheckOrAssertPatternDispatchResult(
|
||||
super._typeAnalyzer, super.node, this._innerPattern, this._isAssert);
|
||||
|
||||
@override
|
||||
Type get typeSchema {
|
||||
if (_isAssert) {
|
||||
return _typeAnalyzer.typeOperations
|
||||
.makeNullable(_innerPattern.typeSchema);
|
||||
} else {
|
||||
// Null-check patterns are only allowed in refutable contexts, and
|
||||
// refutable contexts don't propagate a type schema into the scrutinee.
|
||||
// So this code path is only reachable if the user's code contains errors.
|
||||
_typeAnalyzer.errors?.assertInErrorRecovery();
|
||||
return _typeAnalyzer.unknownType;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void match(
|
||||
Type matchedType,
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context) {
|
||||
// Stack: ()
|
||||
Type innerMatchedType =
|
||||
_typeAnalyzer.typeOperations.promoteToNonNull(matchedType);
|
||||
Node? irrefutableContext = context.irrefutableContext;
|
||||
if (irrefutableContext != null && !_isAssert) {
|
||||
_typeAnalyzer.errors
|
||||
?.refutablePatternInIrrefutableContext(node, irrefutableContext);
|
||||
// Avoid cascading errors
|
||||
context = context.makeRefutable();
|
||||
}
|
||||
_innerPattern.match(innerMatchedType, typeInfos, context);
|
||||
// Stack: (Pattern)
|
||||
_typeAnalyzer.handleNullCheckOrAssertPattern(node,
|
||||
matchedType: matchedType, isAssert: _isAssert);
|
||||
// Stack: (Pattern)
|
||||
}
|
||||
}
|
||||
|
||||
/// Common base class for all specializations of [PatternDispatchResult]
|
||||
/// returned by methods in [TypeAnalyzer].
|
||||
abstract class _PatternDispatchResultImpl<Node extends Object,
|
||||
Expression extends Node, Variable extends Object, Type extends Object>
|
||||
implements PatternDispatchResult<Node, Expression, Variable, Type> {
|
||||
/// Pointer back to the [TypeAnalyzer].
|
||||
final TypeAnalyzer<Node, Node, Expression, Variable, Type> _typeAnalyzer;
|
||||
|
||||
@override
|
||||
final Node node;
|
||||
|
||||
_PatternDispatchResultImpl(this._typeAnalyzer, this.node);
|
||||
}
|
||||
|
||||
/// Specialization of [PatternDispatchResult] returned by
|
||||
/// [TypeAnalyzer.analyzeVariablePattern]
|
||||
class _VariablePatternDispatchResult<Node extends Object,
|
||||
Expression extends Node, Variable extends Object, Type extends Object>
|
||||
extends _PatternDispatchResultImpl<Node, Expression, Variable, Type> {
|
||||
final Variable? _variable;
|
||||
|
||||
final Type? _declaredType;
|
||||
|
||||
final bool _isFinal;
|
||||
|
||||
_VariablePatternDispatchResult(super._typeAnalyzer, super.node,
|
||||
this._variable, this._declaredType, this._isFinal);
|
||||
|
||||
@override
|
||||
Type get typeSchema => _declaredType ?? _typeAnalyzer.unknownType;
|
||||
|
||||
@override
|
||||
void match(
|
||||
Type matchedType,
|
||||
Map<Variable, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context) {
|
||||
// Stack: ()
|
||||
Type staticType = _declaredType ??
|
||||
_typeAnalyzer.variableTypeFromInitializerType(matchedType);
|
||||
Node? irrefutableContext = context.irrefutableContext;
|
||||
if (irrefutableContext != null &&
|
||||
!_typeAnalyzer.typeOperations.isAssignableTo(matchedType, staticType)) {
|
||||
_typeAnalyzer.errors?.patternTypeMismatchInIrrefutableContext(
|
||||
pattern: node,
|
||||
context: irrefutableContext,
|
||||
matchedType: matchedType,
|
||||
requiredType: staticType);
|
||||
}
|
||||
bool isImplicitlyTyped = _declaredType == null;
|
||||
Variable? variable = _variable;
|
||||
if (variable != null) {
|
||||
bool isFirstMatch = _typeAnalyzer._recordTypeInfo(typeInfos,
|
||||
pattern: node,
|
||||
variable: variable,
|
||||
staticType: staticType,
|
||||
isImplicitlyTyped: isImplicitlyTyped);
|
||||
if (isFirstMatch) {
|
||||
_typeAnalyzer.flow?.declare(variable, false);
|
||||
_typeAnalyzer.setVariableType(variable, staticType);
|
||||
// TODO(paulberry): are we handling _isFinal correctly?
|
||||
// TODO(paulberry): do we need to verify that all instances of a
|
||||
// variable are final or all are not final?
|
||||
_typeAnalyzer.flow?.initialize(
|
||||
variable, matchedType, context.getInitializer(node),
|
||||
isFinal: context.isFinal || _isFinal,
|
||||
isLate: context.isLate,
|
||||
isImplicitlyTyped: isImplicitlyTyped);
|
||||
}
|
||||
}
|
||||
_typeAnalyzer.handleVariablePattern(node,
|
||||
matchedType: matchedType, staticType: staticType);
|
||||
// Stack: (Pattern)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -968,6 +968,8 @@ abstract class Pattern extends Node with CaseHead, CaseHeads {
|
|||
Pattern as_(String type) =>
|
||||
new _CastPattern(this, Type(type), location: computeLocation());
|
||||
|
||||
Type computeSchema(Harness h);
|
||||
|
||||
Pattern or(Pattern other) =>
|
||||
_LogicalPattern(this, other, isAnd: false, location: computeLocation());
|
||||
|
||||
|
@ -977,7 +979,11 @@ abstract class Pattern extends Node with CaseHead, CaseHeads {
|
|||
@override
|
||||
String toString() => _debugString(needsKeywordOrType: true);
|
||||
|
||||
PatternDispatchResult<Node, Expression, Var, Type> visit(Harness h);
|
||||
void visit(
|
||||
Harness h,
|
||||
Type matchedType,
|
||||
Map<Var, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context);
|
||||
|
||||
CaseHead when(Expression guard) =>
|
||||
_GuardedCaseHead(this, guard, location: location);
|
||||
|
@ -1245,6 +1251,8 @@ class _CastPattern extends Pattern {
|
|||
|
||||
_CastPattern(this._inner, this._type, {required super.location}) : super._();
|
||||
|
||||
Type computeSchema(Harness h) => h.typeAnalyzer.analyzeCastPatternSchema();
|
||||
|
||||
@override
|
||||
void preVisit(
|
||||
PreVisitor visitor, VariableBinder<Node, Var, Type> variableBinder) {
|
||||
|
@ -1252,8 +1260,18 @@ class _CastPattern extends Pattern {
|
|||
}
|
||||
|
||||
@override
|
||||
PatternDispatchResult<Node, Expression, Var, Type> visit(Harness h) {
|
||||
return h.typeAnalyzer.analyzeCastPattern(this, _inner, _type);
|
||||
void visit(
|
||||
Harness h,
|
||||
Type matchedType,
|
||||
Map<Var, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context) {
|
||||
h.typeAnalyzer
|
||||
.analyzeCastPattern(matchedType, typeInfos, context, _inner, _type);
|
||||
h.irBuilder.atom(_type.type, Kind.type, location: location);
|
||||
h.irBuilder.atom(matchedType.type, Kind.type, location: location);
|
||||
h.irBuilder.apply(
|
||||
'castPattern', [Kind.pattern, Kind.type, Kind.type], Kind.pattern,
|
||||
names: ['matchedType'], location: location);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -1513,15 +1531,26 @@ class _ConstantPattern extends Pattern {
|
|||
|
||||
_ConstantPattern(this.constant, {required super.location}) : super._();
|
||||
|
||||
Type computeSchema(Harness h) =>
|
||||
h.typeAnalyzer.analyzeConstantPatternSchema();
|
||||
|
||||
@override
|
||||
void preVisit(
|
||||
PreVisitor visitor, VariableBinder<Node, Var, Type> variableBinder) {
|
||||
constant.preVisit(visitor);
|
||||
}
|
||||
|
||||
@override
|
||||
PatternDispatchResult<Node, Expression, Var, Type> visit(Harness h) =>
|
||||
h.typeAnalyzer.analyzeConstantPattern(this, constant);
|
||||
void visit(
|
||||
Harness h,
|
||||
Type matchedType,
|
||||
Map<Var, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context) {
|
||||
h.typeAnalyzer.analyzeConstantPattern(
|
||||
matchedType, typeInfos, context, this, constant);
|
||||
h.irBuilder.atom(matchedType.type, Kind.type, location: location);
|
||||
h.irBuilder.apply('const', [Kind.expression, Kind.type], Kind.pattern,
|
||||
names: ['matchedType'], location: location);
|
||||
}
|
||||
|
||||
@override
|
||||
_debugString({required bool needsKeywordOrType}) => constant.toString();
|
||||
|
@ -2040,6 +2069,9 @@ class _ListPattern extends Pattern {
|
|||
_ListPattern(this._elementType, this._elements, {required super.location})
|
||||
: super._();
|
||||
|
||||
Type computeSchema(Harness h) => h.typeAnalyzer
|
||||
.analyzeListPatternSchema(elementType: _elementType, elements: _elements);
|
||||
|
||||
@override
|
||||
void preVisit(
|
||||
PreVisitor visitor, VariableBinder<Node, Var, Type> variableBinder) {
|
||||
|
@ -2048,10 +2080,22 @@ class _ListPattern extends Pattern {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
PatternDispatchResult<Node, Expression, Var, Type> visit(Harness h) {
|
||||
return h.typeAnalyzer.analyzeListPattern(this,
|
||||
void visit(
|
||||
Harness h,
|
||||
Type matchedType,
|
||||
Map<Var, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context) {
|
||||
var requiredType = h.typeAnalyzer.analyzeListPattern(
|
||||
matchedType, typeInfos, context, this,
|
||||
elementType: _elementType, elements: _elements);
|
||||
h.irBuilder.atom(matchedType.type, Kind.type, location: location);
|
||||
h.irBuilder.atom(requiredType.type, Kind.type, location: location);
|
||||
h.irBuilder.apply(
|
||||
'listPattern',
|
||||
[...List.filled(_elements.length, Kind.pattern), Kind.type, Kind.type],
|
||||
Kind.pattern,
|
||||
names: ['matchedType', 'requiredType'],
|
||||
location: location);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -2129,6 +2173,9 @@ class _LogicalPattern extends Pattern {
|
|||
{required this.isAnd, required super.location})
|
||||
: super._();
|
||||
|
||||
Type computeSchema(Harness h) =>
|
||||
h.typeAnalyzer.analyzeLogicalPatternSchema(_lhs, _rhs, isAnd: isAnd);
|
||||
|
||||
@override
|
||||
void preVisit(
|
||||
PreVisitor visitor, VariableBinder<Node, Var, Type> variableBinder) {
|
||||
|
@ -2148,9 +2195,18 @@ class _LogicalPattern extends Pattern {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
PatternDispatchResult<Node, Expression, Var, Type> visit(Harness h) {
|
||||
return h.typeAnalyzer.analyzeLogicalPattern(this, _lhs, _rhs, isAnd: isAnd);
|
||||
void visit(
|
||||
Harness h,
|
||||
Type matchedType,
|
||||
Map<Var, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context) {
|
||||
h.typeAnalyzer.analyzeLogicalPattern(
|
||||
matchedType, typeInfos, context, this, _lhs, _rhs,
|
||||
isAnd: isAnd);
|
||||
h.irBuilder.atom(matchedType.type, Kind.type, location: location);
|
||||
h.irBuilder.apply(isAnd ? 'logicalAndPattern' : 'logicalOrPattern',
|
||||
[Kind.pattern, Kind.pattern, Kind.type], Kind.pattern,
|
||||
names: ['matchedType'], location: location);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -2588,9 +2644,17 @@ class _MiniAstTypeAnalyzer
|
|||
_irBuilder.guard(expression, () => expression.visit(_harness, context));
|
||||
|
||||
@override
|
||||
PatternDispatchResult<Node, Expression, Var, Type> dispatchPattern(
|
||||
void dispatchPattern(
|
||||
Type matchedType,
|
||||
Map<Var, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context,
|
||||
covariant Pattern node) {
|
||||
return node.visit(_harness);
|
||||
return node.visit(_harness, matchedType, typeInfos, context);
|
||||
}
|
||||
|
||||
@override
|
||||
Type dispatchPatternSchema(covariant Pattern node) {
|
||||
return node.computeSchema(_harness);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -2644,50 +2708,11 @@ class _MiniAstTypeAnalyzer
|
|||
location: node.location);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleCastPattern(covariant _CastPattern node,
|
||||
{required Type matchedType}) {
|
||||
_irBuilder.atom(node._type.type, Kind.type, location: node.location);
|
||||
_irBuilder.atom(matchedType.type, Kind.type, location: node.location);
|
||||
_irBuilder.apply(
|
||||
'castPattern', [Kind.pattern, Kind.type, Kind.type], Kind.pattern,
|
||||
names: ['matchedType'], location: node.location);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleConstantPattern(Node node, {required Type matchedType}) {
|
||||
_irBuilder.atom(matchedType.type, Kind.type, location: node.location);
|
||||
_irBuilder.apply('const', [Kind.expression, Kind.type], Kind.pattern,
|
||||
names: ['matchedType'], location: node.location);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleDefault(Node node, int caseIndex) {
|
||||
_irBuilder.atom('default', Kind.caseHead, location: node.location);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleListPattern(Node node, int numElements,
|
||||
{required Type matchedType, required Type requiredType}) {
|
||||
_irBuilder.atom(matchedType.type, Kind.type, location: node.location);
|
||||
_irBuilder.atom(requiredType.type, Kind.type, location: node.location);
|
||||
_irBuilder.apply(
|
||||
'listPattern',
|
||||
[...List.filled(numElements, Kind.pattern), Kind.type, Kind.type],
|
||||
Kind.pattern,
|
||||
names: ['matchedType', 'requiredType'],
|
||||
location: node.location);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleLogicalPattern(Node node,
|
||||
{required bool isAnd, required Type matchedType}) {
|
||||
_irBuilder.atom(matchedType.type, Kind.type, location: node.location);
|
||||
_irBuilder.apply(isAnd ? 'logicalAndPattern' : 'logicalOrPattern',
|
||||
[Kind.pattern, Kind.pattern, Kind.type], Kind.pattern,
|
||||
names: ['matchedType'], location: node.location);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleMergedStatementCase(Statement node,
|
||||
{required int caseIndex,
|
||||
|
@ -2723,19 +2748,9 @@ class _MiniAstTypeAnalyzer
|
|||
_irBuilder.atom('noop', Kind.statement, location: node.location);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleNullCheckOrAssertPattern(Node node,
|
||||
{required Type matchedType, required bool isAssert}) {
|
||||
_irBuilder.atom(matchedType.type, Kind.type, location: node.location);
|
||||
_irBuilder.apply(isAssert ? 'nullAssertPattern' : 'nullCheckPattern',
|
||||
[Kind.pattern, Kind.type], Kind.pattern,
|
||||
names: ['matchedType'], location: node.location);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleSwitchScrutinee(Type type) {}
|
||||
|
||||
@override
|
||||
void handleVariablePattern(covariant _VariablePattern node,
|
||||
{required Type matchedType, required Type staticType}) {
|
||||
_irBuilder.atom(node.variable?.name ?? '_', Kind.variable,
|
||||
|
@ -2757,6 +2772,9 @@ class _MiniAstTypeAnalyzer
|
|||
return node.isExhaustive;
|
||||
}
|
||||
|
||||
@override
|
||||
bool isVariablePattern(Node pattern) => pattern is _VariablePattern;
|
||||
|
||||
Type leastUpperBound(Type t1, Type t2) => _harness._lub(t1, t2);
|
||||
|
||||
@override
|
||||
|
@ -2889,16 +2907,27 @@ class _NullCheckOrAssertPattern extends Pattern {
|
|||
{required super.location})
|
||||
: super._();
|
||||
|
||||
Type computeSchema(Harness h) => h.typeAnalyzer
|
||||
.analyzeNullCheckOrAssertPatternSchema(_inner, isAssert: _isAssert);
|
||||
|
||||
@override
|
||||
void preVisit(
|
||||
PreVisitor visitor, VariableBinder<Node, Var, Type> variableBinder) {
|
||||
_inner.preVisit(visitor, variableBinder);
|
||||
}
|
||||
|
||||
@override
|
||||
PatternDispatchResult<Node, Expression, Var, Type> visit(Harness h) {
|
||||
return h.typeAnalyzer
|
||||
.analyzeNullCheckOrAssertPattern(this, _inner, isAssert: _isAssert);
|
||||
void visit(
|
||||
Harness h,
|
||||
Type matchedType,
|
||||
Map<Var, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context) {
|
||||
h.typeAnalyzer.analyzeNullCheckOrAssertPattern(
|
||||
matchedType, typeInfos, context, this, _inner,
|
||||
isAssert: _isAssert);
|
||||
h.irBuilder.atom(matchedType.type, Kind.type, location: location);
|
||||
h.irBuilder.apply(_isAssert ? 'nullAssertPattern' : 'nullCheckPattern',
|
||||
[Kind.pattern, Kind.type], Kind.pattern,
|
||||
names: ['matchedType'], location: location);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -3301,6 +3330,9 @@ class _VariablePattern extends Pattern {
|
|||
{this.isFinal = false, required super.location})
|
||||
: super._();
|
||||
|
||||
Type computeSchema(Harness h) =>
|
||||
h.typeAnalyzer.analyzeVariablePatternSchema(declaredType);
|
||||
|
||||
@override
|
||||
void preVisit(
|
||||
PreVisitor visitor, VariableBinder<Node, Var, Type> variableBinder) {
|
||||
|
@ -3310,10 +3342,16 @@ class _VariablePattern extends Pattern {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
PatternDispatchResult<Node, Expression, Var, Type> visit(Harness h) {
|
||||
return h.typeAnalyzer
|
||||
.analyzeVariablePattern(this, variable, declaredType, isFinal: isFinal);
|
||||
void visit(
|
||||
Harness h,
|
||||
Type matchedType,
|
||||
Map<Var, VariableTypeInfo<Node, Type>> typeInfos,
|
||||
MatchContext<Node, Expression> context) {
|
||||
var staticType = h.typeAnalyzer.analyzeVariablePattern(
|
||||
matchedType, typeInfos, context, this, variable, declaredType,
|
||||
isFinal: isFinal);
|
||||
h.typeAnalyzer.handleVariablePattern(this,
|
||||
matchedType: matchedType, staticType: staticType);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
import 'dart:collection';
|
||||
import 'dart:math' as math;
|
||||
|
||||
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:analyzer/dart/analysis/features.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/ast/precedence.dart';
|
||||
|
@ -1111,6 +1113,19 @@ class BinaryPatternImpl extends DartPatternImpl implements BinaryPattern {
|
|||
@override
|
||||
E? accept<E>(AstVisitor<E> visitor) => visitor.visitBinaryPattern(this);
|
||||
|
||||
@override
|
||||
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
|
||||
@override
|
||||
void resolvePattern(
|
||||
ResolverVisitor resolverVisitor,
|
||||
DartType matchedType,
|
||||
Map<PromotableElement, VariableTypeInfo<AstNode, DartType>> typeInfos,
|
||||
MatchContext<AstNode, Expression> context) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void visitChildren(AstVisitor visitor) {
|
||||
leftOperand.accept(visitor);
|
||||
|
@ -1518,6 +1533,19 @@ class CastPatternImpl extends DartPatternImpl implements CastPattern {
|
|||
@override
|
||||
E? accept<E>(AstVisitor<E> visitor) => visitor.visitCastPattern(this);
|
||||
|
||||
@override
|
||||
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
|
||||
@override
|
||||
void resolvePattern(
|
||||
ResolverVisitor resolverVisitor,
|
||||
DartType matchedType,
|
||||
Map<PromotableElement, VariableTypeInfo<AstNode, DartType>> typeInfos,
|
||||
MatchContext<AstNode, Expression> context) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void visitChildren(AstVisitor visitor) {
|
||||
type.accept(visitor);
|
||||
|
@ -2730,6 +2758,19 @@ class ConstantPatternImpl extends DartPatternImpl implements ConstantPattern {
|
|||
@override
|
||||
E? accept<E>(AstVisitor<E> visitor) => visitor.visitConstantPattern(this);
|
||||
|
||||
@override
|
||||
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
|
||||
@override
|
||||
void resolvePattern(
|
||||
ResolverVisitor resolverVisitor,
|
||||
DartType matchedType,
|
||||
Map<PromotableElement, VariableTypeInfo<AstNode, DartType>> typeInfos,
|
||||
MatchContext<AstNode, Expression> context) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void visitChildren(AstVisitor visitor) {
|
||||
expression.accept(visitor);
|
||||
|
@ -3269,6 +3310,14 @@ abstract class DartPatternImpl extends AstNodeImpl implements DartPattern {
|
|||
// TODO(brianwilkerson) Remove this and implement it in subclasses when we
|
||||
// have constants for pattern-related precedence values.
|
||||
Precedence get precedence => throw UnimplementedError();
|
||||
|
||||
DartType computePatternSchema(ResolverVisitor resolverVisitor);
|
||||
|
||||
void resolvePattern(
|
||||
ResolverVisitor resolverVisitor,
|
||||
DartType matchedType,
|
||||
Map<PromotableElement, VariableTypeInfo<AstNode, DartType>> typeInfos,
|
||||
MatchContext<AstNode, Expression> context);
|
||||
}
|
||||
|
||||
/// A node that represents the declaration of one or more names. Each declared
|
||||
|
@ -4636,6 +4685,19 @@ class ExtractorPatternImpl extends DartPatternImpl implements ExtractorPattern {
|
|||
@override
|
||||
E? accept<E>(AstVisitor<E> visitor) => visitor.visitExtractorPattern(this);
|
||||
|
||||
@override
|
||||
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
|
||||
@override
|
||||
void resolvePattern(
|
||||
ResolverVisitor resolverVisitor,
|
||||
DartType matchedType,
|
||||
Map<PromotableElement, VariableTypeInfo<AstNode, DartType>> typeInfos,
|
||||
MatchContext<AstNode, Expression> context) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void visitChildren(AstVisitor visitor) {
|
||||
typeName.accept(visitor);
|
||||
|
@ -7971,6 +8033,19 @@ class ListPatternImpl extends DartPatternImpl implements ListPattern {
|
|||
@override
|
||||
E? accept<E>(AstVisitor<E> visitor) => visitor.visitListPattern(this);
|
||||
|
||||
@override
|
||||
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
|
||||
@override
|
||||
void resolvePattern(
|
||||
ResolverVisitor resolverVisitor,
|
||||
DartType matchedType,
|
||||
Map<PromotableElement, VariableTypeInfo<AstNode, DartType>> typeInfos,
|
||||
MatchContext<AstNode, Expression> context) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void visitChildren(AstVisitor visitor) {
|
||||
typeArguments?.accept(visitor);
|
||||
|
@ -8165,6 +8240,19 @@ class MapPatternImpl extends DartPatternImpl implements MapPattern {
|
|||
@override
|
||||
E? accept<E>(AstVisitor<E> visitor) => visitor.visitMapPattern(this);
|
||||
|
||||
@override
|
||||
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
|
||||
@override
|
||||
void resolvePattern(
|
||||
ResolverVisitor resolverVisitor,
|
||||
DartType matchedType,
|
||||
Map<PromotableElement, VariableTypeInfo<AstNode, DartType>> typeInfos,
|
||||
MatchContext<AstNode, Expression> context) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void visitChildren(AstVisitor visitor) {
|
||||
typeArguments?.accept(visitor);
|
||||
|
@ -9383,6 +9471,19 @@ class ParenthesizedPatternImpl extends DartPatternImpl
|
|||
E? accept<E>(AstVisitor<E> visitor) =>
|
||||
visitor.visitParenthesizedPattern(this);
|
||||
|
||||
@override
|
||||
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
|
||||
@override
|
||||
void resolvePattern(
|
||||
ResolverVisitor resolverVisitor,
|
||||
DartType matchedType,
|
||||
Map<PromotableElement, VariableTypeInfo<AstNode, DartType>> typeInfos,
|
||||
MatchContext<AstNode, Expression> context) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void visitChildren(AstVisitor visitor) {
|
||||
pattern.accept(visitor);
|
||||
|
@ -9814,6 +9915,19 @@ class PostfixPatternImpl extends DartPatternImpl implements PostfixPattern {
|
|||
@override
|
||||
E? accept<E>(AstVisitor<E> visitor) => visitor.visitPostfixPattern(this);
|
||||
|
||||
@override
|
||||
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
|
||||
@override
|
||||
void resolvePattern(
|
||||
ResolverVisitor resolverVisitor,
|
||||
DartType matchedType,
|
||||
Map<PromotableElement, VariableTypeInfo<AstNode, DartType>> typeInfos,
|
||||
MatchContext<AstNode, Expression> context) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void visitChildren(AstVisitor visitor) {
|
||||
operand.accept(visitor);
|
||||
|
@ -10279,6 +10393,19 @@ class RecordPatternImpl extends DartPatternImpl implements RecordPattern {
|
|||
@override
|
||||
E? accept<E>(AstVisitor<E> visitor) => visitor.visitRecordPattern(this);
|
||||
|
||||
@override
|
||||
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
|
||||
@override
|
||||
void resolvePattern(
|
||||
ResolverVisitor resolverVisitor,
|
||||
DartType matchedType,
|
||||
Map<PromotableElement, VariableTypeInfo<AstNode, DartType>> typeInfos,
|
||||
MatchContext<AstNode, Expression> context) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void visitChildren(AstVisitor visitor) {
|
||||
fields.accept(visitor);
|
||||
|
@ -10562,6 +10689,19 @@ class RelationalPatternImpl extends DartPatternImpl
|
|||
@override
|
||||
E? accept<E>(AstVisitor<E> visitor) => visitor.visitRelationalPattern(this);
|
||||
|
||||
@override
|
||||
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
|
||||
@override
|
||||
void resolvePattern(
|
||||
ResolverVisitor resolverVisitor,
|
||||
DartType matchedType,
|
||||
Map<PromotableElement, VariableTypeInfo<AstNode, DartType>> typeInfos,
|
||||
MatchContext<AstNode, Expression> context) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void visitChildren(AstVisitor visitor) {
|
||||
operand.accept(visitor);
|
||||
|
@ -13237,6 +13377,19 @@ class VariablePatternImpl extends DartPatternImpl implements VariablePattern {
|
|||
@override
|
||||
E? accept<E>(AstVisitor<E> visitor) => visitor.visitVariablePattern(this);
|
||||
|
||||
@override
|
||||
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
|
||||
@override
|
||||
void resolvePattern(
|
||||
ResolverVisitor resolverVisitor,
|
||||
DartType matchedType,
|
||||
Map<PromotableElement, VariableTypeInfo<AstNode, DartType>> typeInfos,
|
||||
MatchContext<AstNode, Expression> context) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void visitChildren(AstVisitor visitor) {
|
||||
type?.accept(visitor);
|
||||
|
|
|
@ -707,15 +707,31 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
|||
}
|
||||
|
||||
@override
|
||||
PatternDispatchResult<AstNode, Expression, PromotableElement, DartType>
|
||||
dispatchPattern(AstNode pattern) {
|
||||
if (pattern is Expression) {
|
||||
return analyzeConstantPattern(pattern, pattern);
|
||||
void dispatchPattern(
|
||||
DartType matchedType,
|
||||
Map<PromotableElement, VariableTypeInfo<AstNode, DartType>> typeInfos,
|
||||
MatchContext<AstNode, Expression> context,
|
||||
AstNode node) {
|
||||
if (node is DartPatternImpl) {
|
||||
node.resolvePattern(this, matchedType, typeInfos, context);
|
||||
} else {
|
||||
throw UnimplementedError('TODO(paulberry): ${pattern.runtimeType}');
|
||||
// This can occur inside conventional switch statements, since
|
||||
// [SwitchCase] points directly to an [Expression] rather than to a
|
||||
// [ConstantPattern]. So we mimic what
|
||||
// [ConstantPatternImpl.resolvePattern] would do.
|
||||
analyzeConstantPattern(
|
||||
matchedType, typeInfos, context, node, node as Expression);
|
||||
// Stack: (Expression)
|
||||
popRewrite();
|
||||
// Stack: ()
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
DartType dispatchPatternSchema(covariant DartPatternImpl node) {
|
||||
return node.computePatternSchema(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispatchStatement(Statement statement) {
|
||||
statement.accept(this);
|
||||
|
@ -782,36 +798,11 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
|||
switchExhaustiveness!.visitSwitchMember(node.members[caseIndex]);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleCastPattern(AstNode node,
|
||||
{required DartType matchedType, DartType? staticType}) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void handleConstantPattern(AstNode node, {required DartType matchedType}) {
|
||||
// Stack: (Expression)
|
||||
popRewrite();
|
||||
// Stack: ()
|
||||
}
|
||||
|
||||
@override
|
||||
void handleDefault(covariant SwitchStatement node, int caseIndex) {
|
||||
switchExhaustiveness!.visitSwitchMember(node.members[caseIndex]);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleListPattern(AstNode node, int numElements,
|
||||
{required DartType matchedType, required DartType requiredType}) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void handleLogicalPattern(AstNode node,
|
||||
{required bool isAnd, required DartType matchedType}) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void handleMergedStatementCase(covariant SwitchStatement node,
|
||||
{required int caseIndex,
|
||||
|
@ -834,23 +825,11 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
|||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void handleNullCheckOrAssertPattern(AstNode node,
|
||||
{required DartType matchedType, required bool isAssert}) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
@override
|
||||
void handleSwitchScrutinee(DartType type) {
|
||||
switchExhaustiveness = SwitchExhaustiveness(type);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleVariablePattern(AstNode node,
|
||||
{required DartType matchedType, DartType? staticType}) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
|
||||
/// If generic function instantiation should be performed on `expression`,
|
||||
/// inserts a [FunctionReference] node which wraps [expression].
|
||||
///
|
||||
|
@ -911,6 +890,9 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
|||
bool isSwitchExhaustive(AstNode node, DartType expressionType) =>
|
||||
switchExhaustiveness!.isExhaustive;
|
||||
|
||||
@override
|
||||
bool isVariablePattern(AstNode pattern) => pattern is VariablePattern;
|
||||
|
||||
@override
|
||||
DartType listType(DartType elementType) {
|
||||
throw UnimplementedError('TODO(paulberry)');
|
||||
|
|
|
@ -832,6 +832,7 @@ mb
|
|||
mc
|
||||
md
|
||||
me
|
||||
meanings
|
||||
meeting
|
||||
merely
|
||||
meta
|
||||
|
|
Loading…
Reference in a new issue