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:
Paul Berry 2022-09-22 22:41:32 +00:00 committed by Commit Bot
parent 1139ed3373
commit 8da75ede06
6 changed files with 574 additions and 584 deletions

View file

@ -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>

View file

@ -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)
}
}

View file

@ -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

View file

@ -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);

View file

@ -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)');

View file

@ -832,6 +832,7 @@ mb
mc
md
me
meanings
meeting
merely
meta