Skip to content

Commit 66666c3

Browse files
fishythefishCommit Queue
authored andcommitted
[dart2js] Add extension type to distinguish flags and powersets
Change-Id: I516cd7f0e6a03b680a399c483ef93d1e1d6cbeee Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/417911 Reviewed-by: Nate Biggs <[email protected]> Commit-Queue: Mayank Patke <[email protected]>
1 parent ee48eb7 commit 66666c3

File tree

2 files changed

+79
-58
lines changed

2 files changed

+79
-58
lines changed

pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart

Lines changed: 69 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ part of 'masks.dart';
66

77
enum FlatTypeMaskKind { empty, exact, subclass, subtype }
88

9+
// The flags encode not only the powerset, but also the [FlatTypeMaskKind]. If
10+
// we type both flags and powersets as [Bitset]s, we risk accidentally passing
11+
// the wrong one when the other is expected. We can get type safety against this
12+
// by introducing this extension type.
13+
extension type _Flags(Bitset bitset) {}
14+
915
/// A flat type mask is a type mask that has been flattened to contain a
1016
/// base type.
1117
class FlatTypeMask extends TypeMask {
@@ -22,15 +28,22 @@ class FlatTypeMask extends TypeMask {
2228

2329
final ClassEntity? base;
2430

25-
final Bitset flags;
31+
final _Flags _flags;
32+
33+
static _Flags _computeFlags(FlatTypeMaskKind kind, Bitset powerset) =>
34+
_Flags(Bitset(kind.index << _powersetDomains.bitWidth | powerset.bits));
35+
36+
static Bitset _powersetOfFlagsUnion(_Flags a, _Flags b) =>
37+
_getPowerset(_Flags(a.bitset.union(b.bitset)));
2638

27-
static Bitset _computeFlags(FlatTypeMaskKind kind, Bitset powerset) =>
28-
Bitset(kind.index << _powersetDomains.bitWidth | powerset.bits);
39+
static Bitset _powersetOfFlagsIntersection(_Flags a, _Flags b) =>
40+
_getPowerset(_Flags(a.bitset.intersection(b.bitset)));
2941

30-
static FlatTypeMaskKind _getKind(Bitset flags) =>
31-
FlatTypeMaskKind.values[flags.bits >> _powersetDomains.bitWidth];
42+
static FlatTypeMaskKind _getKind(_Flags flags) =>
43+
FlatTypeMaskKind.values[flags.bitset.bits >> _powersetDomains.bitWidth];
3244

33-
static Bitset _getPowerset(Bitset flags) => _powersetDomains.restrict(flags);
45+
static Bitset _getPowerset(_Flags flags) =>
46+
_powersetDomains.restrict(flags.bitset);
3447

3548
static EnumSet<TypeMaskSpecialValue> _composeSpecialValues({
3649
required bool isNullable,
@@ -166,13 +179,13 @@ class FlatTypeMask extends TypeMask {
166179
return FlatTypeMask._cached(base, _computeFlags(kind, powerset), domain);
167180
}
168181

169-
const FlatTypeMask._(this.base, this.flags);
182+
const FlatTypeMask._(this.base, this._flags);
170183

171184
factory FlatTypeMask._cached(
172185
ClassEntity? base,
173-
Bitset flags,
186+
_Flags flags,
174187
CommonMasks domain,
175-
) => domain.getCachedMask(base, flags, () {
188+
) => domain._getCachedMask(base, flags, () {
176189
final mask = FlatTypeMask._(base, flags);
177190
// Since this is the only place [FlatTypeMask]s are allocated, it is an
178191
// invariant that every [FlatTypeMask] is normalized.
@@ -220,7 +233,7 @@ class FlatTypeMask extends TypeMask {
220233
) {
221234
source.begin(tag);
222235
final base = source.readClassOrNull();
223-
final flags = Bitset(source.readInt());
236+
final flags = _Flags(Bitset(source.readInt()));
224237
source.end(tag);
225238
return FlatTypeMask._cached(base, flags, domain);
226239
}
@@ -231,40 +244,44 @@ class FlatTypeMask extends TypeMask {
231244
sink.writeEnum(TypeMaskKind.flat);
232245
sink.begin(tag);
233246
sink.writeClassOrNull(base);
234-
sink.writeInt(flags.bits);
247+
sink.writeInt(_flags.bitset.bits);
235248
sink.end(tag);
236249
}
237250

238-
FlatTypeMaskKind get _kind => _getKind(flags);
251+
FlatTypeMaskKind get _kind => _getKind(_flags);
239252

240253
@override
241-
Bitset get powerset => _getPowerset(flags);
254+
Bitset get powerset => _getPowerset(_flags);
242255

243256
ClassQuery get _classQuery =>
244257
isExact
245258
? ClassQuery.exact
246259
: (isSubclass ? ClassQuery.subclass : ClassQuery.subtype);
247260

248261
@override
249-
bool get isEmpty => isEmptyOrSpecial && _specialValueDomain.isEmpty(flags);
262+
bool get isEmpty =>
263+
isEmptyOrSpecial && _specialValueDomain.isEmpty(_flags.bitset);
250264
@override
251265
bool get isNull =>
252-
isEmptyOrSpecial && _specialValueDomain.restrict(flags) == _nullBit;
266+
isEmptyOrSpecial &&
267+
_specialValueDomain.restrict(_flags.bitset) == _nullBit;
253268
@override
254269
bool get isEmptyOrSpecial => _kind == FlatTypeMaskKind.empty;
255270
@override
256271
bool get isExact => _kind == FlatTypeMaskKind.exact;
257272
@override
258273
bool get isNullable =>
259-
_specialValueDomain.contains(flags, TypeMaskSpecialValue.null_);
274+
_specialValueDomain.contains(_flags.bitset, TypeMaskSpecialValue.null_);
260275
@override
261-
bool get hasLateSentinel =>
262-
_specialValueDomain.contains(flags, TypeMaskSpecialValue.lateSentinel);
276+
bool get hasLateSentinel => _specialValueDomain.contains(
277+
_flags.bitset,
278+
TypeMaskSpecialValue.lateSentinel,
279+
);
263280
@override
264281
AbstractBool get isLateSentinel {
265282
if (!hasLateSentinel) return AbstractBool.false_;
266283
if (isEmptyOrSpecial &&
267-
_specialValueDomain.restrict(flags) == _lateSentinelBit) {
284+
_specialValueDomain.restrict(_flags.bitset) == _lateSentinelBit) {
268285
return AbstractBool.true_;
269286
}
270287
return AbstractBool.maybe;
@@ -279,7 +296,7 @@ class FlatTypeMask extends TypeMask {
279296
@override
280297
FlatTypeMask withPowerset(Bitset powerset, CommonMasks domain) {
281298
final newFlags = _computeFlags(_kind, powerset);
282-
if (newFlags == flags) return this;
299+
if (newFlags == _flags) return this;
283300
return FlatTypeMask._cached(base, newFlags, domain);
284301
}
285302

@@ -436,7 +453,7 @@ class FlatTypeMask extends TypeMask {
436453
TypeMask union(TypeMask other, CommonMasks domain) {
437454
JClosedWorld closedWorld = domain.closedWorld;
438455
if (other is! FlatTypeMask) return other.union(this, domain);
439-
final powerset = this.powerset.union(other.powerset);
456+
final powerset = _powersetOfFlagsUnion(_flags, other._flags);
440457
if (isEmptyOrSpecial) {
441458
return other.withPowerset(powerset, domain);
442459
} else if (other.isEmptyOrSpecial) {
@@ -464,13 +481,14 @@ class FlatTypeMask extends TypeMask {
464481
// The two masks share the base type, so we must chose the least
465482
// constraining kind (the highest) of the two. As both masks are normalized,
466483
// the result will be, too.
467-
final combined =
468-
(flags.bits > other.flags.bits)
469-
? flags.union(other.powerset)
470-
: other.flags.union(powerset);
471-
if (flags == combined) {
484+
final combined = _Flags(
485+
(_flags.bitset.bits > other._flags.bitset.bits)
486+
? _flags.bitset.union(other.powerset)
487+
: other._flags.bitset.union(powerset),
488+
);
489+
if (_flags == combined) {
472490
return this;
473-
} else if (other.flags == combined) {
491+
} else if (other._flags == combined) {
474492
return other;
475493
} else {
476494
return FlatTypeMask._cached(base, combined, domain);
@@ -481,25 +499,21 @@ class FlatTypeMask extends TypeMask {
481499
assert(base != other.base);
482500
assert(domain.closedWorld.classHierarchy.isSubclassOf(other.base!, base!));
483501
final FlatTypeMaskKind combinedKind;
484-
final Bitset combinedPowerset;
502+
final Bitset combinedPowerset = _powersetOfFlagsUnion(_flags, other._flags);
485503
if ((isExact && other.isExact) ||
486504
base == domain.commonElements.objectClass) {
487505
// Since the other mask is a subclass of this mask, we need the
488506
// resulting union to be a subclass too.
489507
combinedKind = FlatTypeMaskKind.subclass;
490-
combinedPowerset = powerset.union(other.powerset);
491508
if (combinedKind == _kind && combinedPowerset == powerset) {
492509
return this;
493510
}
494511
} else {
495512
// Both masks are at least subclass masks, so we pick the least
496513
// constraining kind (the highest) of the two.
497-
final combined =
498-
(flags.bits > other.flags.bits)
499-
? flags.union(other.powerset)
500-
: other.flags.union(powerset);
501-
combinedKind = _getKind(combined);
502-
combinedPowerset = _getPowerset(combined);
514+
combinedKind = _getKind(
515+
_flags.bitset.bits > other._flags.bitset.bits ? _flags : other._flags,
516+
);
503517
}
504518
// If we weaken the constraint on this type, we have to make sure that
505519
// the result is normalized.
@@ -518,7 +532,7 @@ class FlatTypeMask extends TypeMask {
518532
// Since the other mask is a subtype of this mask, we need the
519533
// resulting union to be a subtype too.
520534
final combinedKind = FlatTypeMaskKind.subtype;
521-
final combinedPowerset = powerset.union(other.powerset);
535+
final combinedPowerset = _powersetOfFlagsUnion(_flags, other._flags);
522536
return combinedKind == _kind && combinedPowerset == powerset
523537
? this
524538
// If we weaken the constraint on this type, we have to make sure that
@@ -538,7 +552,7 @@ class FlatTypeMask extends TypeMask {
538552
if (other is! FlatTypeMask) return other.intersection(this, domain);
539553

540554
final otherBase = other.base;
541-
final powerset = this.powerset.intersection(other.powerset);
555+
final powerset = _powersetOfFlagsIntersection(_flags, other._flags);
542556
final includeNull = _specialValueDomain.contains(
543557
powerset,
544558
TypeMaskSpecialValue.null_,
@@ -683,14 +697,19 @@ class FlatTypeMask extends TypeMask {
683697
// The two masks share the base type, so we must chose the most
684698
// constraining kind (the lowest) of the two. The result will be normalized,
685699
// as the two inputs are normalized, too.
686-
final combined =
687-
(flags.bits < other.flags.bits)
688-
? flags.intersection(other.flags.union(_powersetDomains.notMask))
689-
: other.flags.intersection(flags.union(_powersetDomains.notMask));
700+
final combined = _Flags(
701+
(_flags.bitset.bits < other._flags.bitset.bits)
702+
? _flags.bitset.intersection(
703+
other._flags.bitset.union(_powersetDomains.notMask),
704+
)
705+
: other._flags.bitset.intersection(
706+
_flags.bitset.union(_powersetDomains.notMask),
707+
),
708+
);
690709

691-
if (flags == combined) {
710+
if (_flags == combined) {
692711
return this;
693-
} else if (other.flags == combined) {
712+
} else if (other._flags == combined) {
694713
return other;
695714
} else {
696715
return FlatTypeMask._cached(base, combined, domain);
@@ -706,10 +725,12 @@ class FlatTypeMask extends TypeMask {
706725
// Only the other mask puts constraints on the intersection mask,
707726
// so base the combined flags on the other mask. The result is guaranteed to
708727
// be normalized, as the other type was normalized.
709-
final combined = other.flags.intersection(
710-
flags.union(_powersetDomains.notMask),
728+
final combined = _Flags(
729+
other._flags.bitset.intersection(
730+
_flags.bitset.union(_powersetDomains.notMask),
731+
),
711732
);
712-
if (other.flags == combined) {
733+
if (other._flags == combined) {
713734
return other;
714735
} else {
715736
return FlatTypeMask._cached(other.base, combined, domain);
@@ -868,12 +889,12 @@ class FlatTypeMask extends TypeMask {
868889
if (identical(this, other)) return true;
869890
if (other is! FlatTypeMask) return false;
870891
FlatTypeMask otherMask = other;
871-
return (flags == otherMask.flags) && (base == otherMask.base);
892+
return (_flags == otherMask._flags) && (base == otherMask.base);
872893
}
873894

874895
@override
875896
int get hashCode {
876-
return (base == null ? 0 : base.hashCode) + 31 * flags.hashCode;
897+
return (base == null ? 0 : base.hashCode) + 31 * _flags.hashCode;
877898
}
878899

879900
@override

pkg/compiler/lib/src/inferrer/typemasks/masks.dart

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,20 @@ part 'value_type_mask.dart';
4646
// TODO(60419): Go back to using a record as the key when it is no longer slower
4747
// than using a data class.
4848
final class _CanonicalizedTypeMaskKey {
49-
final ClassEntity? base;
50-
final Bitset flags;
49+
final ClassEntity? _base;
50+
final _Flags _flags;
5151

52-
const _CanonicalizedTypeMaskKey(this.base, this.flags);
52+
const _CanonicalizedTypeMaskKey(this._base, this._flags);
5353

5454
@override
55-
int get hashCode => Object.hash(base, flags);
55+
int get hashCode => Object.hash(_base, _flags);
5656

5757
@override
5858
bool operator ==(Object other) =>
5959
identical(this, other) ||
6060
other is _CanonicalizedTypeMaskKey &&
61-
base == other.base &&
62-
flags == other.flags;
61+
_base == other._base &&
62+
_flags == other._flags;
6363
}
6464

6565
class CommonMasks with AbstractValueDomain {
@@ -78,11 +78,11 @@ class CommonMasks with AbstractValueDomain {
7878
final Map<_CanonicalizedTypeMaskKey, FlatTypeMask> _canonicalizedTypeMasks =
7979
{};
8080

81-
/// Return the cached mask for [base] with the given special values, or
82-
/// calls [createMask] to create the mask and cache it.
83-
FlatTypeMask getCachedMask(
81+
/// Return the cached mask for [base] with the given flags, or calls
82+
/// [createMask] to create the mask and cache it.
83+
FlatTypeMask _getCachedMask(
8484
ClassEntity? base,
85-
Bitset flags,
85+
_Flags flags,
8686
FlatTypeMask Function() createMask,
8787
) => _canonicalizedTypeMasks.putIfAbsent(
8888
_CanonicalizedTypeMaskKey(base, flags),

0 commit comments

Comments
 (0)