Skip to content

Commit 5eacff3

Browse files
stereotype441Commit Queue
authored andcommitted
Make type variable constraint generation conventions uniform.
This CL changes the CFE's TypeConstraintGatherer._isNullabilityAwareSubtypeMatch method so that it is responsible for restoring the constraint state if there is no match, making it consistent with the contraint gathering methods in the analyzer and _fe_analyzer_shared. This made it possible to remove much of the calls to state restoring logic that _isNullabilityAwareSubtypeMatch previously had to do after making recursive calls to itself, as well as a lot of state restoring logic in _fe_analyzer_shared. It also made it possible to eliminate _tryNullabilityAwareSubtypeMatch from the CFE (since _isNullabilityAwareSubtypeMatch now has the same behavior). Making this change now should hopefully simplify the remaining steps in sharing type variable constraint generation logic, since it will no longer be necessary to adjust state restoring logic when moving code between the CFE and _fe_analyzer_shared. I also took the liberty of rewriting some of the documentation comments to try to clarify the new conventions. In the process I also discovered several instances of unnecessary state restoring logic in the analyzer; I'll make a separate CL to clean those up (and adjust the analyzer documentation too). Change-Id: If74c8be06f1d53f61d109e5ea2a8526d5cbcd347 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/388265 Commit-Queue: Chloe Stefantsova <[email protected]> Reviewed-by: Chloe Stefantsova <[email protected]> Auto-Submit: Paul Berry <[email protected]>
1 parent b4d5272 commit 5eacff3

File tree

3 files changed

+54
-91
lines changed

3 files changed

+54
-91
lines changed

pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart

Lines changed: 44 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -898,23 +898,18 @@ abstract class TypeConstraintGenerator<
898898
List<TypeStructure>? getTypeArgumentsAsInstanceOf(
899899
TypeDeclarationType type, TypeDeclaration typeDeclaration);
900900

