Skip to content

Commit 3bc27ff

Browse files
chloestefantsovaCommit Queue
authored andcommitted
[analyzer][cfe] Share constraint generation from bounded parameters
Part of #54902 Change-Id: I545ce1faa08448bf6f573550ca1349c124e55e9c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/394780 Reviewed-by: Paul Berry <[email protected]> Commit-Queue: Chloe Stefantsova <[email protected]>
1 parent 4b7c6b1 commit 3bc27ff

File tree

8 files changed

+107
-17
lines changed

8 files changed

+107
-17
lines changed

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,27 @@ abstract interface class TypeAnalyzerOperations<
434434
/// step too.
435435
TypeStructure? matchFutureOrInternal(TypeStructure type);
436436

437+
/// If [type] is a parameter type with empty nullability suffix, returns its
438+
/// bound, whether it is its type parameter bound or its promoted bound.
439+
/// Otherwise, returns null.
440+
///
441+
/// In the example below matching the appearance of `X` in the parameters of
442+
/// `foo` returns `num`, matching the appearance of `Y` in the parameters of
443+
/// the function type `int Function<Y extends Object>(Y)` returns `Object`,
444+
/// and matching the inferred type of the variable `z`, which is `X & int`,
445+
/// `X` promoted to `int`, returns `int`. Matching a non-type parameter type,
446+
/// such as `int`, would return null.
447+
///
448+
/// int foo<X extends num>(int Function<Y extends Object>(Y) f, X x) {
449+
/// if (x is int) {
450+
/// var z = x;
451+
/// return z;
452+
/// } else {
453+
/// return f(x);
454+
/// }
455+
/// }
456+
TypeStructure? matchTypeParameterBoundInternal(TypeStructure type);
457+
437458
/// If [type] is a parameter type that is of a kind used in type inference,
438459
/// returns the corresponding parameter.
439460
///

pkg/_fe_analyzer_shared/test/mini_ast.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3297,6 +3297,20 @@ class MiniAstOperations
32973297
// TODO(paulberry): Implement greatest closure of types in mini ast.
32983298
throw UnimplementedError();
32993299
}
3300+
3301+
@override
3302+
Type? matchTypeParameterBoundInternal(Type type) {
3303+
if (type
3304+
case TypeParameterType(
3305+
:var promotion,
3306+
:var typeParameter,
3307+
nullabilitySuffix: NullabilitySuffix.none
3308+
)) {
3309+
return promotion ?? typeParameter.bound;
3310+
} else {
3311+
return null;
3312+
}
3313+
}
33003314
}
33013315

33023316
/// Representation of an expression or statement in the pseudo-Dart language

pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,33 @@ main() {
828828
check(tcg._constraints).isEmpty();
829829
});
830830
});
831+
832+
group('matchTypeParameterBoundInternal', () {
833+
test('Non-promoted parameter on LHS', () {
834+
var tcg = _TypeConstraintGatherer({'T'});
835+
check(tcg.performSubtypeConstraintGenerationInternal(
836+
TypeParameterType(TypeRegistry.addTypeParameter('X')
837+
..bound = Type('Future<String>')),
838+
Type('Future<T>'),
839+
leftSchema: false,
840+
astNodeForTesting: Node.placeholder()))
841+
.isTrue();
842+
check(tcg._constraints).unorderedEquals(['String <: T']);
843+
});
844+
845+
test('Promoted parameter on LHS', () {
846+
var tcg = _TypeConstraintGatherer({'T'});
847+
check(tcg.performSubtypeConstraintGenerationInternal(
848+
TypeParameterType(
849+
TypeRegistry.addTypeParameter('X')..bound = Type('Object'),
850+
promotion: Type('Future<num>')),
851+
Type('Future<T>'),
852+
leftSchema: false,
853+
astNodeForTesting: Node.placeholder()))
854+
.isTrue();
855+
check(tcg._constraints).unorderedEquals(['num <: T']);
856+
});
857+
});
831858
}
832859

833860
class _TypeConstraintGatherer extends TypeConstraintGenerator<Type,
@@ -948,6 +975,14 @@ class _TypeConstraintGatherer extends TypeConstraintGenerator<Type,
948975
return true;
949976
}
950977

