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:
Paul Berry 2022-12-12 19:55:56 +00:00 committed by Commit Queue
parent 0b3533aa95
commit 1859824f74
3 changed files with 120 additions and 71 deletions

View file

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

View file

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

View file

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