901-
/// Matches [p] against [q] as a subtype against supertype and returns true if
902-
/// [p] and [q] are both function types, and [p] is a subtype of [q] under
903-
/// some constraints imposed on type parameters occurring in [q]; false if
904-
/// both [p] and [q] are function types, but [p] can't possibly be a subtype
905-
/// of [q] under any constraints; and null otherwise.
901+
/// Matches [p] against [q].
902+
///
903+
/// If [p] and [q] are both non-generic function types, and [p] is a subtype
904+
/// of [q] under some constraints, the constraints making the relation
905+
/// possible are recorded, and `true` is returned. Otherwise, the constraint
906+
/// state is unchanged (or rolled back using [restoreState]), and `null` is
907+
/// returned.
906908
///
907909
/// An invariant of the type inference is that only [p] or [q] may be a
908910
/// schema (in other words, may contain the unknown type `_`); the other must
909911
/// be simply a type. If [leftSchema] is `true`, [p] may contain `_`; if it is
910912
/// `false`, [q] may contain `_`.
911-
///
912-
/// As the generator computes the constraints making the relation possible, it
913-
/// changes its internal state. The current state of the generator can be
914-
/// obtained by [currentState], and the generator can be restored to a state
915-
/// via [restoreState]. All of the shared constraint generation methods are
916-
/// supposed to restore the generator to the prior state in case of a
917-
/// mismatch, taking that responsibility away from the caller.
918913
bool? performSubtypeConstraintGenerationForFunctionTypes(
919914
TypeStructure p, TypeStructure q,
920915
{required bool leftSchema, required AstNode? astNodeForTesting}) {
@@ -943,7 +938,6 @@ abstract class TypeConstraintGenerator<
943938
if (!performSubtypeConstraintGenerationInternal(
944939
p.returnType, q.returnType,
945940
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
946-
restoreState(state);
947941
return null;
948942
}
949943
for (int i = 0; i < q.positionalParameterTypes.length; ++i) {
@@ -973,7 +967,6 @@ abstract class TypeConstraintGenerator<
973967
if (!performSubtypeConstraintGenerationInternal(
974968
p.returnType, q.returnType,
975969
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
976-
restoreState(state);
977970
return null;
978971
}
979972
for (int i = 0; i < p.positionalParameterTypes.length; ++i) {
@@ -1048,23 +1041,18 @@ abstract class TypeConstraintGenerator<
10481041
return null;
10491042
}
10501043

1051-
/// Matches [p] against [q] as a subtype against supertype and returns true if
1052-
/// [p] and [q] are both FutureOr, with or without nullability suffixes as
1053-
/// defined by [enableDiscrepantObliviousnessOfNullabilitySuffixOfFutureOr],
1054-
/// and [p] is a subtype of [q] under some constraints imposed on type
1055-
/// parameters occurring in [q], and false otherwise.
1044+
/// Matches [p] against [q].
1045+
///
1046+
/// If [q] is of the form `FutureOr<q0>` for some `q0`, and [p] is a subtype
1047+
/// of [q] under some constraints, the constraints making the relation
1048+
/// possible are recorded, and `true` is returned. Otherwise, the constraint
1049+
/// state is unchanged (or rolled back using [restoreState]), and `false` is
1050+
/// returned.
10561051
///
10571052
/// An invariant of the type inference is that only [p] or [q] may be a
10581053
/// schema (in other words, may contain the unknown type `_`); the other must
10591054
/// be simply a type. If [leftSchema] is `true`, [p] may contain `_`; if it is
10601055
/// `false`, [q] may contain `_`.
1061-
///
1062-
/// As the generator computes the constraints making the relation possible,
1063-
/// it changes its internal state. The current state of the generator can be
1064-
/// obtained by [currentState], and the generator can be restored to a state
1065-
/// via [restoreState]. All of the shared constraint generation methods are
1066-
/// supposed to restore the generator to the prior state in case of a
1067-
/// mismatch, taking that responsibility away from the caller.
10681056
bool performSubtypeConstraintGenerationForFutureOr(
10691057
TypeStructure p, TypeStructure q,
10701058
{required bool leftSchema, required AstNode? astNodeForTesting}) {
@@ -1083,7 +1071,6 @@ abstract class TypeConstraintGenerator<
10831071
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
10841072
return true;
10851073
}
1086-
restoreState(state);
10871074
}
10881075

10891076
// Or if `P` is a subtype match for `Future<Q0>` under non-empty
@@ -1095,14 +1082,12 @@ abstract class TypeConstraintGenerator<
10951082
if (isMatchWithFuture && matchWithFutureAddsConstraints) {
10961083
return true;
10971084
}
1098-
restoreState(state);
10991085

11001086
// Or if `P` is a subtype match for `Q0` under constraint set `C`.
11011087
if (performSubtypeConstraintGenerationInternal(p, q0,
11021088
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
11031089
return true;
11041090
}
1105-
restoreState(state);
11061091

11071092
// Or if `P` is a subtype match for `Future<Q0>` under empty
11081093
// constraint set `C`.
@@ -1114,24 +1099,22 @@ abstract class TypeConstraintGenerator<
11141099
return false;
11151100
}
11161101

1117-
/// Matches [p] against [q] as a subtype against supertype and returns true if
1118-
/// [p] and [q] are both type declaration types as defined by the enum
1119-
/// [TypeDeclarationKind], and [p] is a subtype of [q] under some constraints
1120-
/// imposed on type parameters occurring in [q]; false if both [p] and [q] are
1121-
/// type declaration types, but [p] can't possibly be a subtype of [q] under
1122-
/// any constraints; and null otherwise.
1102+
/// Matches [p] against [q] as a subtype against supertype.
1103+
///
1104+
/// If [p] and [q] are both type declaration types, then:
1105+
///
1106+
/// - If [p] is a subtype of [q] under some constraints, the constraints
1107+
/// making the relation possible are recorded, and `true` is returned.
1108+
/// - Otherwise, the constraint state is unchanged (or rolled back using
1109+
/// [restoreState]), and `false` is returned.
1110+
///
1111+
/// Otherwise (either [p] or [q] is not a type declaration type), the
1112+
/// constraint state is unchanged, and `null` is returned.
11231113
///
11241114
/// An invariant of the type inference is that only [p] or [q] may be a
11251115
/// schema (in other words, may contain the unknown type `_`); the other must
11261116
/// be simply a type. If [leftSchema] is `true`, [p] may contain `_`; if it is
11271117
/// `false`, [q] may contain `_`.
1128-
///
1129-
/// As the generator computes the constraints making the relation possible, it
1130-
/// changes its internal state. The current state of the generator can be
1131-
/// obtained by [currentState], and the generator can be restored to a state
1132-
/// via [restoreState]. All of the shared constraint generation methods are
1133-
/// supposed to restore the generator to the prior state in case of a
1134-
/// mismatch, taking that responsibility away from the caller.
11351118
bool? performSubtypeConstraintGenerationForTypeDeclarationTypes(
11361119
TypeStructure p, TypeStructure q,
11371120
{required bool leftSchema, required AstNode? astNodeForTesting}) {
@@ -1174,6 +1157,14 @@ abstract class TypeConstraintGenerator<
11741157
}
11751158
}
11761159

1160+
/// Implementation backing [performSubtypeConstraintGenerationLeftSchema] and
1161+
/// [performSubtypeConstraintGenerationRightSchema].
1162+
///
1163+
/// If [p] is a subtype of [q] under some constraints, the constraints making
1164+
/// the relation possible are recorded, and `true` is returned. Otherwise,
1165+
/// the constraint state is unchanged (or rolled back using [restoreState]),
1166+
/// and `false` is returned.
1167+
///
11771168
/// [performSubtypeConstraintGenerationInternal] should be implemented by
11781169
/// concrete classes implementing [TypeConstraintGenerator]. The
11791170
/// implementations of [performSubtypeConstraintGenerationLeftSchema] and
@@ -1201,9 +1192,8 @@ abstract class TypeConstraintGenerator<
12011192
/// As the generator computes the constraints making the relation possible,
12021193
/// it changes its internal state. The current state of the generator can be
12031194
/// obtained by [currentState], and the generator can be restored to a state
1204-
/// via [restoreState]. All of the shared constraint generation methods are
1205-
/// supposed to restore the generator to the prior state in case of a
1206-
/// mismatch, taking that responsibility away from the caller.
1195+
/// via [restoreState]. If this method returns `false`, it restores the state,
1196+
/// so it is not necessary for the caller to do so.
12071197
///
12081198
/// The algorithm for subtype constraint generation is described in
12091199
/// https://github.com/dart-lang/language/blob/main/resources/type-system/inference.md#subtype-constraint-generation
@@ -1219,9 +1209,8 @@ abstract class TypeConstraintGenerator<
12191209
/// As the generator computes the constraints making the relation possible,
12201210
/// it changes its internal state. The current state of the generator can be
12211211
/// obtained by [currentState], and the generator can be restored to a state
1222-
/// via [restoreState]. All of the shared constraint generation methods are
1223-
/// supposed to restore the generator to the prior state in case of a
1224-
/// mismatch, taking that responsibility away from the caller.
1212+
/// via [restoreState]. If this method returns `false`, it restores the state,
1213+
/// so it is not necessary for the caller to do so.
12251214
///
12261215
/// The algorithm for subtype constraint generation is described in
12271216
/// https://github.com/dart-lang/language/blob/main/resources/type-system/inference.md#subtype-constraint-generation
@@ -1271,6 +1260,13 @@ abstract class TypeConstraintGenerator<
12711260
return true;
12721261
}
12731262

1263+
/// Matches [p] against [q], assuming both [p] and [q] are both type
1264+
/// declaration types that refer to different type declarations.
1265+
///
1266+
/// If [p] is a subtype of [q] under some constraints, the constraints making
1267+
/// the relation possible are recorded, and `true` is returned. Otherwise,
1268+
/// the constraint state is unchanged (or rolled back using [restoreState]),
1269+
/// and `false` is returned.
12741270
bool _interfaceTypes(TypeStructure p, TypeStructure q, bool leftSchema,
12751271
{required AstNode? astNodeForTesting}) {
12761272
if (p.nullabilitySuffix != NullabilitySuffix.none) {

pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart

Lines changed: 9 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
123123
/// a subtype of [type] under any set of constraints.
124124
bool tryConstrainLower(DartType type, DartType bound,
125125
{required TreeNode? treeNodeForTesting}) {
126-
return _tryNullabilityAwareSubtypeMatch(bound, type,
126+
return _isNullabilityAwareSubtypeMatch(bound, type,
127127
constrainSupertype: true, treeNodeForTesting: treeNodeForTesting);
128128
}
129129

@@ -133,7 +133,7 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
133133
/// a subtype of [bound] under any set of constraints.
134134
bool tryConstrainUpper(DartType type, DartType bound,
135135
{required TreeNode? treeNodeForTesting}) {
136-
return _tryNullabilityAwareSubtypeMatch(type, bound,
136+
return _isNullabilityAwareSubtypeMatch(type, bound,
137137
constrainSupertype: false, treeNodeForTesting: treeNodeForTesting);
138138
}
139139

@@ -156,34 +156,6 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
156156
return isMatch;
157157
}
158158

159-
/// Tries to match [subtype] against [supertype].
160-
///
161-
/// If the match succeeds, the member returns true, and the resulting type
162-
/// constraints are recorded for later use by [computeConstraints]. If the
163-
/// match fails, the member returns false, and the set of type constraints is
164-
/// unchanged.
165-
///
166-
/// In contrast with [_tryNullabilityObliviousSubtypeMatch], this method
167-
/// distinguishes between cases when the type parameters to constraint occur
168-
/// in [subtype] and in [supertype]. If [constrainSupertype] is true, the
169-
/// type parameters to constrain occur in [supertype]; otherwise, they occur
170-
/// in [subtype]. If one type contains the type parameters to constrain, the
171-
/// other one isn't allowed to contain them. The type that contains the type
172-
/// parameters isn't allowed to also contain [UnknownType], that is, to be a
173-
/// type schema.
174-
bool _tryNullabilityAwareSubtypeMatch(DartType subtype, DartType supertype,
175-
{required bool constrainSupertype,
176-
required TreeNode? treeNodeForTesting}) {
177-
int baseConstraintCount = _protoConstraints.length;
178-
bool isMatch = _isNullabilityAwareSubtypeMatch(subtype, supertype,
179-
constrainSupertype: constrainSupertype,
180-
treeNodeForTesting: treeNodeForTesting);
181-
if (!isMatch) {
182-
_protoConstraints.length = baseConstraintCount;
183-
}
184-
return isMatch;
185-
}
186-
187159
/// Add constraint: [lower] <: [parameter] <: TOP.
188160
void _constrainParameterLower(StructuralParameter parameter, DartType lower,
189161
{required TreeNode? treeNodeForTesting}) {
@@ -367,10 +339,10 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
367339

368340
/// Matches [p] against [q] as a subtype against supertype.
369341
///
370-
/// Returns true if [p] is a subtype of [q] under some constraints, and false
371-
/// otherwise. The constraints making the relation possible are recorded to
372-
/// [_protoConstraints]. It is the responsibility of the caller to cleanup
373-
/// [_protoConstraints] in case [p] can't be a subtype of [q].
342+
/// If [p] is a subtype of [q] under some constraints, the constraints making
343+
/// the relation possible are recorded to [_protoConstraints], and `true` is
344+
/// returned. Otherwise, [_protoConstraints] is left unchanged (or rolled
345+
/// back), and `false` is returned.
374346
///
375347
/// If [constrainSupertype] is true, the type parameters to constrain occur in
376348
/// [supertype]; otherwise, they occur in [subtype]. If one type contains the
@@ -505,7 +477,6 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
505477
treeNodeForTesting: treeNodeForTesting)) {
506478
return true;
507479
}
508-
_protoConstraints.length = baseConstraintCount;
509480

510481
if ((p is SharedDynamicTypeStructure || p is SharedVoidTypeStructure) &&
511482
_isNullabilityAwareSubtypeMatch(
@@ -514,7 +485,6 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
514485
treeNodeForTesting: treeNodeForTesting)) {
515486
return true;
516487
}
517-
_protoConstraints.length = baseConstraintCount;
518488

519489
bool isMatchWithRawQ = _isNullabilityAwareSubtypeMatch(p, rawQ,
520490
constrainSupertype: constrainSupertype,
@@ -524,20 +494,17 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
524494
if (isMatchWithRawQ && matchWithRawQAddsConstraints) {
525495
return true;
526496
}
527-
_protoConstraints.length = baseConstraintCount;
528497

529498
if (_isNullabilityAwareSubtypeMatch(
530499
p, typeOperations.nullType.unwrapTypeView(),
531500
constrainSupertype: constrainSupertype,
532501
treeNodeForTesting: treeNodeForTesting)) {
533502
return true;
534503
}
535-
_protoConstraints.length = baseConstraintCount;
536504

537505
if (isMatchWithRawQ && !matchWithRawQAddsConstraints) {
538506
return true;
539507
}
540-
_protoConstraints.length = baseConstraintCount;
541508
}
542509

543510
// If P is FutureOr<P0> the match holds under constraint set C1 + C2:
@@ -616,22 +583,18 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
616583
// already eliminated the case that X is a variable in L.
617584
if (p is TypeParameterType) {
618585
// Coverage-ignore-block(suite): Not run.
619-
final int baseConstraintCount = _protoConstraints.length;
620586
if (_isNullabilityAwareSubtypeMatch(p.bound, q,
621587
constrainSupertype: constrainSupertype,
622588
treeNodeForTesting: treeNodeForTesting)) {
623589
return true;
624590
}
625-
_protoConstraints.length = baseConstraintCount;
626591
} else if (p is StructuralParameterType) {
627592
// Coverage-ignore-block(suite): Not run.
628-
final int baseConstraintCount = _protoConstraints.length;
629593
if (_isNullabilityAwareSubtypeMatch(p.bound, q,
630594
constrainSupertype: constrainSupertype,
631595
treeNodeForTesting: treeNodeForTesting)) {
632596
return true;
633597
}
634-
_protoConstraints.length = baseConstraintCount;
635598
}
636599

637600
bool? constraintGenerationResult =
@@ -774,6 +737,7 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
774737
}
775738
}
776739
if (sameNames) {
740+
final int baseConstraintCount = _protoConstraints.length;
777741
bool isMatch = true;
778742
for (int i = 0; isMatch && i < p.positional.length; i++) {
779743
isMatch = isMatch &&
@@ -788,6 +752,8 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
788752
treeNodeForTesting: treeNodeForTesting);
789753
}
790754
if (isMatch) return true;
755+
// Coverage-ignore(suite): Not run.
756+
_protoConstraints.length = baseConstraintCount;
791757
}
792758
}
793759

pkg/front_end/test/spell_checking_list_code.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ awaited
121121
awaiting
122122
awaits
123123
b
124+
backing
124125
backlog
125126
backping
126127
backstop

0 commit comments

Comments
 (0)