978+
if (typeAnalyzerOperations.matchTypeParameterBoundInternal(p)
979+
case var bound?) {
980+
if (performSubtypeConstraintGenerationInternal(bound, q,
981+
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
982+
return true;
983+
}
984+
}
985+
951986
bool? result = performSubtypeConstraintGenerationForTypeDeclarationTypes(
952987
p, q,
953988
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting);

pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import 'package:analyzer/dart/element/element.dart';
1414
import 'package:analyzer/dart/element/type.dart';
1515
import 'package:analyzer/src/dart/ast/ast.dart';
1616
import 'package:analyzer/src/dart/element/element.dart';
17-
import 'package:analyzer/src/dart/element/type.dart';
1817
import 'package:analyzer/src/dart/element/type_algebra.dart';
1918
import 'package:analyzer/src/dart/element/type_schema.dart';
2019
import 'package:analyzer/src/dart/element/type_system.dart';
@@ -326,10 +325,10 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
326325
// variable `X & B`), the match holds with constraint set `C`:
327326
// If `B` is a subtype match for `Q` with constraint set `C`.
328327
// Note: we have already eliminated the case that `X` is a variable in `L`.
329-
if (P_nullability == NullabilitySuffix.none && P is TypeParameterTypeImpl) {
330-
var B = P.promotedBound ?? P.element.bound;
331-
if (B != null &&
332-
trySubtypeMatch(B, Q, leftSchema, nodeForTesting: nodeForTesting)) {
328+
if (typeAnalyzerOperations.matchTypeParameterBoundInternal(P)
329+
case var bound?) {
330+
if (performSubtypeConstraintGenerationInternal(bound, Q,
331+
leftSchema: leftSchema, astNodeForTesting: nodeForTesting)) {
333332
return true;
334333
}
335334
}

pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,16 @@ class TypeSystemOperations
736736
}
737737
}
738738

739+
@override
740+
DartType? matchTypeParameterBoundInternal(DartType type) {
741+
if (type is TypeParameterTypeImpl &&
742+
type.nullabilitySuffix == NullabilitySuffix.none) {
743+
return type.promotedBound ?? type.element.bound;
744+
} else {
745+
return null;
746+
}
747+
}
748+
739749
@override
740750
SharedTypeView<DartType> normalize(SharedTypeView<DartType> type) {
741751
return SharedTypeView(typeSystem.normalize(type.unwrapTypeView()));

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

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -428,17 +428,11 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
428428
//
429429
// If B is a subtype match for Q with constraint set C. Note that we have
430430
// already eliminated the case that X is a variable in L.
431-
if (p is TypeParameterType) {
432-
if (_isNullabilityAwareSubtypeMatch(p.bound, q,
433-
constrainSupertype: constrainSupertype,
434-
treeNodeForTesting: treeNodeForTesting)) {
435-
return true;
436-
}
437-
} else if (p is StructuralParameterType) {
438-
// Coverage-ignore-block(suite): Not run.
439-
if (_isNullabilityAwareSubtypeMatch(p.bound, q,
440-
constrainSupertype: constrainSupertype,
441-
treeNodeForTesting: treeNodeForTesting)) {
431+
if (typeAnalyzerOperations.matchTypeParameterBoundInternal(p)
432+
case DartType bound?) {
433+
if (performSubtypeConstraintGenerationInternal(bound, q,
434+
leftSchema: constrainSupertype,
435+
astNodeForTesting: treeNodeForTesting)) {
442436
return true;
443437
}
444438
}

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,23 @@ class OperationsCfe
10131013
.functionRawType(Nullability.nonNullable))
10141014
.eliminateToLeast(type);
10151015
}
1016+
1017+
@override
1018+
DartType? matchTypeParameterBoundInternal(DartType type) {
1019+
if (type.nullabilitySuffix != NullabilitySuffix.none) {
1020+
return null;
1021+
}
1022+
if (type is TypeParameterType) {
1023+
return type.parameter.bound;
1024+
} else if (type is StructuralParameterType) {
1025+
// Coverage-ignore-block(suite): Not run.
1026+
return type.parameter.bound;
1027+
} else if (type is IntersectionType) {
1028+
return type.right;
1029+
} else {
1030+
return null;
1031+
}
1032+
}
10161033
}
10171034

10181035
/// Type inference results used for testing.

pkg/front_end/test/coverage_suite_expected.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1046,7 +1046,7 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
10461046
),
10471047
// 100.0%.
10481048
"package:front_end/src/type_inference/type_inference_engine.dart": (
1049-
hitCount: 536,
1049+
hitCount: 545,
10501050
missCount: 0,
10511051
),
10521052
// 100.0%.

0 commit comments

Comments
 (0)