mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 23:39:48 +00:00
Shared patterns analysis: Separate handling of logical and/or.
As I'm beginning to work on flow analysis for logical and/or patterns I'm realizing that the shared analysis methods for handling logical-and and logical-or patterns are going to need to have different parameters, so it makes sense to separate them. This makes the code clearer anyhow, since they weren't sharing any functionality. Bug: https://github.com/dart-lang/sdk/issues/50419 Change-Id: I17b0ad53f916f48ee02ccb1c9c23488261a6a1b6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274920 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
parent
0b3533aa95
commit
1859824f74
|
@ -697,58 +697,69 @@ mixin TypeAnalyzer<
|
|||
return listType(currentGLB);
|
||||
}
|
||||
|
||||
/// Analyzes a logical-or or logical-and pattern. [node] is the pattern
|
||||
/// itself, and [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.
|
||||
/// Analyzes a logical-and pattern. [node] is the pattern itself, and [lhs]
|
||||
/// and [rhs] are the left and right sides of the `&&` operator.
|
||||
///
|
||||
/// See [dispatchPattern] for the meanings of [matchedType] and [context].
|
||||
///
|
||||
/// Stack effect: pushes (Pattern left, Pattern right)
|
||||
void analyzeLogicalPattern(
|
||||
void analyzeLogicalAndPattern(
|
||||
Type matchedType,
|
||||
MatchContext<Node, Expression, Pattern, Type, Variable> context,
|
||||
Pattern node,
|
||||
Node lhs,
|
||||
Node rhs,
|
||||
{required bool isAnd}) {
|
||||
if (isAnd) {
|
||||
// Stack: ()
|
||||
dispatchPattern(matchedType, context, lhs);
|
||||
// Stack: (Pattern left)
|
||||
dispatchPattern(matchedType, context, rhs);
|
||||
// Stack: (Pattern left, Pattern right)
|
||||
} else {
|
||||
Node? irrefutableContext = context.irrefutableContext;
|
||||
if (irrefutableContext != null) {
|
||||
errors?.refutablePatternInIrrefutableContext(node, irrefutableContext);
|
||||
// Avoid cascading errors
|
||||
context = context.makeRefutable();
|
||||
}
|
||||
// Stack: ()
|
||||
dispatchPattern(matchedType, context, lhs);
|
||||
// Stack: (Pattern left)
|
||||
dispatchPattern(matchedType, context, rhs);
|
||||
// Stack: (Pattern left, Pattern right)
|
||||
}
|
||||
Node rhs) {
|
||||
// Stack: ()
|
||||
dispatchPattern(matchedType, context, lhs);
|
||||
// Stack: (Pattern left)
|
||||
dispatchPattern(matchedType, 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.
|
||||
/// Computes the type schema for a logical-and pattern. [lhs] and [rhs] are
|
||||
/// the left and right sides of the `&&` operator.
|
||||
///
|
||||
/// Stack effect: none.
|
||||
Type analyzeLogicalPatternSchema(Node lhs, Node rhs, {required bool isAnd}) {
|
||||
if (isAnd) {
|
||||
return operations.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;
|
||||
Type analyzeLogicalAndPatternSchema(Node lhs, Node rhs) {
|
||||
return operations.glb(
|
||||
dispatchPatternSchema(lhs), dispatchPatternSchema(rhs));
|
||||
}
|
||||
|
||||
/// Analyzes a logical-or pattern. [node] is the pattern itself, and [lhs]
|
||||
/// and [rhs] are the left and right sides of the `||` operator.
|
||||
///
|
||||
/// See [dispatchPattern] for the meanings of [matchedType] and [context].
|
||||
///
|
||||
/// Stack effect: pushes (Pattern left, Pattern right)
|
||||
void analyzeLogicalOrPattern(
|
||||
Type matchedType,
|
||||
MatchContext<Node, Expression, Pattern, Type, Variable> context,
|
||||
Pattern node,
|
||||
Node lhs,
|
||||
Node rhs) {
|
||||
Node? irrefutableContext = context.irrefutableContext;
|
||||
if (irrefutableContext != null) {
|
||||
errors?.refutablePatternInIrrefutableContext(node, irrefutableContext);
|
||||
// Avoid cascading errors
|
||||
context = context.makeRefutable();
|
||||
}
|
||||
// Stack: ()
|
||||
dispatchPattern(matchedType, context, lhs);
|
||||
// Stack: (Pattern left)
|
||||
dispatchPattern(matchedType, context, rhs);
|
||||
// Stack: (Pattern left, Pattern right)
|
||||
}
|
||||
|
||||
/// Computes the type schema for a logical-or pattern. [lhs] and [rhs] are
|
||||
/// the left and right sides of the `|` or `&` operator.
|
||||
///
|
||||
/// Stack effect: none.
|
||||
Type analyzeLogicalOrPatternSchema(Node lhs, Node rhs) {
|
||||
// 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 map pattern. [node] is the pattern itself, [typeArguments]
|
||||
|
|
|
@ -1279,7 +1279,7 @@ abstract class Pattern extends Node
|
|||
}
|
||||
|
||||
Pattern and(Pattern other) =>
|
||||
_LogicalPattern(this, other, isAnd: true, location: computeLocation());
|
||||
_LogicalAndPattern(this, other, location: computeLocation());
|
||||
|
||||
Pattern as_(String type) =>
|
||||
new _CastPattern(this, Type(type), location: computeLocation());
|
||||
|
@ -1291,7 +1291,7 @@ abstract class Pattern extends Node
|
|||
Type computeSchema(Harness h);
|
||||
|
||||
Pattern or(Pattern other) =>
|
||||
_LogicalPattern(this, other, isAnd: false, location: computeLocation());
|
||||
_LogicalOrPattern(this, other, location: computeLocation());
|
||||
|
||||
RecordPatternField recordField([String? name]) {
|
||||
return RecordPatternField(
|
||||
|
@ -2774,34 +2774,23 @@ class _Logical extends Expression {
|
|||
}
|
||||
}
|
||||
|
||||
class _LogicalPattern extends Pattern {
|
||||
class _LogicalAndPattern extends Pattern {
|
||||
final Pattern _lhs;
|
||||
|
||||
final Pattern _rhs;
|
||||
|
||||
final bool isAnd;
|
||||
|
||||
_LogicalPattern(this._lhs, this._rhs,
|
||||
{required this.isAnd, required super.location})
|
||||
_LogicalAndPattern(this._lhs, this._rhs, {required super.location})
|
||||
: super._();
|
||||
|
||||
@override
|
||||
Type computeSchema(Harness h) =>
|
||||
h.typeAnalyzer.analyzeLogicalPatternSchema(_lhs, _rhs, isAnd: isAnd);
|
||||
h.typeAnalyzer.analyzeLogicalAndPatternSchema(_lhs, _rhs);
|
||||
|
||||
@override
|
||||
void preVisit(PreVisitor visitor, VariableBinder<Node, Var> variableBinder,
|
||||
{required bool isInAssignment}) {
|
||||
if (isAnd) {
|
||||
_lhs.preVisit(visitor, variableBinder, isInAssignment: isInAssignment);
|
||||
_rhs.preVisit(visitor, variableBinder, isInAssignment: isInAssignment);
|
||||
} else {
|
||||
variableBinder.logicalOrPatternStart();
|
||||
_lhs.preVisit(visitor, variableBinder, isInAssignment: isInAssignment);
|
||||
variableBinder.logicalOrPatternFinishLeft();
|
||||
_rhs.preVisit(visitor, variableBinder, isInAssignment: isInAssignment);
|
||||
variableBinder.logicalOrPatternFinish(this);
|
||||
}
|
||||
_lhs.preVisit(visitor, variableBinder, isInAssignment: isInAssignment);
|
||||
_rhs.preVisit(visitor, variableBinder, isInAssignment: isInAssignment);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -2810,10 +2799,10 @@ class _LogicalPattern extends Pattern {
|
|||
Type matchedType,
|
||||
SharedMatchContext context,
|
||||
) {
|
||||
h.typeAnalyzer.analyzeLogicalPattern(matchedType, context, this, _lhs, _rhs,
|
||||
isAnd: isAnd);
|
||||
h.typeAnalyzer
|
||||
.analyzeLogicalAndPattern(matchedType, context, this, _lhs, _rhs);
|
||||
h.irBuilder.atom(matchedType.type, Kind.type, location: location);
|
||||
h.irBuilder.apply(isAnd ? 'logicalAndPattern' : 'logicalOrPattern',
|
||||
h.irBuilder.apply('logicalAndPattern',
|
||||
[Kind.pattern, Kind.pattern, Kind.type], Kind.pattern,
|
||||
names: ['matchedType'], location: location);
|
||||
}
|
||||
|
@ -2821,7 +2810,51 @@ class _LogicalPattern extends Pattern {
|
|||
@override
|
||||
_debugString({required bool needsKeywordOrType}) => [
|
||||
_lhs._debugString(needsKeywordOrType: false),
|
||||
isAnd ? '&' : '|',
|
||||
'&&',
|
||||
_rhs._debugString(needsKeywordOrType: false)
|
||||
].join(' ');
|
||||
}
|
||||
|
||||
class _LogicalOrPattern extends Pattern {
|
||||
final Pattern _lhs;
|
||||
|
||||
final Pattern _rhs;
|
||||
|
||||
_LogicalOrPattern(this._lhs, this._rhs, {required super.location})
|
||||
: super._();
|
||||
|
||||
@override
|
||||
Type computeSchema(Harness h) =>
|
||||
h.typeAnalyzer.analyzeLogicalOrPatternSchema(_lhs, _rhs);
|
||||
|
||||
@override
|
||||
void preVisit(PreVisitor visitor, VariableBinder<Node, Var> variableBinder,
|
||||
{required bool isInAssignment}) {
|
||||
variableBinder.logicalOrPatternStart();
|
||||
_lhs.preVisit(visitor, variableBinder, isInAssignment: isInAssignment);
|
||||
variableBinder.logicalOrPatternFinishLeft();
|
||||
_rhs.preVisit(visitor, variableBinder, isInAssignment: isInAssignment);
|
||||
variableBinder.logicalOrPatternFinish(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void visit(
|
||||
Harness h,
|
||||
Type matchedType,
|
||||
SharedMatchContext context,
|
||||
) {
|
||||
h.typeAnalyzer
|
||||
.analyzeLogicalOrPattern(matchedType, context, this, _lhs, _rhs);
|
||||
h.irBuilder.atom(matchedType.type, Kind.type, location: location);
|
||||
h.irBuilder.apply('logicalOrPattern',
|
||||
[Kind.pattern, Kind.pattern, Kind.type], Kind.pattern,
|
||||
names: ['matchedType'], location: location);
|
||||
}
|
||||
|
||||
@override
|
||||
_debugString({required bool needsKeywordOrType}) => [
|
||||
_lhs._debugString(needsKeywordOrType: false),
|
||||
'||',
|
||||
_rhs._debugString(needsKeywordOrType: false)
|
||||
].join(' ');
|
||||
}
|
||||
|
@ -4521,7 +4554,7 @@ class _VariableBinder extends VariableBinder<Node, Var> {
|
|||
components.first.name,
|
||||
components: [
|
||||
for (var variable in components)
|
||||
if (key is _LogicalPattern && variable is PatternVariableJoin)
|
||||
if (key is _LogicalOrPattern && variable is PatternVariableJoin)
|
||||
...variable.components
|
||||
else
|
||||
variable
|
||||
|
|
|
@ -1178,12 +1178,13 @@ class BinaryPatternImpl extends DartPatternImpl implements BinaryPattern {
|
|||
|
||||
@override
|
||||
DartType computePatternSchema(ResolverVisitor resolverVisitor) {
|
||||
return resolverVisitor.analyzeLogicalPatternSchema(
|
||||
leftOperand,
|
||||
rightOperand,
|
||||
isAnd: operator.type == TokenType.AMPERSAND ||
|
||||
operator.type == TokenType.AMPERSAND_AMPERSAND,
|
||||
);
|
||||
if (operator.type == TokenType.AMPERSAND_AMPERSAND) {
|
||||
return resolverVisitor.analyzeLogicalAndPatternSchema(
|
||||
leftOperand, rightOperand);
|
||||
} else {
|
||||
return resolverVisitor.analyzeLogicalOrPatternSchema(
|
||||
leftOperand, rightOperand);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -1194,9 +1195,13 @@ class BinaryPatternImpl extends DartPatternImpl implements BinaryPattern {
|
|||
) {
|
||||
assert(operator.type == TokenType.AMPERSAND_AMPERSAND ||
|
||||
operator.type == TokenType.BAR_BAR);
|
||||
resolverVisitor.analyzeLogicalPattern(
|
||||
matchedType, context, this, leftOperand, rightOperand,
|
||||
isAnd: operator.type == TokenType.AMPERSAND_AMPERSAND);
|
||||
if (operator.type == TokenType.AMPERSAND_AMPERSAND) {
|
||||
resolverVisitor.analyzeLogicalAndPattern(
|
||||
matchedType, context, this, leftOperand, rightOperand);
|
||||
} else {
|
||||
resolverVisitor.analyzeLogicalOrPattern(
|
||||
matchedType, context, this, leftOperand, rightOperand);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
Loading…
Reference in a new issue