@@ -411,6 +411,11 @@ mixin StandardBounds {
411
411
type1, type2, clientLibrary);
412
412
}
413
413
414
+ if (type1 is RecordType && type2 is RecordType ) {
415
+ return _getNullabilityAwareRecordStandardLowerBound (
416
+ type1, type2, clientLibrary);
417
+ }
418
+
414
419
// DOWN(T1, T2) = T1 if T1 <: T2.
415
420
// DOWN(T1, T2) = T2 if T2 <: T1.
416
421
@@ -780,6 +785,33 @@ mixin StandardBounds {
780
785
type1, coreTypes.objectNonNullableRawType, clientLibrary);
781
786
}
782
787
788
+ if (type1 is RecordType ) {
789
+ if (type2 is RecordType ) {
790
+ return _getNullabilityAwareRecordStandardUpperBound (
791
+ type1, type2, clientLibrary);
792
+ }
793
+
794
+ if (type2 is InterfaceType && type2.classNode == coreTypes.recordClass) {
795
+ // UP(Record(...), Record) = Record
796
+ return coreTypes.recordRawType (uniteNullabilities (
797
+ type1.declaredNullability, type2.declaredNullability));
798
+ }
799
+
800
+ // UP(Record(...), T2) = UP(Object, T2)
801
+ return _getNullabilityAwareStandardUpperBound (
802
+ coreTypes.objectNonNullableRawType, type2, clientLibrary);
803
+ } else if (type2 is RecordType ) {
804
+ if (type1 is InterfaceType && type1.classNode == coreTypes.recordClass) {
805
+ // UP(Record, Record(...)) = Record
806
+ return coreTypes.recordRawType (uniteNullabilities (
807
+ type1.declaredNullability, type2.declaredNullability));
808
+ }
809
+
810
+ // UP(T1, Record(...)) = UP(T1, Object)
811
+ return _getNullabilityAwareStandardUpperBound (
812
+ type1, coreTypes.objectNonNullableRawType, clientLibrary);
813
+ }
814
+
783
815
// UP(FutureOr<T1>, FutureOr<T2>) = FutureOr<T3> where T3 = UP(T1, T2)
784
816
// UP(Future<T1>, FutureOr<T2>) = FutureOr<T3> where T3 = UP(T1, T2)
785
817
// UP(FutureOr<T1>, Future<T2>) = FutureOr<T3> where T3 = UP(T1, T2)
@@ -1051,6 +1083,60 @@ mixin StandardBounds {
1051
1083
math.min (f.requiredParameterCount, g.requiredParameterCount));
1052
1084
}
1053
1085
1086
+ /// Computes the nullability-aware lower bound of two record types.
1087
+ ///
1088
+ /// The algorithm is defined as follows:
1089
+ /// DOWN((P00, ..., P0k, Named0), (P10, ..., P1k, Named1)) =
1090
+ /// (P20, ..., P2k, Named2)
1091
+ /// if:
1092
+ /// P2i is DOWN(P0i, P1i),
1093
+ /// Named0 contains R0i xi
1094
+ /// if Named1 contains R1i xi
1095
+ /// Named1 contains R1i xi
1096
+ /// if Named0 contains R0i xi
1097
+ /// Named2 contains exactly R2i xi
1098
+ /// for each xi in both Named0 and Named1
1099
+ /// where R0i xi is in Named0
1100
+ /// where R1i xi is in Named1
1101
+ /// and R2i is UP(R0i, R1i)
1102
+ /// DOWN(Record(...), Record(...)) = Never otherwise.
1103
+ DartType _getNullabilityAwareRecordStandardLowerBound (
1104
+ RecordType r1, RecordType r2, Library clientLibrary) {
1105
+ // The fallback result for whenever the following rule applies:
1106
+ // DOWN(Record(...), Record(...)) = Never otherwise.
1107
+ late final DartType fallbackResult = NeverType .fromNullability (
1108
+ intersectNullabilities (r1.declaredNullability, r2.declaredNullability));
1109
+
1110
+ if (r1.positional.length != r2.positional.length ||
1111
+ r1.named.length != r2.named.length) {
1112
+ return fallbackResult;
1113
+ }
1114
+
1115
+ int positionalLength = r1.positional.length;
1116
+ int namedLength = r1.named.length;
1117
+
1118
+ for (int i = 0 ; i < namedLength; i++ ) {
1119
+ if (r1.named[i].name != r2.named[i].name) {
1120
+ return fallbackResult;
1121
+ }
1122
+ }
1123
+
1124
+ List <DartType > positional = new List <DartType >.generate (
1125
+ positionalLength,
1126
+ (i) => _getNullabilityAwareStandardLowerBound (
1127
+ r1.positional[i], r2.positional[i], clientLibrary));
1128
+
1129
+ List <NamedType > named = new List <NamedType >.generate (
1130
+ namedLength,
1131
+ (i) => new NamedType (
1132
+ r1.named[i].name,
1133
+ _getNullabilityAwareStandardLowerBound (
1134
+ r1.named[i].type, r2.named[i].type, clientLibrary)));
1135
+
1136
+ return new RecordType (positional, named,
1137
+ intersectNullabilities (r1.declaredNullability, r2.declaredNullability));
1138
+ }
1139
+
1054
1140
/// Computes the nullability-aware lower bound of two function types.
1055
1141
///
1056
1142
/// UP(
@@ -1219,6 +1305,63 @@ mixin StandardBounds {
1219
1305
requiredParameterCount: f.requiredParameterCount);
1220
1306
}
1221
1307
1308
+ /// Computes the nullability-aware lower bound of two record types.
1309
+ ///
1310
+ /// UP((P00, ... P0k, Named0), (P10, ... P1k, Named1)) =
1311
+ /// (P20, ..., P2k, Named2)
1312
+ /// if:
1313
+ /// P2i is UP(P0i, P1i)
1314
+ /// Named0 contains R0i xi
1315
+ /// if Named1 contains R1i xi
1316
+ /// Named1 contains R1i xi
1317
+ /// if Named0 contains R0i xi
1318
+ /// Named2 contains exactly R2i xi
1319
+ /// for each xi in both Named0 and Named1
1320
+ /// where R0i xi is in Named0
1321
+ /// where R1i xi is in Named1
1322
+ /// and R2i is UP(R0i, R1i)
1323
+ /// UP(Record(...), Record(...)) = Record otherwise
1324
+ DartType _getNullabilityAwareRecordStandardUpperBound (
1325
+ RecordType r1, RecordType r2, Library clientLibrary) {
1326
+ // The return value for whenever the following applies:
1327
+ // UP(Record(...), Record(...)) = Record otherwise
1328
+ late final DartType fallbackResult = coreTypes.recordRawType (
1329
+ uniteNullabilities (r1.declaredNullability, r2.declaredNullability));
1330
+
1331
+ // Here we perform a quick check on the function types to figure out if we
1332
+ // can compute a non-trivial upper bound for them.
1333
+ if (r1.positional.length != r2.positional.length ||
1334
+ r1.named.length != r2.named.length) {
1335
+ return fallbackResult;
1336
+ }
1337
+
1338
+ int positionalLength = r1.positional.length;
1339
+ int namedLength = r1.named.length;
1340
+
1341
+ for (int i = 0 ; i < namedLength; i++ ) {
1342
+ // The named parameters of record types are assumed to be sorted
1343
+ // lexicographically.
1344
+ if (r1.named[i].name != r2.named[i].name) {
1345
+ return fallbackResult;
1346
+ }
1347
+ }
1348
+
1349
+ List <DartType > positional = new List <DartType >.generate (
1350
+ positionalLength,
1351
+ (i) => _getNullabilityAwareStandardUpperBound (
1352
+ r1.positional[i], r2.positional[i], clientLibrary));
1353
+
1354
+ List <NamedType > named = new List <NamedType >.generate (
1355
+ namedLength,
1356
+ (i) => new NamedType (
1357
+ r1.named[i].name,
1358
+ _getNullabilityAwareStandardUpperBound (
1359
+ r1.named[i].type, r2.named[i].type, clientLibrary)));
1360
+
1361
+ return new RecordType (positional, named,
1362
+ uniteNullabilities (r1.declaredNullability, r2.declaredNullability));
1363
+ }
1364
+
1222
1365
DartType _getNullabilityAwareTypeParameterStandardUpperBound (
1223
1366
TypeParameterType type1, DartType type2, Library clientLibrary) {
1224
1367
// UP(X1 extends B1, T2) =
0 commit comments