[dart2js] Use exactOrEmpty in RecordTypeMask.toFlatTypeMask When a record shape is uninstantiated in the closed world, RecordTypeMask.toFlatTypeMask falls back to the record arity class (e.g. _Record3). If no record literals are instantiated anywhere in the program, the arity class itself is also uninstantiated and has no strict subclasses. Using TypeMask.exactOrEmpty / TypeMask.nonNullExactOrEmpty ensures that an uninstantiated record arity class normalizes to an empty mask instead of triggering an assertion failure in TypeMask.assertIsNormalized. Also preserve special values (null, lateSentinel) in RecordTypeMask.createRecordWithPowerset when any field type is empty. Fixes https://github.com/dart-lang/sdk/issues/64100 TAG=agy CONV=bca542cb-694d-48a1-baac-322aa1508f0c Change-Id: Icd22328a35481d2af7e179a5e98c59d5163d05a6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/549040 Reviewed-by: Nate Biggs <natebiggs@google.com> Commit-Queue: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/record_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/record_type_mask.dart index a20eaf7..6c115e4 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/record_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/record_type_mask.dart
@@ -58,7 +58,10 @@ // If any field is empty then this record is not instantiable and we // simplify to an empty mask. if (types.any((e) => e.isEmpty)) { - return domain.emptyType; + return FlatTypeMask._emptyOrSpecial( + domain, + _specialValueDomain.restrict(powerset), + ); } return RecordTypeMask._(types, shape, powerset); } @@ -383,17 +386,18 @@ hasLateSentinel: hasLateSentinel, ); } else { - return isNullable - ? FlatTypeMask.exact( - recordClass, - domain, - hasLateSentinel: hasLateSentinel, - ) - : FlatTypeMask.nonNullExact( - recordClass, - domain, - hasLateSentinel: hasLateSentinel, - ); + return (isNullable + ? TypeMask.exactOrEmpty( + recordClass, + domain, + hasLateSentinel: hasLateSentinel, + ) + : TypeMask.nonNullExactOrEmpty( + recordClass, + domain, + hasLateSentinel: hasLateSentinel, + )) + as FlatTypeMask; } }
diff --git a/pkg/compiler/test/inference/record_type_test.dart b/pkg/compiler/test/inference/record_type_test.dart index c587164..e259ac1 100644 --- a/pkg/compiler/test/inference/record_type_test.dart +++ b/pkg/compiler/test/inference/record_type_test.dart
@@ -1034,8 +1034,37 @@ Expect.isFalse(recordAMask.nullable(domain).isNull); } + runNoRecordsInstantiatedTest() async { + TypeEnvironment env = await TypeEnvironment.create(r""" + class A {} + main() { + print(A()); + } + """, testBackendWorld: true); + JClosedWorld world = env.jClosedWorld; + final domain = world.abstractValueDomain as CommonMasks; + final aMask = FlatTypeMask.nonNullExact(env.getClass('A'), domain); + final shape3 = RecordShape(2, ["bar"]); + final uninstantiatedRecordMask = RecordTypeMask.createRecord(domain, [ + aMask, + aMask, + aMask, + ], shape3) as RecordTypeMask; + Expect.equals( + domain.emptyType, + uninstantiatedRecordMask.toFlatTypeMask(domain), + ); + Expect.equals( + domain.nullType, + (uninstantiatedRecordMask.nullable(domain) as RecordTypeMask) + .toFlatTypeMask(domain), + ); + Expect.equals(aMask, uninstantiatedRecordMask.union(aMask, domain)); + } + asyncTest(() async { print('--test from kernel------------------------------------------------'); await runTest(); + await runNoRecordsInstantiatedTest(); }); }