Skip to content

Commit be1b2a9

Browse files
chloestefantsovaCommit Queue
authored andcommitted
[analyzer][cfe] Share type constraint generation
Part of #54902 Change-Id: I15f7a78c9390466f5a48400f81def175faeb9fd2 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/395020 Reviewed-by: Erik Ernst <[email protected]> Commit-Queue: Chloe Stefantsova <[email protected]>
1 parent a00d45f commit be1b2a9

File tree

13 files changed

+292
-548
lines changed

13 files changed

+292
-548
lines changed

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

Lines changed: 163 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -196,12 +196,17 @@ abstract interface class TypeAnalyzerOperations<
196196
@override
197197
bool isNever(SharedTypeView<TypeStructure> type);
198198

199-
/// Returns `true` if `Null` is not a subtype of all types matching
200-
/// [typeSchema].
199+
/// Returns `true` if `Null` is a subtype of all types matching [type].
201200
///
202-
/// The predicate of [isNonNullable] could be computed directly with a subtype
203-
/// query, but the implementations can do that more efficiently.
204-
bool isNonNullable(SharedTypeSchemaView<TypeStructure> typeSchema);
201+
/// The predicate of [isNullableInternal] could be computed directly with a
202+
/// subtype query, but the implementations can do that more efficiently.
203+
bool isNullableInternal(TypeStructure type);
204+
205+
/// Returns `true` if `Null` is not a subtype of all types matching [type].
206+
///
207+
/// The predicate of [isNonNullableInternal] could be computed directly with
208+
/// a subtype query, but the implementations can do that more efficiently.
209+
bool isNonNullableInternal(TypeStructure type);
205210

206211
/// Returns `true` if [type] is `Null`.
207212
bool isNull(SharedTypeView<TypeStructure> type);
@@ -943,12 +948,12 @@ abstract class TypeConstraintGenerator<
943948
/// Add constraint: [lower] <: [typeParameter] <: TOP.
944949
void addLowerConstraintForParameter(
945950
TypeParameterStructure typeParameter, TypeStructure lower,
946-
{required AstNode? nodeForTesting});
951+
{required AstNode? astNodeForTesting});
947952

948953
/// Add constraint: BOTTOM <: [typeParameter] <: [upper].
949954
void addUpperConstraintForParameter(
950955
TypeParameterStructure typeParameter, TypeStructure upper,
951-
{required AstNode? nodeForTesting});
956+
{required AstNode? astNodeForTesting});
952957

953958
/// Returns the type arguments of the supertype of [type] that is an
954959
/// instantiation of [typeDeclaration]. If none of the supertypes of [type]
@@ -1543,6 +1548,9 @@ abstract class TypeConstraintGenerator<
15431548
return false;
15441549
}
15451550

1551+
/// Type parameters being constrained by [TypeConstraintGenerator].
1552+
Iterable<TypeParameterStructure> get typeParametersToConstrain;
1553+
15461554
/// Implementation backing [performSubtypeConstraintGenerationLeftSchema] and
15471555
/// [performSubtypeConstraintGenerationRightSchema].
15481556
///
@@ -1568,7 +1576,154 @@ abstract class TypeConstraintGenerator<
15681576
/// [performSubtypeConstraintGenerationRightSchema] from the mixin.
15691577
bool performSubtypeConstraintGenerationInternal(
15701578
TypeStructure p, TypeStructure q,
1571-
{required bool leftSchema, required AstNode? astNodeForTesting});
1579+
{required bool leftSchema, required AstNode? astNodeForTesting}) {
1580+
// If `P` is `_` then the match holds with no constraints.
1581+
if (p is SharedUnknownTypeStructure) {
1582+
return true;
1583+
}
1584+
1585+
// If `Q` is `_` then the match holds with no constraints.
1586+
if (q is SharedUnknownTypeStructure) {
1587+
return true;
1588+
}
1589+
1590+
// If `P` is a type variable `X` in `L`, then the match holds:
1591+
// Under constraint `_ <: X <: Q`.
1592+
NullabilitySuffix pNullability = p.nullabilitySuffix;
1593+
if (typeAnalyzerOperations.matchInferableParameter(new SharedTypeView(p))
1594+
case var pParameter?
1595+
when pNullability == NullabilitySuffix.none &&
1596+
typeParametersToConstrain.contains(pParameter)) {
1597+
addUpperConstraintForParameter(pParameter, q,
1598+
astNodeForTesting: astNodeForTesting);
1599+
return true;
1600+
}
1601+
1602+
// If `Q` is a type variable `X` in `L`, then the match holds:
1603+
// Under constraint `P <: X <: _`.
1604+
NullabilitySuffix qNullability = q.nullabilitySuffix;
1605+
if (typeAnalyzerOperations.matchInferableParameter(new SharedTypeView(q))
1606+
case var qParameter?
1607+
when qNullability == NullabilitySuffix.none &&
1608+
typeParametersToConstrain.contains(qParameter) &&
1609+
(!inferenceUsingBoundsIsEnabled ||
1610+
(qParameter.bound == null ||
1611+
typeAnalyzerOperations.isSubtypeOfInternal(
1612+
p,
1613+
typeAnalyzerOperations.greatestClosureOfTypeInternal(
1614+
qParameter.bound!,
1615+
[...typeParametersToConstrain]))))) {
1616+
addLowerConstraintForParameter(qParameter, p,
1617+
astNodeForTesting: astNodeForTesting);
1618+
return true;
1619+
}
1620+
1621+
// If `P` and `Q` are identical types, then the subtype match holds
1622+
// under no constraints.
1623+
if (p == q) {
1624+
return true;
1625+
}
1626+
1627+
// Note that it's not necessary to rewind [_constraints] to its prior state
1628+
// in case [performSubtypeConstraintGenerationForFutureOr] returns false, as
1629+
// [performSubtypeConstraintGenerationForFutureOr] handles the rewinding of
1630+
// the state itself.
1631+
if (performSubtypeConstraintGenerationForRightFutureOr(p, q,
1632+
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
1633+
return true;
1634+
}
1635+
1636+
if (performSubtypeConstraintGenerationForRightNullableType(p, q,
1637+
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
1638+
return true;
1639+
}
1640+
1641+
// If `P` is `FutureOr<P0>` the match holds under constraint set `C1 + C2`:
1642+
if (performSubtypeConstraintGenerationForLeftFutureOr(p, q,
1643+
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
1644+
return true;
1645+
}
1646+
1647+
// If `P` is `P0?` the match holds under constraint set `C1 + C2`:
1648+
if (performSubtypeConstraintGenerationForLeftNullableType(p, q,
1649+
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
1650+
return true;
1651+
}
1652+
1653+
// If `Q` is `dynamic`, `Object?`, or `void` then the match holds under
1654+
// no constraints.
1655+
if (q is SharedDynamicTypeStructure ||
1656+
q is SharedVoidTypeStructure ||
1657+
q == typeAnalyzerOperations.objectQuestionType.unwrapTypeView()) {
1658+
return true;
1659+
}
1660+
1661+
// If `P` is `Never` then the match holds under no constraints.
1662+
if (typeAnalyzerOperations.isNever(new SharedTypeView(p))) {
1663+
return true;
1664+
}
1665+
1666+
// If `Q` is `Object`, then the match holds under no constraints:
1667+
// Only if `P` is non-nullable.
1668+
if (q == typeAnalyzerOperations.objectType.unwrapTypeView()) {
1669+
return typeAnalyzerOperations.isNonNullableInternal(p);
1670+
}
1671+
1672+
// If `P` is `Null`, then the match holds under no constraints:
1673+
// Only if `Q` is nullable.
1674+
if (pNullability == NullabilitySuffix.none &&
1675+
typeAnalyzerOperations.isNull(new SharedTypeView(p))) {
1676+
return typeAnalyzerOperations.isNullableInternal(q);
1677+
}
1678+
1679+
// If `P` is a type variable `X` with bound `B` (or a promoted type
1680+
// variable `X & B`), the match holds with constraint set `C`:
1681+
// If `B` is a subtype match for `Q` with constraint set `C`.
1682+
// Note: we have already eliminated the case that `X` is a variable in `L`.
1683+
if (typeAnalyzerOperations.matchTypeParameterBoundInternal(p)
1684+
case var bound?) {
1685+
if (performSubtypeConstraintGenerationInternal(bound, q,
1686+
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
1687+
return true;
1688+
}
1689+
}
1690+
1691+
bool? result = performSubtypeConstraintGenerationForTypeDeclarationTypes(
1692+
p, q,
1693+
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting);
1694+
if (result != null) {
1695+
return result;
1696+
}
1697+
1698+
// If `Q` is `Function` then the match holds under no constraints:
1699+
// If `P` is a function type.
1700+
if (typeAnalyzerOperations.isDartCoreFunction(new SharedTypeView(q))) {
1701+
if (p is SharedFunctionTypeStructure) {
1702+
return true;
1703+
}
1704+
}
1705+
1706+
if (performSubtypeConstraintGenerationForFunctionTypes(p, q,
1707+
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
1708+
return true;
1709+
}
1710+
1711+
// A type `P` is a subtype match for `Record` with respect to `L` under no
1712+
// constraints:
1713+
// If `P` is a record type or `Record`.
1714+
if (typeAnalyzerOperations.isDartCoreRecord(new SharedTypeView(q))) {
1715+
if (p is SharedRecordTypeStructure<TypeStructure>) {
1716+
return true;
1717+
}
1718+
}
1719+
1720+
if (performSubtypeConstraintGenerationForRecordTypes(p, q,
1721+
leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
1722+
return true;
1723+
}
1724+
1725+
return false;
1726+
}
15721727

15731728
/// Matches type schema [p] against type [q] as a subtype against supertype,
15741729
/// assuming [p] is the constraining type schema, and [q] contains the type

pkg/_fe_analyzer_shared/test/mini_ast.dart

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2993,32 +2993,6 @@ class MiniAstOperations
29932993
unwrappedType.nullabilitySuffix == NullabilitySuffix.none;
29942994
}
29952995

2996-
@override
2997-
bool isNonNullable(SharedTypeSchemaView<Type> type) {
2998-
Type unwrappedType = type.unwrapTypeSchemaView();
2999-
if (unwrappedType is DynamicType ||
3000-
unwrappedType is SharedUnknownTypeStructure ||
3001-
unwrappedType is VoidType ||
3002-
unwrappedType is NullType) {
3003-
return false;
3004-
} else if (unwrappedType
3005-
case TypeParameterType(
3006-
:var promotion,
3007-
nullabilitySuffix: NullabilitySuffix.none
3008-
)) {
3009-
return promotion != null &&
3010-
isNonNullable(SharedTypeSchemaView(promotion));
3011-
} else if (type.nullabilitySuffix == NullabilitySuffix.question) {
3012-
return false;
3013-
} else if (matchFutureOrInternal(unwrappedType) case Type typeArgument?) {
3014-
return isNonNullable(SharedTypeSchemaView(typeArgument));
3015-
}
3016-
// TODO(cstefantsova): Update to a fast-pass implementation when the
3017-
// mini-ast testing framework supports looking up superinterfaces of
3018-
// extension types or looking up bounds of type parameters.
3019-
return _typeSystem.isSubtype(NullType.instance, unwrappedType);
3020-
}
3021-
30222996
@override
30232997
bool isNull(SharedTypeView<Type> type) {
30242998
return type.unwrapTypeView() is NullType;
@@ -3311,6 +3285,53 @@ class MiniAstOperations
33113285
return null;
33123286
}
33133287
}
3288+
3289+
@override
3290+
bool isNonNullableInternal(Type type) {
3291+
Type unwrappedType = type;
3292+
if (unwrappedType is DynamicType ||
3293+
unwrappedType is SharedUnknownTypeStructure ||
3294+
unwrappedType is VoidType ||
3295+
unwrappedType is NullType ||
3296+
unwrappedType is InvalidType) {
3297+
return false;
3298+
} else if (unwrappedType
3299+
case TypeParameterType(
3300+
:var promotion,
3301+
:var typeParameter,
3302+
nullabilitySuffix: NullabilitySuffix.none
3303+
)) {
3304+
if (promotion != null) {
3305+
return isNonNullableInternal(promotion);
3306+
} else {
3307+
return isNonNullableInternal(typeParameter.bound);
3308+
}
3309+
} else if (type.nullabilitySuffix == NullabilitySuffix.question) {
3310+
return false;
3311+
} else if (matchFutureOrInternal(unwrappedType) case Type typeArgument?) {
3312+
return isNonNullableInternal(typeArgument);
3313+
}
3314+
return true;
3315+
}
3316+
3317+
@override
3318+
bool isNullableInternal(Type type) {
3319+
Type unwrappedType = type;
3320+
if (unwrappedType is DynamicType ||
3321+
unwrappedType is SharedUnknownTypeStructure ||
3322+
unwrappedType is VoidType ||
3323+
unwrappedType is NullType) {
3324+
return true;
3325+
} else if (type.nullabilitySuffix == NullabilitySuffix.question) {
3326+
return false;
3327+
} else if (matchFutureOrInternal(unwrappedType) case Type typeArgument?) {
3328+
return isNullableInternal(typeArgument);
3329+
}
3330+
// TODO(cstefantsova): Update to a fast-pass implementation when the
3331+
// mini-ast testing framework supports looking up superinterfaces of
3332+
// extension types or looking up bounds of type parameters.
3333+
return _typeSystem.isSubtype(NullType.instance, unwrappedType);
3334+
}
33143335
}
33153336

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

0 commit comments

Comments
 (0)