From 39fd70e8161f9dde29bcdcd0b81f850c07a550eb Mon Sep 17 00:00:00 2001 From: Mayank Patke Date: Mon, 13 Sep 2021 21:59:20 +0000 Subject: [PATCH] [dart2js] Refactor typemasks. Change-Id: I13886baba98f70614cd05e6ee5cb38421264b537 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210645 Reviewed-by: Stephen Adams Reviewed-by: Joshua Litt --- .../src/inferrer/abstract_value_domain.dart | 8 - .../src/inferrer/powersets/powerset_bits.dart | 4 - .../lib/src/inferrer/powersets/powersets.dart | 12 - .../lib/src/inferrer/powersets/wrapped.dart | 8 - pkg/compiler/lib/src/inferrer/trivial.dart | 6 - .../lib/src/inferrer/type_system.dart | 10 +- .../typemasks/container_type_mask.dart | 77 ++---- .../typemasks/dictionary_type_mask.dart | 97 +++----- .../inferrer/typemasks/flat_type_mask.dart | 231 ++++++++++-------- .../typemasks/forwarding_type_mask.dart | 59 +++-- .../src/inferrer/typemasks/map_type_mask.dart | 87 +++---- .../lib/src/inferrer/typemasks/masks.dart | 142 +++++------ .../src/inferrer/typemasks/set_type_mask.dart | 67 ++--- .../lib/src/inferrer/typemasks/type_mask.dart | 91 ++++--- .../inferrer/typemasks/union_type_mask.dart | 124 +++++----- .../inferrer/typemasks/value_type_mask.dart | 52 ++-- pkg/compiler/lib/src/ssa/nodes.dart | 3 - pkg/compiler/lib/src/util/util.dart | 9 - .../test/analyses/dart2js_allowed.json | 56 ----- .../test/inference/type_combination_test.dart | 112 +++++---- .../test/inference/type_mask2_test.dart | 34 +-- .../test/inference/type_mask_test_helper.dart | 3 +- 22 files changed, 560 insertions(+), 732 deletions(-) diff --git a/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart b/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart index 93a30d07575..2aa58d412fe 100644 --- a/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart +++ b/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart @@ -299,10 +299,6 @@ abstract class AbstractValueDomain { /// exact class at runtime. AbstractBool isExact(covariant AbstractValue value); - /// Returns an [AbstractBool] that describes whether [value] is an exact class - /// or `null` at runtime. - AbstractBool isExactOrNull(covariant AbstractValue value); - /// Returns the [ClassEntity] if this [value] is a non-null instance of an /// exact class at runtime, and `null` otherwise. ClassEntity getExactClass(covariant AbstractValue value); @@ -323,10 +319,6 @@ abstract class AbstractValueDomain { /// bool at runtime. AbstractBool isPrimitiveBoolean(covariant AbstractValue value); - /// Returns an [AbstractBool] that describes whether [value] is a JavaScript - /// array at runtime. - AbstractBool isPrimitiveArray(covariant AbstractValue value); - /// Returns an [AbstractBool] that describes whether [value] is a JavaScript /// string, array, native HTML list or `null` at runtime. AbstractBool isIndexablePrimitive(covariant AbstractValue value); diff --git a/pkg/compiler/lib/src/inferrer/powersets/powerset_bits.dart b/pkg/compiler/lib/src/inferrer/powersets/powerset_bits.dart index 22a900bc90d..e46606e76da 100644 --- a/pkg/compiler/lib/src/inferrer/powersets/powerset_bits.dart +++ b/pkg/compiler/lib/src/inferrer/powersets/powerset_bits.dart @@ -281,8 +281,6 @@ class PowersetBitsDomain { AbstractBool isIndexablePrimitive(int value) => isOther(value); - AbstractBool isPrimitiveArray(int value) => isOther(value); - AbstractBool isPrimitiveBoolean(int value) { if (isDefinitelyTrue(value) || isDefinitelyFalse(value)) { return AbstractBool.True; @@ -300,8 +298,6 @@ class PowersetBitsDomain { ? AbstractBool.True : (isPotentiallyNull(value) ? AbstractBool.Maybe : AbstractBool.False); - AbstractBool isExactOrNull(int value) => AbstractBool.Maybe; - AbstractBool isExact(int value) => AbstractBool.Maybe; AbstractBool isEmpty(int value) { diff --git a/pkg/compiler/lib/src/inferrer/powersets/powersets.dart b/pkg/compiler/lib/src/inferrer/powersets/powersets.dart index fedb435f1c5..e348bf4e0e2 100644 --- a/pkg/compiler/lib/src/inferrer/powersets/powersets.dart +++ b/pkg/compiler/lib/src/inferrer/powersets/powersets.dart @@ -527,12 +527,6 @@ class PowersetDomain implements AbstractValueDomain { _powersetBitsDomain.isIndexablePrimitive(value._powersetBits), _abstractValueDomain.isIndexablePrimitive(value._abstractValue)); - @override - AbstractBool isPrimitiveArray(covariant PowersetValue value) => - AbstractBool.strengthen( - _powersetBitsDomain.isPrimitiveArray(value._powersetBits), - _abstractValueDomain.isPrimitiveArray(value._abstractValue)); - @override AbstractBool isPrimitiveBoolean(covariant PowersetValue value) => AbstractBool.strengthen( @@ -560,12 +554,6 @@ class PowersetDomain implements AbstractValueDomain { ClassEntity getExactClass(covariant PowersetValue value) => _abstractValueDomain.getExactClass(value._abstractValue); - @override - AbstractBool isExactOrNull(covariant PowersetValue value) => - AbstractBool.strengthen( - _powersetBitsDomain.isExactOrNull(value._powersetBits), - _abstractValueDomain.isExactOrNull(value._abstractValue)); - @override AbstractBool isExact(covariant PowersetValue value) => AbstractBool.strengthen(_powersetBitsDomain.isExact(value._powersetBits), diff --git a/pkg/compiler/lib/src/inferrer/powersets/wrapped.dart b/pkg/compiler/lib/src/inferrer/powersets/wrapped.dart index d60a060f8a2..d91774484e4 100644 --- a/pkg/compiler/lib/src/inferrer/powersets/wrapped.dart +++ b/pkg/compiler/lib/src/inferrer/powersets/wrapped.dart @@ -382,10 +382,6 @@ class WrappedAbstractValueDomain implements AbstractValueDomain { AbstractBool isIndexablePrimitive(covariant WrappedAbstractValue value) => _abstractValueDomain.isIndexablePrimitive(value._abstractValue); - @override - AbstractBool isPrimitiveArray(covariant WrappedAbstractValue value) => - _abstractValueDomain.isPrimitiveArray(value._abstractValue); - @override AbstractBool isPrimitiveBoolean(covariant WrappedAbstractValue value) => _abstractValueDomain.isPrimitiveBoolean(value._abstractValue); @@ -406,10 +402,6 @@ class WrappedAbstractValueDomain implements AbstractValueDomain { ClassEntity getExactClass(covariant WrappedAbstractValue value) => _abstractValueDomain.getExactClass(value._abstractValue); - @override - AbstractBool isExactOrNull(covariant WrappedAbstractValue value) => - _abstractValueDomain.isExactOrNull(value._abstractValue); - @override AbstractBool isExact(covariant WrappedAbstractValue value) => _abstractValueDomain.isExact(value._abstractValue); diff --git a/pkg/compiler/lib/src/inferrer/trivial.dart b/pkg/compiler/lib/src/inferrer/trivial.dart index 27e4f12b0f8..b8966bf1c51 100644 --- a/pkg/compiler/lib/src/inferrer/trivial.dart +++ b/pkg/compiler/lib/src/inferrer/trivial.dart @@ -274,9 +274,6 @@ class TrivialAbstractValueDomain implements AbstractValueDomain { @override AbstractBool isIndexablePrimitive(AbstractValue value) => AbstractBool.Maybe; - @override - AbstractBool isPrimitiveArray(AbstractValue value) => AbstractBool.Maybe; - @override AbstractBool isPrimitiveBoolean(AbstractValue value) => AbstractBool.Maybe; @@ -292,9 +289,6 @@ class TrivialAbstractValueDomain implements AbstractValueDomain { @override ClassEntity getExactClass(AbstractValue value) => null; - @override - AbstractBool isExactOrNull(AbstractValue value) => AbstractBool.Maybe; - @override AbstractBool isExact(AbstractValue value) => AbstractBool.Maybe; diff --git a/pkg/compiler/lib/src/inferrer/type_system.dart b/pkg/compiler/lib/src/inferrer/type_system.dart index 85f2de5485f..aa5a9d1c5be 100644 --- a/pkg/compiler/lib/src/inferrer/type_system.dart +++ b/pkg/compiler/lib/src/inferrer/type_system.dart @@ -648,7 +648,7 @@ class TypeSystem { // mapped iterable, we save the intermediate results to avoid computing them // again. var list = []; - bool isDynamicIngoringNull = false; + bool isDynamicIgnoringNull = false; bool mayBeNull = false; for (AbstractValue mask in masks) { // Don't do any work on computing unions if we know that after all that @@ -656,12 +656,12 @@ class TypeSystem { // TODO(sigmund): change to `mask == dynamicType` so we can continue to // track the non-nullable bit. if (_abstractValueDomain.containsAll(mask).isPotentiallyTrue) { - isDynamicIngoringNull = true; + isDynamicIgnoringNull = true; } if (_abstractValueDomain.isNull(mask).isPotentiallyTrue) { mayBeNull = true; } - if (isDynamicIngoringNull && mayBeNull) return dynamicType; + if (isDynamicIgnoringNull && mayBeNull) return dynamicType; list.add(mask); } @@ -671,12 +671,12 @@ class TypeSystem { newType == null ? mask : _abstractValueDomain.union(newType, mask); // Likewise - stop early if we already reach dynamic. if (_abstractValueDomain.containsAll(newType).isPotentiallyTrue) { - isDynamicIngoringNull = true; + isDynamicIgnoringNull = true; } if (_abstractValueDomain.isNull(newType).isPotentiallyTrue) { mayBeNull = true; } - if (isDynamicIngoringNull && mayBeNull) return dynamicType; + if (isDynamicIgnoringNull && mayBeNull) return dynamicType; } return newType ?? _abstractValueDomain.emptyType; diff --git a/pkg/compiler/lib/src/inferrer/typemasks/container_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/container_type_mask.dart index 188a95163da..3d0564e1051 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/container_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/container_type_mask.dart @@ -27,20 +27,20 @@ class ContainerTypeMask extends AllocationTypeMask { // The length of the container. final int length; - ContainerTypeMask(this.forwardTo, this.allocationNode, this.allocationElement, - this.elementType, this.length); + const ContainerTypeMask(this.forwardTo, this.allocationNode, + this.allocationElement, this.elementType, this.length); /// Deserializes a [ContainerTypeMask] object from [source]. factory ContainerTypeMask.readFromDataSource( DataSource source, CommonMasks domain) { source.begin(tag); - TypeMask forwardTo = new TypeMask.readFromDataSource(source, domain); + TypeMask forwardTo = TypeMask.readFromDataSource(source, domain); ir.TreeNode allocationNode = source.readTreeNodeOrNull(); MemberEntity allocationElement = source.readMemberOrNull(); - TypeMask elementType = new TypeMask.readFromDataSource(source, domain); + TypeMask elementType = TypeMask.readFromDataSource(source, domain); int length = source.readIntOrNull(); source.end(tag); - return new ContainerTypeMask( + return ContainerTypeMask( forwardTo, allocationNode, allocationElement, elementType, length); } @@ -58,19 +58,13 @@ class ContainerTypeMask extends AllocationTypeMask { } @override - TypeMask nullable() { - return isNullable - ? this - : new ContainerTypeMask(forwardTo.nullable(), allocationNode, - allocationElement, elementType, length); - } - - @override - TypeMask nonNullable() { - return isNullable - ? new ContainerTypeMask(forwardTo.nonNullable(), allocationNode, - allocationElement, elementType, length) - : this; + ContainerTypeMask withFlags({bool isNullable}) { + isNullable ??= this.isNullable; + if (isNullable == this.isNullable) { + return this; + } + return ContainerTypeMask(forwardTo.withFlags(isNullable: isNullable), + allocationNode, allocationElement, elementType, length); } @override @@ -79,36 +73,16 @@ class ContainerTypeMask extends AllocationTypeMask { bool get isExact => true; @override - bool equalsDisregardNull(other) { - if (other is! ContainerTypeMask) return false; - return super.equalsDisregardNull(other) && - allocationNode == other.allocationNode && - elementType == other.elementType && - length == other.length; - } - - @override - TypeMask intersection(TypeMask other, CommonMasks domain) { - TypeMask forwardIntersection = forwardTo.intersection(other, domain); - if (forwardIntersection.isEmptyOrNull) return forwardIntersection; - return forwardIntersection.isNullable ? nullable() : nonNullable(); - } - - @override - TypeMask union(dynamic other, CommonMasks domain) { - if (this == other) { - return this; - } else if (equalsDisregardNull(other)) { - return other.isNullable ? other : this; - } else if (other.isEmptyOrNull) { - return other.isNullable ? this.nullable() : this; - } else if (other.isContainer && + TypeMask _unionSpecialCases(TypeMask other, CommonMasks domain, + {bool isNullable}) { + assert(isNullable != null); + if (other is ContainerTypeMask && elementType != null && other.elementType != null) { TypeMask newElementType = elementType.union(other.elementType, domain); int newLength = (length == other.length) ? length : null; TypeMask newForwardTo = forwardTo.union(other.forwardTo, domain); - return new ContainerTypeMask( + return ContainerTypeMask( newForwardTo, allocationNode == other.allocationNode ? allocationNode : null, allocationElement == other.allocationElement @@ -116,19 +90,22 @@ class ContainerTypeMask extends AllocationTypeMask { : null, newElementType, newLength); - } else { - return forwardTo.union(other, domain); } + return null; } @override - bool operator ==(other) => super == other; + bool operator ==(other) { + if (identical(this, other)) return true; + if (other is! ContainerTypeMask) return false; + return super == other && + elementType == other.elementType && + length == other.length; + } @override - int get hashCode { - return computeHashCode( - allocationNode, isNullable, elementType, length, forwardTo); - } + int get hashCode => Hashing.objectHash( + length, Hashing.objectHash(elementType, super.hashCode)); @override String toString() { diff --git a/pkg/compiler/lib/src/inferrer/typemasks/dictionary_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/dictionary_type_mask.dart index cc54437256c..1fcd10f4c9e 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/dictionary_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/dictionary_type_mask.dart @@ -16,9 +16,9 @@ class DictionaryTypeMask extends MapTypeMask { static const String tag = 'dictionary-type-mask'; // The underlying key/value map of this dictionary. - final Map _typeMap; + final Map _typeMap; - DictionaryTypeMask( + const DictionaryTypeMask( TypeMask forwardTo, ir.Node allocationNode, MemberEntity allocationElement, @@ -31,15 +31,15 @@ class DictionaryTypeMask extends MapTypeMask { factory DictionaryTypeMask.readFromDataSource( DataSource source, CommonMasks domain) { source.begin(tag); - TypeMask forwardTo = new TypeMask.readFromDataSource(source, domain); + TypeMask forwardTo = TypeMask.readFromDataSource(source, domain); ir.TreeNode allocationNode = source.readTreeNodeOrNull(); MemberEntity allocationElement = source.readMemberOrNull(); - TypeMask keyType = new TypeMask.readFromDataSource(source, domain); - TypeMask valueType = new TypeMask.readFromDataSource(source, domain); - Map typeMap = source - .readStringMap(() => new TypeMask.readFromDataSource(source, domain)); + TypeMask keyType = TypeMask.readFromDataSource(source, domain); + TypeMask valueType = TypeMask.readFromDataSource(source, domain); + Map typeMap = + source.readStringMap(() => TypeMask.readFromDataSource(source, domain)); source.end(tag); - return new DictionaryTypeMask(forwardTo, allocationNode, allocationElement, + return DictionaryTypeMask(forwardTo, allocationNode, allocationElement, keyType, valueType, typeMap); } @@ -53,27 +53,20 @@ class DictionaryTypeMask extends MapTypeMask { sink.writeMemberOrNull(allocationElement); keyType.writeToDataSink(sink); valueType.writeToDataSink(sink); - sink.writeStringMap(_typeMap, (AbstractValue value) { - TypeMask typeMask = value; + sink.writeStringMap(_typeMap, (TypeMask typeMask) { typeMask.writeToDataSink(sink); }); sink.end(tag); } @override - TypeMask nullable() { - return isNullable - ? this - : new DictionaryTypeMask(forwardTo.nullable(), allocationNode, - allocationElement, keyType, valueType, _typeMap); - } - - @override - TypeMask nonNullable() { - return isNullable - ? new DictionaryTypeMask(forwardTo.nonNullable(), allocationNode, - allocationElement, keyType, valueType, _typeMap) - : this; + DictionaryTypeMask withFlags({bool isNullable}) { + isNullable ??= this.isNullable; + if (isNullable == this.isNullable) { + return this; + } + return DictionaryTypeMask(forwardTo.withFlags(isNullable: isNullable), + allocationNode, allocationElement, keyType, valueType, _typeMap); } @override @@ -86,37 +79,15 @@ class DictionaryTypeMask extends MapTypeMask { TypeMask getValueForKey(String key) => _typeMap[key]; @override - bool equalsDisregardNull(other) { - if (other is! DictionaryTypeMask) return false; - return allocationNode == other.allocationNode && - keyType == other.keyType && - valueType == other.valueType && - _typeMap.keys.every((k) => other._typeMap.containsKey(k)) && - other._typeMap.keys.every( - (k) => _typeMap.containsKey(k) && _typeMap[k] == other._typeMap[k]); - } - - @override - TypeMask intersection(TypeMask other, CommonMasks domain) { - TypeMask forwardIntersection = forwardTo.intersection(other, domain); - if (forwardIntersection.isEmptyOrNull) return forwardIntersection; - return forwardIntersection.isNullable ? nullable() : nonNullable(); - } - - @override - TypeMask union(dynamic other, CommonMasks domain) { - if (this == other) { - return this; - } else if (equalsDisregardNull(other)) { - return other.isNullable ? other : this; - } else if (other.isEmptyOrNull) { - return other.isNullable ? this.nullable() : this; - } else if (other.isDictionary) { + TypeMask _unionSpecialCases(TypeMask other, CommonMasks domain, + {bool isNullable}) { + assert(isNullable != null); + if (other is DictionaryTypeMask) { TypeMask newForwardTo = forwardTo.union(other.forwardTo, domain); TypeMask newKeyType = keyType.union(other.keyType, domain); TypeMask newValueType = valueType.union(other.valueType, domain); - Map mappings = {}; - _typeMap.forEach((k, dynamic v) { + Map mappings = {}; + _typeMap.forEach((k, v) { if (!other._typeMap.containsKey(k)) { mappings[k] = v.nullable(); } @@ -128,28 +99,32 @@ class DictionaryTypeMask extends MapTypeMask { mappings[k] = v.nullable(); } }); - return new DictionaryTypeMask( + return DictionaryTypeMask( newForwardTo, null, null, newKeyType, newValueType, mappings); - } else if (other.isMap && + } + if (other is MapTypeMask && (other.keyType != null) && (other.valueType != null)) { TypeMask newForwardTo = forwardTo.union(other.forwardTo, domain); TypeMask newKeyType = keyType.union(other.keyType, domain); TypeMask newValueType = valueType.union(other.valueType, domain); - return new MapTypeMask( - newForwardTo, null, null, newKeyType, newValueType); - } else { - return forwardTo.union(other, domain); + return MapTypeMask(newForwardTo, null, null, newKeyType, newValueType); } + return null; } @override - bool operator ==(other) => super == other; + bool operator ==(other) { + if (identical(this, other)) return true; + if (other is! DictionaryTypeMask) return false; + return super == other && + _typeMap.keys.every((k) => other._typeMap.containsKey(k)) && + other._typeMap.keys.every( + (k) => _typeMap.containsKey(k) && _typeMap[k] == other._typeMap[k]); + } @override - int get hashCode { - return computeHashCode(allocationNode, isNullable, _typeMap, forwardTo); - } + int get hashCode => Hashing.objectHash(_typeMap, super.hashCode); @override String toString() { diff --git a/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart index 451af335e28..4667801df3c 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart @@ -4,74 +4,94 @@ part of masks; +enum _FlatTypeMaskKind { empty, exact, subclass, subtype } + /// A flat type mask is a type mask that has been flattened to contain a /// base type. -class FlatTypeMask implements TypeMask { +class FlatTypeMask extends TypeMask { /// Tag used for identifying serialized [FlatTypeMask] objects in a /// debugging data stream. static const String tag = 'flat-type-mask'; - static const int EMPTY = 0; - static const int EXACT = 1; - static const int SUBCLASS = 2; - static const int SUBTYPE = 3; + static const int _NULL_INDEX = 0; + static const int _USED_INDICES = 1; + + static const int _NONE_MASK = 0; + static const int _NULL_MASK = 1 << _NULL_INDEX; + static const int _ALL_MASK = (1 << _USED_INDICES) - 1; final ClassEntity base; final int flags; - factory FlatTypeMask.exact(ClassEntity base, JClosedWorld world) => - FlatTypeMask._canonicalize(base, EXACT, true, world); - factory FlatTypeMask.subclass(ClassEntity base, JClosedWorld world) => - FlatTypeMask._canonicalize(base, SUBCLASS, true, world); - factory FlatTypeMask.subtype(ClassEntity base, JClosedWorld world) => - FlatTypeMask._canonicalize(base, SUBTYPE, true, world); + static int _computeFlags(_FlatTypeMaskKind kind, {bool hasNull: false}) { + int mask = _NONE_MASK; + if (hasNull) mask |= _NULL_MASK; + return _computeFlagsRaw(kind.index, mask); + } - const FlatTypeMask.nonNullEmpty() - : base = null, - flags = 0; - const FlatTypeMask.empty() - : base = null, - flags = 1; + static int _computeFlagsRaw(int kind, int mask) => + kind << _USED_INDICES | mask; + + static _FlatTypeMaskKind _lookupKind(int flags) => + _FlatTypeMaskKind.values[flags >> _USED_INDICES]; + + static bool _hasNullFlag(int flags) => flags & _NULL_MASK != _NONE_MASK; + + factory FlatTypeMask.exact(ClassEntity base, JClosedWorld world) => + FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.exact, world, + isNullable: true); + factory FlatTypeMask.subclass(ClassEntity base, JClosedWorld world) => + FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.subclass, world, + isNullable: true); + factory FlatTypeMask.subtype(ClassEntity base, JClosedWorld world) => + FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.subtype, world, + isNullable: true); + + factory FlatTypeMask.nonNullEmpty() => const FlatTypeMask._(null, _NONE_MASK); + + factory FlatTypeMask.empty() => const FlatTypeMask._(null, _NULL_MASK); factory FlatTypeMask.nonNullExact(ClassEntity base, JClosedWorld world) => - FlatTypeMask._canonicalize(base, EXACT, false, world); + FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.exact, world); factory FlatTypeMask.nonNullSubclass(ClassEntity base, JClosedWorld world) => - FlatTypeMask._canonicalize(base, SUBCLASS, false, world); + FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.subclass, world); factory FlatTypeMask.nonNullSubtype(ClassEntity base, JClosedWorld world) => - FlatTypeMask._canonicalize(base, SUBTYPE, false, world); + FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.subtype, world); factory FlatTypeMask._canonicalize( - ClassEntity base, int kind, bool isNullable, JClosedWorld world) { + ClassEntity base, _FlatTypeMaskKind kind, JClosedWorld world, + {bool isNullable: false}) { if (base == world.commonElements.nullClass) { return FlatTypeMask.empty(); } - return FlatTypeMask._(base, (kind << 1) | (isNullable ? 1 : 0)); + return FlatTypeMask._(base, _computeFlags(kind, hasNull: isNullable)); } - FlatTypeMask._(this.base, this.flags); + const FlatTypeMask._(this.base, this.flags); /// Ensures that the generated mask is normalized, i.e., a call to /// [TypeMask.assertIsNormalized] with the factory's result returns `true`. factory FlatTypeMask.normalized( ClassEntity base, int flags, CommonMasks domain) { + bool isNullable = _hasNullFlag(flags); if (base == domain.commonElements.nullClass) { return FlatTypeMask.empty(); } - if ((flags >> 1) == EMPTY || ((flags >> 1) == EXACT)) { - return new FlatTypeMask._(base, flags); + _FlatTypeMaskKind kind = _lookupKind(flags); + if (kind == _FlatTypeMaskKind.empty || kind == _FlatTypeMaskKind.exact) { + return FlatTypeMask._(base, flags); } - if ((flags >> 1) == SUBTYPE) { + if (kind == _FlatTypeMaskKind.subtype) { if (!domain._closedWorld.classHierarchy.hasAnyStrictSubtype(base) || domain._closedWorld.classHierarchy.hasOnlySubclasses(base)) { - flags = (flags & 0x1) | (SUBCLASS << 1); + flags = _computeFlags(_FlatTypeMaskKind.subclass, hasNull: isNullable); } } - if (((flags >> 1) == SUBCLASS) && + if (kind == _FlatTypeMaskKind.subclass && !domain._closedWorld.classHierarchy.hasAnyStrictSubclass(base)) { - flags = (flags & 0x1) | (EXACT << 1); + flags = _computeFlags(_FlatTypeMaskKind.exact, hasNull: isNullable); } - return domain.getCachedMask( - base, flags, () => new FlatTypeMask._(base, flags)); + return domain.getCachedMask(base, flags, () => FlatTypeMask._(base, flags)); } /// Deserializes a [FlatTypeMask] object from [source]. @@ -81,8 +101,7 @@ class FlatTypeMask implements TypeMask { ClassEntity base = source.readClassOrNull(); int flags = source.readInt(); source.end(tag); - return domain.getCachedMask( - base, flags, () => new FlatTypeMask._(base, flags)); + return domain.getCachedMask(base, flags, () => FlatTypeMask._(base, flags)); } /// Serializes this [FlatTypeMask] to [sink]. @@ -95,20 +114,24 @@ class FlatTypeMask implements TypeMask { sink.end(tag); } + _FlatTypeMaskKind get _kind => _lookupKind(flags); + + int get _mask => flags & _ALL_MASK; + ClassQuery get _classQuery => isExact ? ClassQuery.EXACT : (isSubclass ? ClassQuery.SUBCLASS : ClassQuery.SUBTYPE); @override - bool get isEmpty => isEmptyOrNull && !isNullable; + bool get isEmpty => isEmptyOrFlagged && _mask == _NONE_MASK; @override - bool get isNull => isEmptyOrNull && isNullable; + bool get isNull => isEmptyOrFlagged && _mask == _NULL_MASK; @override - bool get isEmptyOrNull => (flags >> 1) == EMPTY; + bool get isEmptyOrFlagged => _kind == _FlatTypeMaskKind.empty; @override - bool get isExact => (flags >> 1) == EXACT; + bool get isExact => _kind == _FlatTypeMaskKind.exact; @override - bool get isNullable => (flags & 1) != 0; + bool get isNullable => _hasNullFlag(flags); @override bool get isUnion => false; @@ -128,22 +151,19 @@ class FlatTypeMask implements TypeMask { // TODO(kasperl): Get rid of these. They should not be a visible // part of the implementation because they make it hard to add // proper union types if we ever want to. - bool get isSubclass => (flags >> 1) == SUBCLASS; - bool get isSubtype => (flags >> 1) == SUBTYPE; + bool get isSubclass => _kind == _FlatTypeMaskKind.subclass; + bool get isSubtype => _kind == _FlatTypeMaskKind.subtype; @override - TypeMask nullable() { - return isNullable ? this : new FlatTypeMask._(base, flags | 1); - } - - @override - TypeMask nonNullable() { - return isNullable ? new FlatTypeMask._(base, flags & ~1) : this; + FlatTypeMask withFlags({bool isNullable}) { + int newFlags = _computeFlags(_kind, hasNull: isNullable ?? this.isNullable); + if (newFlags == flags) return this; + return FlatTypeMask._(base, newFlags); } @override bool contains(ClassEntity other, JClosedWorld closedWorld) { - if (isEmptyOrNull) { + if (isEmptyOrFlagged) { return false; } else if (identical(base, other)) { return true; @@ -184,11 +204,11 @@ class FlatTypeMask implements TypeMask { @override bool isInMask(TypeMask other, JClosedWorld closedWorld) { - if (isEmptyOrNull) return isNullable ? other.isNullable : true; - // The empty type contains no classes. - if (other.isEmptyOrNull) return false; // Quick check whether to handle null. if (isNullable && !other.isNullable) return false; + // The empty type contains no classes. + if (isEmptyOrFlagged) return true; + if (other.isEmptyOrFlagged) return false; other = TypeMask.nonForwardingMask(other); // If other is union, delegate to UnionTypeMask.containsMask. if (other is! FlatTypeMask) return other.containsMask(this, closedWorld); @@ -259,14 +279,14 @@ class FlatTypeMask implements TypeMask { @override bool satisfies(ClassEntity cls, JClosedWorld closedWorld) { - if (isEmptyOrNull) return false; + if (isEmptyOrFlagged) return false; if (closedWorld.classHierarchy.isSubtypeOf(base, cls)) return true; return false; } @override ClassEntity singleClass(JClosedWorld closedWorld) { - if (isEmptyOrNull) return null; + if (isEmptyOrFlagged) return null; if (isNullable) return null; // It is Null and some other class. if (isExact) { return base; @@ -282,7 +302,7 @@ class FlatTypeMask implements TypeMask { @override bool containsAll(JClosedWorld closedWorld) { - if (isEmptyOrNull || isExact) return false; + if (isEmptyOrFlagged || isExact) return false; return identical(base, closedWorld.commonElements.objectClass); } @@ -294,10 +314,11 @@ class FlatTypeMask implements TypeMask { assert(TypeMask.assertIsNormalized(other, closedWorld)); if (other is! FlatTypeMask) return other.union(this, domain); FlatTypeMask flatOther = other; - if (isEmptyOrNull) { - return isNullable ? flatOther.nullable() : flatOther; - } else if (flatOther.isEmptyOrNull) { - return flatOther.isNullable ? nullable() : this; + bool isNullable = this.isNullable || flatOther.isNullable; + if (isEmptyOrFlagged) { + return flatOther.withFlags(isNullable: isNullable); + } else if (flatOther.isEmptyOrFlagged) { + return withFlags(isNullable: isNullable); } else if (base == flatOther.base) { return unionSame(flatOther, domain); } else if (closedWorld.classHierarchy.isSubclassOf(flatOther.base, base)) { @@ -309,9 +330,9 @@ class FlatTypeMask implements TypeMask { } else if (closedWorld.classHierarchy.isSubtypeOf(base, flatOther.base)) { return flatOther.unionStrictSubtype(this, domain); } else { - return new UnionTypeMask._internal( - [this.nonNullable(), flatOther.nonNullable()], - isNullable || other.isNullable); + return UnionTypeMask._internal( + [withoutFlags(), flatOther.withoutFlags()], + isNullable: isNullable); } } @@ -323,15 +344,14 @@ class FlatTypeMask implements TypeMask { // constraining kind (the highest) of the two. If either one of // the masks are nullable the result should be nullable too. // As both masks are normalized, the result will be, too. - int combined = (flags > other.flags) - ? flags | (other.flags & 1) - : other.flags | (flags & 1); + int combined = + (flags > other.flags) ? flags | other._mask : other.flags | _mask; if (flags == combined) { return this; } else if (other.flags == combined) { return other; } else { - return new FlatTypeMask.normalized(base, combined, domain); + return FlatTypeMask.normalized(base, combined, domain); } } @@ -346,19 +366,19 @@ class FlatTypeMask implements TypeMask { // Since the other mask is a subclass of this mask, we need the // resulting union to be a subclass too. If either one of the // masks are nullable the result should be nullable too. - combined = (SUBCLASS << 1) | ((flags | other.flags) & 1); + combined = _computeFlagsRaw( + _FlatTypeMaskKind.subclass.index, _mask | other._mask); } else { // Both masks are at least subclass masks, so we pick the least // constraining kind (the highest) of the two. If either one of // the masks are nullable the result should be nullable too. - combined = (flags > other.flags) - ? flags | (other.flags & 1) - : other.flags | (flags & 1); + combined = + (flags > other.flags) ? flags | other._mask : other.flags | _mask; } // If we weaken the constraint on this type, we have to make sure that // the result is normalized. - return (flags != combined) - ? new FlatTypeMask.normalized(base, combined, domain) + return flags != combined + ? FlatTypeMask.normalized(base, combined, domain) : this; } @@ -371,11 +391,12 @@ class FlatTypeMask implements TypeMask { // Since the other mask is a subtype of this mask, we need the // resulting union to be a subtype too. If either one of the masks // are nullable the result should be nullable too. - int combined = (SUBTYPE << 1) | ((flags | other.flags) & 1); + int combined = + _computeFlagsRaw(_FlatTypeMaskKind.subtype.index, _mask | other._mask); // We know there is at least one subtype, [other.base], so no need // to normalize. - return (flags != combined) - ? new FlatTypeMask.normalized(base, combined, domain) + return flags != combined + ? FlatTypeMask.normalized(base, combined, domain) : this; } @@ -391,10 +412,10 @@ class FlatTypeMask implements TypeMask { bool includeNull = isNullable && flatOther.isNullable; - if (isEmptyOrNull) { - return includeNull ? this : nonNullable(); - } else if (flatOther.isEmptyOrNull) { - return includeNull ? other : other.nonNullable(); + if (isEmptyOrFlagged) { + return withFlags(isNullable: includeNull); + } else if (flatOther.isEmptyOrFlagged) { + return other.withFlags(isNullable: includeNull); } SubclassResult result = domain._closedWorld.classHierarchy @@ -428,17 +449,17 @@ class FlatTypeMask implements TypeMask { } else if (result.classes.length == 1) { ClassEntity cls = result.classes.first; return includeNull - ? new TypeMask.subclass(cls, domain._closedWorld) - : new TypeMask.nonNullSubclass(cls, domain._closedWorld); + ? TypeMask.subclass(cls, domain._closedWorld) + : TypeMask.nonNullSubclass(cls, domain._closedWorld); } List masks = List.from(result.classes.map( (ClassEntity cls) => TypeMask.nonNullSubclass(cls, domain._closedWorld))); if (masks.length > UnionTypeMask.MAX_UNION_LENGTH) { - return UnionTypeMask.flatten(masks, includeNull, domain); + return UnionTypeMask.flatten(masks, domain, includeNull: includeNull); } - return new UnionTypeMask._internal(masks, includeNull); + return UnionTypeMask._internal(masks, isNullable: includeNull); } } @@ -448,7 +469,7 @@ class FlatTypeMask implements TypeMask { FlatTypeMask flatOther = other; if (isNullable && flatOther.isNullable) return false; - if (isEmptyOrNull || flatOther.isEmptyOrNull) return true; + if (isEmptyOrFlagged || flatOther.isEmptyOrFlagged) return true; if (base == flatOther.base) return false; if (isExact && flatOther.isExact) return true; @@ -495,14 +516,14 @@ class FlatTypeMask implements TypeMask { // are nullable, will the result be nullable too. // The result will be normalized, as the two inputs are normalized, too. int combined = (flags < other.flags) - ? flags & ((other.flags & 1) | ~1) - : other.flags & ((flags & 1) | ~1); + ? flags & (other._mask | ~_ALL_MASK) + : other.flags & (_mask | ~_ALL_MASK); if (flags == combined) { return this; } else if (other.flags == combined) { return other; } else { - return new FlatTypeMask.normalized(base, combined, domain); + return FlatTypeMask.normalized(base, combined, domain); } } @@ -517,28 +538,26 @@ class FlatTypeMask implements TypeMask { // masks are nullable, will the result be nullable too. // The result is guaranteed to be normalized, as the other type // was normalized. - int combined = other.flags & ((flags & 1) | ~1); + int combined = other.flags & (_mask | ~_ALL_MASK); if (other.flags == combined) { return other; } else { - return new FlatTypeMask.normalized(other.base, combined, domain); + return FlatTypeMask.normalized(other.base, combined, domain); } } TypeMask intersectionEmpty(FlatTypeMask other) { - return isNullable && other.isNullable - ? new TypeMask.empty() - : new TypeMask.nonNullEmpty(); + bool isNullable = this.isNullable && other.isNullable; + return isNullable ? TypeMask.empty() : TypeMask.nonNullEmpty(); } @override bool canHit(MemberEntity element, Name name, JClosedWorld closedWorld) { CommonElements commonElements = closedWorld.commonElements; assert(element.name == name.text); - if (isEmpty) return false; - if (isNull) { - return closedWorld.hasElementIn( - commonElements.jsNullClass, name, element); + if (isEmptyOrFlagged) { + return isNullable && + closedWorld.hasElementIn(commonElements.jsNullClass, name, element); } ClassEntity other = element.enclosingClass; @@ -572,7 +591,7 @@ class FlatTypeMask implements TypeMask { Selector selector, covariant JClosedWorld closedWorld) { // A call on an empty type mask is either dead code, or a call on // `null`. - if (isEmptyOrNull) return false; + if (isEmptyOrFlagged) return false; // A call on an exact mask for an abstract class is dead code. // TODO(johnniwinther): A type mask cannot be abstract. Remove the need // for this noise (currently used for super-calls in inference and mirror @@ -584,7 +603,7 @@ class FlatTypeMask implements TypeMask { @override MemberEntity locateSingleMember(Selector selector, CommonMasks domain) { - if (isEmptyOrNull) return null; + if (isEmptyOrFlagged) return null; JClosedWorld closedWorld = domain._closedWorld; if (closedWorld.includesClosureCallInDomain(selector, this, domain)) return null; @@ -628,13 +647,15 @@ class FlatTypeMask implements TypeMask { @override String toString() { - if (isEmptyOrNull) return isNullable ? '[null]' : '[empty]'; - StringBuffer buffer = new StringBuffer(); - if (isNullable) buffer.write('null|'); - if (isExact) buffer.write('exact='); - if (isSubclass) buffer.write('subclass='); - if (isSubtype) buffer.write('subtype='); - buffer.write(base.name); - return "[$buffer]"; + StringBuffer buffer = StringBuffer('['); + buffer.writeAll([ + if (isEmpty) 'empty', + if (isNullable) 'null', + if (isExact) 'exact=${base.name}', + if (isSubclass) 'subclass=${base.name}', + if (isSubtype) 'subtype=${base.name}', + ], '|'); + buffer.write(']'); + return buffer.toString(); } } diff --git a/pkg/compiler/lib/src/inferrer/typemasks/forwarding_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/forwarding_type_mask.dart index ec29cbe66cb..6918f6dcef1 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/forwarding_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/forwarding_type_mask.dart @@ -4,15 +4,15 @@ part of masks; -/// A type mask that wraps an other one, and delegate all its +/// A type mask that wraps another one, and delegates all its /// implementation methods to it. -abstract class ForwardingTypeMask implements TypeMask { +abstract class ForwardingTypeMask extends TypeMask { TypeMask get forwardTo; - ForwardingTypeMask(); + const ForwardingTypeMask(); @override - bool get isEmptyOrNull => forwardTo.isEmptyOrNull; + bool get isEmptyOrFlagged => forwardTo.isEmptyOrFlagged; @override bool get isEmpty => forwardTo.isEmpty; @override @@ -93,17 +93,25 @@ abstract class ForwardingTypeMask implements TypeMask { } @override - TypeMask union(other, CommonMasks domain) { + TypeMask union(TypeMask other, CommonMasks domain) { if (this == other) { return this; - } else if (equalsDisregardNull(other)) { - return other.isNullable ? other : this; - } else if (other.isEmptyOrNull) { - return other.isNullable ? this.nullable() : this; } - return forwardTo.union(other, domain); + bool isNullable = this.isNullable || other.isNullable; + if (isEmptyOrFlagged) { + return other.withFlags(isNullable: isNullable); + } + if (other.isEmptyOrFlagged) { + return withFlags(isNullable: isNullable); + } + return _unionSpecialCases(other, domain, isNullable: isNullable) ?? + forwardTo.union(other, domain); } + TypeMask _unionSpecialCases(TypeMask other, CommonMasks domain, + {bool isNullable}) => + null; + @override bool isDisjoint(TypeMask other, JClosedWorld closedWorld) { return forwardTo.isDisjoint(other, closedWorld); @@ -111,7 +119,9 @@ abstract class ForwardingTypeMask implements TypeMask { @override TypeMask intersection(TypeMask other, CommonMasks domain) { - return forwardTo.intersection(other, domain); + TypeMask forwardIntersection = forwardTo.intersection(other, domain); + if (forwardIntersection.isEmptyOrFlagged) return forwardIntersection; + return withFlags(isNullable: forwardIntersection.isNullable); } @override @@ -130,28 +140,33 @@ abstract class ForwardingTypeMask implements TypeMask { return forwardTo.locateSingleMember(selector, domain); } - bool equalsDisregardNull(other) { - if (other is! ForwardingTypeMask) return false; - if (forwardTo.isNullable) { - return forwardTo == other.forwardTo.nullable(); - } else { - return forwardTo == other.forwardTo.nonNullable(); - } - } - @override bool operator ==(other) { - return equalsDisregardNull(other) && isNullable == other.isNullable; + if (identical(this, other)) return true; + if (other is! ForwardingTypeMask) return false; + return forwardTo == other.forwardTo; } @override - int get hashCode => throw "Subclass should implement hashCode getter"; + int get hashCode => forwardTo.hashCode; } abstract class AllocationTypeMask extends ForwardingTypeMask { + const AllocationTypeMask(); + // The [ir.Node] where this type mask was created. ir.Node get allocationNode; // The [Entity] where this type mask was created. MemberEntity get allocationElement; + + @override + bool operator ==(other) { + if (identical(this, other)) return true; + if (other is! AllocationTypeMask) return false; + return super == other && allocationNode == other.allocationNode; + } + + @override + int get hashCode => Hashing.objectHash(allocationNode, super.hashCode); } diff --git a/pkg/compiler/lib/src/inferrer/typemasks/map_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/map_type_mask.dart index 556be49c85e..0aa399b0636 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/map_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/map_type_mask.dart @@ -27,20 +27,20 @@ class MapTypeMask extends AllocationTypeMask { // The key type of this map. final TypeMask keyType; - MapTypeMask(this.forwardTo, this.allocationNode, this.allocationElement, + const MapTypeMask(this.forwardTo, this.allocationNode, this.allocationElement, this.keyType, this.valueType); /// Deserializes a [MapTypeMask] object from [source]. factory MapTypeMask.readFromDataSource( DataSource source, CommonMasks domain) { source.begin(tag); - TypeMask forwardTo = new TypeMask.readFromDataSource(source, domain); + TypeMask forwardTo = TypeMask.readFromDataSource(source, domain); ir.TreeNode allocationNode = source.readTreeNodeOrNull(); MemberEntity allocationElement = source.readMemberOrNull(); - TypeMask keyType = new TypeMask.readFromDataSource(source, domain); - TypeMask valueType = new TypeMask.readFromDataSource(source, domain); + TypeMask keyType = TypeMask.readFromDataSource(source, domain); + TypeMask valueType = TypeMask.readFromDataSource(source, domain); source.end(tag); - return new MapTypeMask( + return MapTypeMask( forwardTo, allocationNode, allocationElement, keyType, valueType); } @@ -58,19 +58,13 @@ class MapTypeMask extends AllocationTypeMask { } @override - TypeMask nullable() { - return isNullable - ? this - : new MapTypeMask(forwardTo.nullable(), allocationNode, - allocationElement, keyType, valueType); - } - - @override - TypeMask nonNullable() { - return isNullable - ? new MapTypeMask(forwardTo.nonNullable(), allocationNode, - allocationElement, keyType, valueType) - : this; + MapTypeMask withFlags({bool isNullable}) { + isNullable ??= this.isNullable; + if (isNullable == this.isNullable) { + return this; + } + return MapTypeMask(forwardTo.withFlags(isNullable: isNullable), + allocationNode, allocationElement, keyType, valueType); } @override @@ -81,30 +75,10 @@ class MapTypeMask extends AllocationTypeMask { bool get isExact => true; @override - bool equalsDisregardNull(other) { - if (other is! MapTypeMask) return false; - return super.equalsDisregardNull(other) && - allocationNode == other.allocationNode && - keyType == other.keyType && - valueType == other.valueType; - } - - @override - TypeMask intersection(TypeMask other, CommonMasks domain) { - TypeMask forwardIntersection = forwardTo.intersection(other, domain); - if (forwardIntersection.isEmptyOrNull) return forwardIntersection; - return forwardIntersection.isNullable ? nullable() : nonNullable(); - } - - @override - TypeMask union(dynamic other, CommonMasks domain) { - if (this == other) { - return this; - } else if (equalsDisregardNull(other)) { - return other.isNullable ? other : this; - } else if (other.isEmptyOrNull) { - return other.isNullable ? this.nullable() : this; - } else if (other.isMap && + TypeMask _unionSpecialCases(TypeMask other, CommonMasks domain, + {bool isNullable}) { + assert(isNullable != null); + if (other is MapTypeMask && keyType != null && other.keyType != null && valueType != null && @@ -112,19 +86,19 @@ class MapTypeMask extends AllocationTypeMask { TypeMask newKeyType = keyType.union(other.keyType, domain); TypeMask newValueType = valueType.union(other.valueType, domain); TypeMask newForwardTo = forwardTo.union(other.forwardTo, domain); - return new MapTypeMask( - newForwardTo, null, null, newKeyType, newValueType); - } else if (other.isDictionary) { + return MapTypeMask(newForwardTo, null, null, newKeyType, newValueType); + } + if (other is DictionaryTypeMask) { // TODO(johnniwinther): Find another way to check this invariant that // doesn't need the compiler. assert(other.keyType == - new TypeMask.nonNullExact( + TypeMask.nonNullExact( domain.commonElements.jsStringClass, domain._closedWorld)); TypeMask newKeyType = keyType.union(other.keyType, domain); TypeMask newValueType = - other.typeMap.values.fold(keyType, (p, n) => p.union(n, domain)); + other._typeMap.values.fold(keyType, (p, n) => p.union(n, domain)); TypeMask newForwardTo = forwardTo.union(other.forwardTo, domain); - MapTypeMask newMapTypeMask = new MapTypeMask( + MapTypeMask newMapTypeMask = MapTypeMask( newForwardTo, allocationNode == other.allocationNode ? allocationNode : null, allocationElement == other.allocationElement @@ -133,19 +107,22 @@ class MapTypeMask extends AllocationTypeMask { newKeyType, newValueType); return newMapTypeMask; - } else { - return forwardTo.union(other, domain); } + return null; } @override - bool operator ==(other) => super == other; + bool operator ==(other) { + if (identical(this, other)) return true; + if (other is! MapTypeMask) return false; + return super == other && + keyType == other.keyType && + valueType == other.valueType; + } @override - int get hashCode { - return computeHashCode( - allocationNode, isNullable, keyType, valueType, forwardTo); - } + int get hashCode => Hashing.objectHash( + valueType, Hashing.objectHash(keyType, super.hashCode)); @override String toString() { diff --git a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart index 2ac07ca5bd3..46d74d8e155 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart @@ -75,10 +75,10 @@ class CommonMasks implements AbstractValueDomain { TypeMask _unmodifiableArrayType; TypeMask _interceptorType; - /// Cache of [FlatTypeMask]s grouped by the 8 possible values of the + /// Cache of [FlatTypeMask]s grouped by the possible values of the /// `FlatTypeMask.flags` property. - final List> _canonicalizedTypeMasks = - new List>.filled(8, null); + final List> _canonicalizedTypeMasks = List.filled( + _FlatTypeMaskKind.values.length << FlatTypeMask._USED_INDICES, null); /// Return the cached mask for [base] with the given flags, or /// calls [createMask] to create the mask and cache it. @@ -89,126 +89,121 @@ class CommonMasks implements AbstractValueDomain { } @override - TypeMask get dynamicType => _dynamicType ??= new TypeMask.subclass( - _closedWorld.commonElements.objectClass, _closedWorld); + TypeMask get dynamicType => _dynamicType ??= + TypeMask.subclass(_closedWorld.commonElements.objectClass, _closedWorld); @override - TypeMask get nonNullType => _nonNullType ??= new TypeMask.nonNullSubclass( + TypeMask get nonNullType => _nonNullType ??= TypeMask.nonNullSubclass( _closedWorld.commonElements.objectClass, _closedWorld); @override TypeMask get intType => _intType ??= - new TypeMask.nonNullSubclass(commonElements.jsIntClass, _closedWorld); + TypeMask.nonNullSubclass(commonElements.jsIntClass, _closedWorld); @override TypeMask get uint32Type => _uint32Type ??= - new TypeMask.nonNullSubclass(commonElements.jsUInt32Class, _closedWorld); + TypeMask.nonNullSubclass(commonElements.jsUInt32Class, _closedWorld); @override TypeMask get uint31Type => _uint31Type ??= - new TypeMask.nonNullExact(commonElements.jsUInt31Class, _closedWorld); + TypeMask.nonNullExact(commonElements.jsUInt31Class, _closedWorld); @override - TypeMask get positiveIntType => - _positiveIntType ??= new TypeMask.nonNullSubclass( - commonElements.jsPositiveIntClass, _closedWorld); + TypeMask get positiveIntType => _positiveIntType ??= + TypeMask.nonNullSubclass(commonElements.jsPositiveIntClass, _closedWorld); @override TypeMask get numNotIntType => _numNotIntType ??= - new TypeMask.nonNullExact(commonElements.jsNumNotIntClass, _closedWorld); + TypeMask.nonNullExact(commonElements.jsNumNotIntClass, _closedWorld); @override TypeMask get numType => _numType ??= - new TypeMask.nonNullSubclass(commonElements.jsNumberClass, _closedWorld); + TypeMask.nonNullSubclass(commonElements.jsNumberClass, _closedWorld); @override TypeMask get boolType => _boolType ??= - new TypeMask.nonNullExact(commonElements.jsBoolClass, _closedWorld); + TypeMask.nonNullExact(commonElements.jsBoolClass, _closedWorld); @override TypeMask get functionType => _functionType ??= - new TypeMask.nonNullSubtype(commonElements.functionClass, _closedWorld); + TypeMask.nonNullSubtype(commonElements.functionClass, _closedWorld); @override TypeMask get listType => _listType ??= - new TypeMask.nonNullSubtype(commonElements.jsArrayClass, _closedWorld); + TypeMask.nonNullSubtype(commonElements.jsArrayClass, _closedWorld); @override - TypeMask get constListType => _constListType ??= new TypeMask.nonNullExact( + TypeMask get constListType => _constListType ??= TypeMask.nonNullExact( commonElements.jsUnmodifiableArrayClass, _closedWorld); @override TypeMask get fixedListType => _fixedListType ??= - new TypeMask.nonNullExact(commonElements.jsFixedArrayClass, _closedWorld); + TypeMask.nonNullExact(commonElements.jsFixedArrayClass, _closedWorld); @override - TypeMask get growableListType => - _growableListType ??= new TypeMask.nonNullExact( - commonElements.jsExtendableArrayClass, _closedWorld); + TypeMask get growableListType => _growableListType ??= TypeMask.nonNullExact( + commonElements.jsExtendableArrayClass, _closedWorld); @override TypeMask get setType => _setType ??= - new TypeMask.nonNullSubtype(commonElements.setLiteralClass, _closedWorld); + TypeMask.nonNullSubtype(commonElements.setLiteralClass, _closedWorld); @override - TypeMask get constSetType => _constSetType ??= new TypeMask.nonNullSubtype( + TypeMask get constSetType => _constSetType ??= TypeMask.nonNullSubtype( commonElements.constSetLiteralClass, _closedWorld); @override TypeMask get mapType => _mapType ??= - new TypeMask.nonNullSubtype(commonElements.mapLiteralClass, _closedWorld); + TypeMask.nonNullSubtype(commonElements.mapLiteralClass, _closedWorld); @override - TypeMask get constMapType => _constMapType ??= new TypeMask.nonNullSubtype( + TypeMask get constMapType => _constMapType ??= TypeMask.nonNullSubtype( commonElements.constMapLiteralClass, _closedWorld); @override TypeMask get stringType => _stringType ??= - new TypeMask.nonNullExact(commonElements.jsStringClass, _closedWorld); + TypeMask.nonNullExact(commonElements.jsStringClass, _closedWorld); @override TypeMask get typeType => _typeType ??= - new TypeMask.nonNullExact(commonElements.typeLiteralClass, _closedWorld); + TypeMask.nonNullExact(commonElements.typeLiteralClass, _closedWorld); @override TypeMask get syncStarIterableType => _syncStarIterableType ??= - new TypeMask.nonNullExact(commonElements.syncStarIterable, _closedWorld); + TypeMask.nonNullExact(commonElements.syncStarIterable, _closedWorld); @override - TypeMask get asyncFutureType => - _asyncFutureType ??= new TypeMask.nonNullExact( - commonElements.futureImplementation, _closedWorld); + TypeMask get asyncFutureType => _asyncFutureType ??= + TypeMask.nonNullExact(commonElements.futureImplementation, _closedWorld); @override TypeMask get asyncStarStreamType => _asyncStarStreamType ??= - new TypeMask.nonNullExact(commonElements.controllerStream, _closedWorld); + TypeMask.nonNullExact(commonElements.controllerStream, _closedWorld); // TODO(johnniwinther): Assert that the null type has been resolved. @override - TypeMask get nullType => _nullType ??= const TypeMask.empty(); + TypeMask get nullType => _nullType ??= TypeMask.empty(); @override - TypeMask get emptyType => const TypeMask.nonNullEmpty(); + TypeMask get emptyType => TypeMask.nonNullEmpty(); - TypeMask get indexablePrimitiveType => - _indexablePrimitiveType ??= new TypeMask.nonNullSubtype( - commonElements.jsIndexableClass, _closedWorld); + TypeMask get indexablePrimitiveType => _indexablePrimitiveType ??= + TypeMask.nonNullSubtype(commonElements.jsIndexableClass, _closedWorld); TypeMask get readableArrayType => _readableArrayType ??= - new TypeMask.nonNullSubclass(commonElements.jsArrayClass, _closedWorld); + TypeMask.nonNullSubclass(commonElements.jsArrayClass, _closedWorld); @override TypeMask get mutableArrayType => - _mutableArrayType ??= new TypeMask.nonNullSubclass( + _mutableArrayType ??= TypeMask.nonNullSubclass( commonElements.jsMutableArrayClass, _closedWorld); TypeMask get unmodifiableArrayType => - _unmodifiableArrayType ??= new TypeMask.nonNullExact( + _unmodifiableArrayType ??= TypeMask.nonNullExact( commonElements.jsUnmodifiableArrayClass, _closedWorld); - TypeMask get interceptorType => - _interceptorType ??= new TypeMask.nonNullSubclass( - commonElements.jsInterceptorClass, _closedWorld); + TypeMask get interceptorType => _interceptorType ??= + TypeMask.nonNullSubclass(commonElements.jsInterceptorClass, _closedWorld); @override AbstractBool isTypedArray(TypeMask mask) { @@ -232,37 +227,37 @@ class CommonMasks implements AbstractValueDomain { ClassEntity typedDataClass = _closedWorld.commonElements.typedDataClass; return AbstractBool.maybeOrFalse(typedDataClass != null && _closedWorld.classHierarchy.isInstantiated(typedDataClass) && - intersects(mask, new TypeMask.subtype(typedDataClass, _closedWorld)) && + intersects(mask, TypeMask.subtype(typedDataClass, _closedWorld)) && intersects( mask, - new TypeMask.subtype( + TypeMask.subtype( _closedWorld.commonElements.jsIndexingBehaviorInterface, _closedWorld))); } @override TypeMask createNonNullExact(ClassEntity cls) { - return new TypeMask.nonNullExact(cls, _closedWorld); + return TypeMask.nonNullExact(cls, _closedWorld); } @override TypeMask createNullableExact(ClassEntity cls) { - return new TypeMask.exact(cls, _closedWorld); + return TypeMask.exact(cls, _closedWorld); } @override TypeMask createNonNullSubclass(ClassEntity cls) { - return new TypeMask.nonNullSubclass(cls, _closedWorld); + return TypeMask.nonNullSubclass(cls, _closedWorld); } @override TypeMask createNonNullSubtype(ClassEntity cls) { - return new TypeMask.nonNullSubtype(cls, _closedWorld); + return TypeMask.nonNullSubtype(cls, _closedWorld); } @override TypeMask createNullableSubtype(ClassEntity cls) { - return new TypeMask.subtype(cls, _closedWorld); + return TypeMask.subtype(cls, _closedWorld); } @override @@ -446,10 +441,6 @@ class CommonMasks implements AbstractValueDomain { AbstractBool isExact(TypeMask value) => AbstractBool.trueOrMaybe(value.isExact && !value.isNullable); - @override - AbstractBool isExactOrNull(TypeMask value) => - AbstractBool.trueOrMaybe(value.isExact || _isNull(value)); - @override ClassEntity getExactClass(TypeMask mask) { return mask.singleClass(_closedWorld); @@ -470,7 +461,7 @@ class CommonMasks implements AbstractValueDomain { @override AbstractValue createPrimitiveValue( covariant TypeMask originalValue, PrimitiveConstantValue value) { - return new ValueTypeMask(originalValue, value); + return ValueTypeMask(originalValue, value); } @override @@ -484,15 +475,13 @@ class CommonMasks implements AbstractValueDomain { } } - bool _isNull(TypeMask value) => value.isNull; - @override AbstractBool isPrimitive(TypeMask value) { return AbstractBool.maybeOrFalse(_canBePrimitiveNumber(value) || _canBePrimitiveArray(value) || _canBePrimitiveBoolean(value) || _canBePrimitiveString(value) || - _isNull(value)); + value.isNull); } @override @@ -518,10 +507,6 @@ class CommonMasks implements AbstractValueDomain { return _containsType(value, commonElements.jsBoolClass); } - @override - AbstractBool isPrimitiveArray(TypeMask value) => - AbstractBool.maybeOrFalse(_canBePrimitiveArray(value)); - bool _canBePrimitiveArray(TypeMask value) { return _containsType(value, commonElements.jsArrayClass) || _containsType(value, commonElements.jsFixedArrayClass) || @@ -672,7 +657,7 @@ class CommonMasks implements AbstractValueDomain { return _isIndexablePrimitive(value) || _isNumberOrNull(value) || _isBooleanOrNull(value) || - _isNull(value); + value.isNull; } @override @@ -731,13 +716,13 @@ class CommonMasks implements AbstractValueDomain { MemberEntity allocationElement, AbstractValue elementType, int length) { - return new ContainerTypeMask( + return ContainerTypeMask( forwardTo, allocationNode, allocationElement, elementType, length); } @override AbstractValue unionOfMany(Iterable values) { - TypeMask result = const TypeMask.nonNullEmpty(); + TypeMask result = TypeMask.nonNullEmpty(); for (TypeMask value in values) { result = result.union(value, this); } @@ -748,18 +733,18 @@ class CommonMasks implements AbstractValueDomain { AbstractValue computeReceiver(Iterable members) { assert(_closedWorld.classHierarchy .hasAnyStrictSubclass(_closedWorld.commonElements.objectClass)); - return new TypeMask.unionOf( + return TypeMask.unionOf( members.expand((MemberEntity element) { ClassEntity cls = element.enclosingClass; return [cls]..addAll(_closedWorld.mixinUsesOf(cls)); }).map((cls) { if (_closedWorld.commonElements.jsNullClass == cls) { - return const TypeMask.empty(); + return TypeMask.empty(); } else if (_closedWorld.classHierarchy.isInstantiated(cls)) { - return new TypeMask.nonNullSubclass(cls, _closedWorld); + return TypeMask.nonNullSubclass(cls, _closedWorld); } else { // TODO(johnniwinther): Avoid the need for this case. - return const TypeMask.empty(); + return TypeMask.empty(); } }), this); @@ -880,7 +865,7 @@ class CommonMasks implements AbstractValueDomain { @override AbstractValue createMapValue(AbstractValue forwardTo, Object allocationNode, MemberEntity allocationElement, AbstractValue key, AbstractValue value) { - return new MapTypeMask( + return MapTypeMask( forwardTo, allocationNode, allocationElement, key, value); } @@ -892,14 +877,14 @@ class CommonMasks implements AbstractValueDomain { AbstractValue key, AbstractValue value, Map mappings) { - return new DictionaryTypeMask( - forwardTo, allocationNode, allocationElement, key, value, mappings); + return DictionaryTypeMask(forwardTo, allocationNode, allocationElement, key, + value, Map.from(mappings)); } @override AbstractValue createSetValue(AbstractValue forwardTo, Object allocationNode, MemberEntity allocationElement, AbstractValue elementType) { - return new SetTypeMask( + return SetTypeMask( forwardTo, allocationNode, allocationElement, elementType); } @@ -963,8 +948,8 @@ class CommonMasks implements AbstractValueDomain { @override TypeMask readAbstractValueFromDataSource(DataSource source) { - return source.readCached( - () => new TypeMask.readFromDataSource(source, this)); + return source + .readCached(() => TypeMask.readFromDataSource(source, this)); } @override @@ -985,7 +970,10 @@ String formatType(DartTypes dartTypes, TypeMask type) { // can be really long and mess up the layout. // Capitalize Null to emphasize that it's the null type mask and not // a null value we accidentally printed out. - if (type.isEmptyOrNull) return type.isNullable ? 'Null' : 'Empty'; + if (type.isEmpty) return 'Empty'; + if (type.isEmptyOrFlagged) { + return [if (type.isNullable) 'Null'].join(''); + } String nullFlag = type.isNullable ? '?' : ''; String subFlag = type.isExact ? '' diff --git a/pkg/compiler/lib/src/inferrer/typemasks/set_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/set_type_mask.dart index cfe96da0553..e8538664bcc 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/set_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/set_type_mask.dart @@ -24,19 +24,19 @@ class SetTypeMask extends AllocationTypeMask { // The element type of this set. final TypeMask elementType; - SetTypeMask(this.forwardTo, this.allocationNode, this.allocationElement, + const SetTypeMask(this.forwardTo, this.allocationNode, this.allocationElement, this.elementType); /// Deserializes a [SetTypeMask] object from [source]. factory SetTypeMask.readFromDataSource( DataSource source, CommonMasks domain) { source.begin(tag); - TypeMask forwardTo = new TypeMask.readFromDataSource(source, domain); + TypeMask forwardTo = TypeMask.readFromDataSource(source, domain); ir.TreeNode allocationNode = source.readTreeNodeOrNull(); MemberEntity allocationElement = source.readMemberOrNull(); - TypeMask elementType = new TypeMask.readFromDataSource(source, domain); + TypeMask elementType = TypeMask.readFromDataSource(source, domain); source.end(tag); - return new SetTypeMask( + return SetTypeMask( forwardTo, allocationNode, allocationElement, elementType); } @@ -53,16 +53,14 @@ class SetTypeMask extends AllocationTypeMask { } @override - TypeMask nullable() => isNullable - ? this - : new SetTypeMask( - forwardTo.nullable(), allocationNode, allocationElement, elementType); - - @override - TypeMask nonNullable() => isNullable - ? new SetTypeMask(forwardTo.nonNullable(), allocationNode, - allocationElement, elementType) - : this; + SetTypeMask withFlags({bool isNullable}) { + isNullable ??= this.isNullable; + if (isNullable == this.isNullable) { + return this; + } + return SetTypeMask(forwardTo.withFlags(isNullable: isNullable), + allocationNode, allocationElement, elementType); + } @override bool get isSet => true; @@ -71,45 +69,28 @@ class SetTypeMask extends AllocationTypeMask { bool get isExact => true; @override - bool equalsDisregardNull(other) { - if (other is! SetTypeMask) return false; - return super.equalsDisregardNull(other) && - allocationNode == other.allocationNode && - elementType == other.elementType; - } - - @override - TypeMask intersection(TypeMask other, CommonMasks domain) { - TypeMask forwardIntersection = forwardTo.intersection(other, domain); - if (forwardIntersection.isEmptyOrNull) return forwardIntersection; - return forwardIntersection.isNullable ? nullable() : nonNullable(); - } - - @override - TypeMask union(dynamic other, CommonMasks domain) { - if (this == other) { - return this; - } else if (equalsDisregardNull(other)) { - return other.isNullable ? other : this; - } else if (other.isEmptyOrNull) { - return other.isNullable ? this.nullable() : this; - } else if (other.isSet && + TypeMask _unionSpecialCases(TypeMask other, CommonMasks domain, + {bool isNullable}) { + assert(isNullable != null); + if (other is SetTypeMask && elementType != null && other.elementType != null) { TypeMask newElementType = elementType.union(other.elementType, domain); TypeMask newForwardTo = forwardTo.union(other.forwardTo, domain); - return new SetTypeMask(newForwardTo, null, null, newElementType); - } else { - return forwardTo.union(other, domain); + return SetTypeMask(newForwardTo, null, null, newElementType); } + return null; } @override - bool operator ==(other) => super == other; + bool operator ==(other) { + if (identical(this, other)) return true; + if (other is! SetTypeMask) return false; + return super == other && elementType == other.elementType; + } @override - int get hashCode => - computeHashCode(allocationNode, isNullable, elementType, forwardTo); + int get hashCode => Hashing.objectHash(elementType, super.hashCode); @override String toString() => 'Set($forwardTo, element: $elementType)'; diff --git a/pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart index 7fd62a8c3cc..b929cc316bb 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart @@ -25,7 +25,7 @@ class IncreasingTypeMaskSet extends UniverseSelectorConstraints { bool needsNoSuchMethodHandling(Selector selector, JClosedWorld world) { if (isAll) { TypeMask mask = - new TypeMask.subclass(world.commonElements.objectClass, world); + TypeMask.subclass(world.commonElements.objectClass, world); return mask.needsNoSuchMethodHandling(selector, world); } for (TypeMask mask in _masks) { @@ -44,7 +44,7 @@ class IncreasingTypeMaskSet extends UniverseSelectorConstraints { _masks = null; return true; } - _masks ??= new Set(); + _masks ??= {}; return _masks.add(mask); } @@ -65,12 +65,12 @@ class TypeMaskStrategy implements AbstractValueStrategy { @override AbstractValueDomain createDomain(JClosedWorld closedWorld) { - return new CommonMasks(closedWorld); + return CommonMasks(closedWorld); } @override SelectorConstraintsStrategy createSelectorStrategy() { - return new TypeMaskSelectorStrategy(); + return TypeMaskSelectorStrategy(); } } @@ -80,8 +80,7 @@ class TypeMaskSelectorStrategy implements SelectorConstraintsStrategy { @override UniverseSelectorConstraints createSelectorConstraints( Selector selector, Object initialConstraint) { - return new IncreasingTypeMaskSet() - ..addReceiverConstraint(initialConstraint); + return IncreasingTypeMaskSet()..addReceiverConstraint(initialConstraint); } @override @@ -109,13 +108,9 @@ enum TypeMaskKind { /// operations on it are not guaranteed to be precise and they may /// yield conservative answers that contain too many classes. abstract class TypeMask implements AbstractValue { - factory TypeMask( - ClassEntity base, int kind, bool isNullable, CommonMasks domain) { - return new FlatTypeMask.normalized( - base, (kind << 1) | (isNullable ? 1 : 0), domain); - } + const TypeMask(); - const factory TypeMask.empty() = FlatTypeMask.empty; + factory TypeMask.empty() = FlatTypeMask.empty; factory TypeMask.exact(ClassEntity base, JClosedWorld closedWorld) { assert( @@ -124,13 +119,13 @@ abstract class TypeMask implements AbstractValue { base ?? CURRENT_ELEMENT_SPANNABLE, "Cannot create exact type mask for uninstantiated " "class $base.\n${closedWorld.classHierarchy.dump(base)}")); - return new FlatTypeMask.exact(base, closedWorld); + return FlatTypeMask.exact(base, closedWorld); } factory TypeMask.exactOrEmpty(ClassEntity base, JClosedWorld closedWorld) { if (closedWorld.classHierarchy.isInstantiated(base)) - return new FlatTypeMask.exact(base, closedWorld); - return const TypeMask.empty(); + return FlatTypeMask.exact(base, closedWorld); + return TypeMask.empty(); } factory TypeMask.subclass(ClassEntity base, JClosedWorld closedWorld) { @@ -142,30 +137,30 @@ abstract class TypeMask implements AbstractValue { "class $base.\n${closedWorld.classHierarchy.dump(base)}")); ClassEntity topmost = closedWorld.getLubOfInstantiatedSubclasses(base); if (topmost == null) { - return new TypeMask.empty(); + return TypeMask.empty(); } else if (closedWorld.classHierarchy.hasAnyStrictSubclass(topmost)) { - return new FlatTypeMask.subclass(topmost, closedWorld); + return FlatTypeMask.subclass(topmost, closedWorld); } else { - return new TypeMask.exact(topmost, closedWorld); + return TypeMask.exact(topmost, closedWorld); } } factory TypeMask.subtype(ClassEntity base, JClosedWorld closedWorld) { ClassEntity topmost = closedWorld.getLubOfInstantiatedSubtypes(base); if (topmost == null) { - return new TypeMask.empty(); + return TypeMask.empty(); } if (closedWorld.classHierarchy.hasOnlySubclasses(topmost)) { - return new TypeMask.subclass(topmost, closedWorld); + return TypeMask.subclass(topmost, closedWorld); } if (closedWorld.classHierarchy.hasAnyStrictSubtype(topmost)) { - return new FlatTypeMask.subtype(topmost, closedWorld); + return FlatTypeMask.subtype(topmost, closedWorld); } else { - return new TypeMask.exact(topmost, closedWorld); + return TypeMask.exact(topmost, closedWorld); } } - const factory TypeMask.nonNullEmpty() = FlatTypeMask.nonNullEmpty; + factory TypeMask.nonNullEmpty() = FlatTypeMask.nonNullEmpty; factory TypeMask.nonNullExact(ClassEntity base, JClosedWorld closedWorld) { assert( @@ -174,15 +169,15 @@ abstract class TypeMask implements AbstractValue { base ?? CURRENT_ELEMENT_SPANNABLE, "Cannot create exact type mask for uninstantiated " "class $base.\n${closedWorld.classHierarchy.dump(base)}")); - return new FlatTypeMask.nonNullExact(base, closedWorld); + return FlatTypeMask.nonNullExact(base, closedWorld); } factory TypeMask.nonNullExactOrEmpty( ClassEntity base, JClosedWorld closedWorld) { if (closedWorld.classHierarchy.isInstantiated(base)) { - return new FlatTypeMask.nonNullExact(base, closedWorld); + return FlatTypeMask.nonNullExact(base, closedWorld); } - return const TypeMask.nonNullEmpty(); + return TypeMask.nonNullEmpty(); } factory TypeMask.nonNullSubclass(ClassEntity base, JClosedWorld closedWorld) { @@ -194,26 +189,26 @@ abstract class TypeMask implements AbstractValue { "class $base.\n${closedWorld.classHierarchy.dump(base)}")); ClassEntity topmost = closedWorld.getLubOfInstantiatedSubclasses(base); if (topmost == null) { - return new TypeMask.nonNullEmpty(); + return TypeMask.nonNullEmpty(); } else if (closedWorld.classHierarchy.hasAnyStrictSubclass(topmost)) { - return new FlatTypeMask.nonNullSubclass(topmost, closedWorld); + return FlatTypeMask.nonNullSubclass(topmost, closedWorld); } else { - return new TypeMask.nonNullExact(topmost, closedWorld); + return TypeMask.nonNullExact(topmost, closedWorld); } } factory TypeMask.nonNullSubtype(ClassEntity base, JClosedWorld closedWorld) { ClassEntity topmost = closedWorld.getLubOfInstantiatedSubtypes(base); if (topmost == null) { - return new TypeMask.nonNullEmpty(); + return TypeMask.nonNullEmpty(); } if (closedWorld.classHierarchy.hasOnlySubclasses(topmost)) { - return new TypeMask.nonNullSubclass(topmost, closedWorld); + return TypeMask.nonNullSubclass(topmost, closedWorld); } if (closedWorld.classHierarchy.hasAnyStrictSubtype(topmost)) { - return new FlatTypeMask.nonNullSubtype(topmost, closedWorld); + return FlatTypeMask.nonNullSubtype(topmost, closedWorld); } else { - return new TypeMask.nonNullExact(topmost, closedWorld); + return TypeMask.nonNullExact(topmost, closedWorld); } } @@ -226,21 +221,21 @@ abstract class TypeMask implements AbstractValue { TypeMaskKind kind = source.readEnum(TypeMaskKind.values); switch (kind) { case TypeMaskKind.flat: - return new FlatTypeMask.readFromDataSource(source, domain); + return FlatTypeMask.readFromDataSource(source, domain); case TypeMaskKind.union: - return new UnionTypeMask.readFromDataSource(source, domain); + return UnionTypeMask.readFromDataSource(source, domain); case TypeMaskKind.container: - return new ContainerTypeMask.readFromDataSource(source, domain); + return ContainerTypeMask.readFromDataSource(source, domain); case TypeMaskKind.set: - return new SetTypeMask.readFromDataSource(source, domain); + return SetTypeMask.readFromDataSource(source, domain); case TypeMaskKind.map: - return new MapTypeMask.readFromDataSource(source, domain); + return MapTypeMask.readFromDataSource(source, domain); case TypeMaskKind.dictionary: - return new DictionaryTypeMask.readFromDataSource(source, domain); + return DictionaryTypeMask.readFromDataSource(source, domain); case TypeMaskKind.value: - return new ValueTypeMask.readFromDataSource(source, domain); + return ValueTypeMask.readFromDataSource(source, domain); } - throw new UnsupportedError("Unexpected TypeMaskKind $kind."); + throw UnsupportedError("Unexpected TypeMaskKind $kind."); } /// Serializes this [TypeMask] to [sink]. @@ -271,7 +266,7 @@ abstract class TypeMask implements AbstractValue { TypeMask mask, JClosedWorld closedWorld) { mask = nonForwardingMask(mask); if (mask is FlatTypeMask) { - if (mask.isEmptyOrNull) return null; + if (mask.isEmptyOrFlagged) return null; if (mask.base == closedWorld.commonElements.nullClass) { return 'The class ${mask.base} is not canonicalized.'; } @@ -308,10 +303,14 @@ abstract class TypeMask implements AbstractValue { } /// Returns a nullable variant of [this] type mask. - TypeMask nullable(); + TypeMask nullable() => withFlags(isNullable: true); /// Returns a non-nullable variant of [this] type mask. - TypeMask nonNullable(); + TypeMask nonNullable() => withFlags(isNullable: false); + + TypeMask withoutFlags() => withFlags(isNullable: false); + + TypeMask withFlags({bool isNullable}); /// Whether nothing matches this mask, not even null. bool get isEmpty; @@ -322,8 +321,8 @@ abstract class TypeMask implements AbstractValue { /// Whether the only possible value in this mask is Null. bool get isNull; - /// Whether [isEmpty] or [isNull] is true. - bool get isEmptyOrNull; + /// Whether [this] mask is empty or only represents values tracked by flags. + bool get isEmptyOrFlagged; /// Whether this mask only includes instances of an exact class, and none of /// it's subclasses or subtypes. diff --git a/pkg/compiler/lib/src/inferrer/typemasks/union_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/union_type_mask.dart index dddd0edcdbd..b1f55a0c9b1 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/union_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/union_type_mask.dart @@ -4,7 +4,7 @@ part of masks; -class UnionTypeMask implements TypeMask { +class UnionTypeMask extends TypeMask { /// Tag used for identifying serialized [UnionTypeMask] objects in a /// debugging data stream. static const String tag = 'union-type-mask'; @@ -22,21 +22,21 @@ class UnionTypeMask implements TypeMask { @override final bool isNullable; - UnionTypeMask._internal(this.disjointMasks, this.isNullable) { - assert(disjointMasks.length > 1); - assert(disjointMasks.every((TypeMask mask) => !mask.isUnion)); - assert(disjointMasks.every((TypeMask mask) => !mask.isNullable)); - } + UnionTypeMask._internal(this.disjointMasks, {this.isNullable}) + : assert(isNullable != null), + assert(disjointMasks.length > 1), + assert(disjointMasks.every((TypeMask mask) => !mask.isUnion)), + assert(disjointMasks.every((TypeMask mask) => !mask.isNullable)); /// Deserializes a [UnionTypeMask] object from [source]. factory UnionTypeMask.readFromDataSource( DataSource source, CommonMasks domain) { source.begin(tag); List disjointMasks = - source.readList(() => new TypeMask.readFromDataSource(source, domain)); + source.readList(() => TypeMask.readFromDataSource(source, domain)); bool isNullable = source.readBool(); source.end(tag); - return new UnionTypeMask._internal(disjointMasks, isNullable); + return UnionTypeMask._internal(disjointMasks, isNullable: isNullable); } /// Serializes this [UnionTypeMask] to [sink]. @@ -59,11 +59,12 @@ class UnionTypeMask implements TypeMask { if (disjoint.isEmpty) return isNullable ? TypeMask.empty() : TypeMask.nonNullEmpty(); if (disjoint.length > MAX_UNION_LENGTH) { - return flatten(disjoint, isNullable, domain); + return flatten(disjoint, domain, includeNull: isNullable); } if (disjoint.length == 1) - return isNullable ? disjoint[0].nullable() : disjoint[0]; - UnionTypeMask union = new UnionTypeMask._internal(disjoint, isNullable); + return disjoint[0].withFlags(isNullable: isNullable); + UnionTypeMask union = + UnionTypeMask._internal(disjoint, isNullable: isNullable); assert(TypeMask.assertIsNormalized(union, domain._closedWorld)); return union; } @@ -73,7 +74,7 @@ class UnionTypeMask implements TypeMask { // TODO(johnniwinther): Impose an order on the mask to ensure subclass masks // are preferred to subtype masks. for (TypeMask mask in masks) { - mask = TypeMask.nonForwardingMask(mask).nonNullable(); + mask = TypeMask.nonForwardingMask(mask).withoutFlags(); if (mask.isUnion) { UnionTypeMask union = mask; unionOfHelper(union.disjointMasks, disjoint, domain); @@ -121,8 +122,10 @@ class UnionTypeMask implements TypeMask { } } - static TypeMask flatten( - List masks, bool includeNull, CommonMasks domain) { + static TypeMask flatten(List masks, CommonMasks domain, + {bool includeNull}) { + assert(includeNull != null); + // TODO(johnniwinther): Move this computation to [ClosedWorld] and use the // class set structures. if (masks.isEmpty) throw ArgumentError.value(masks, 'masks'); @@ -136,7 +139,7 @@ class UnionTypeMask implements TypeMask { // Compute the best candidate and its kind. ClassEntity bestElement; - int bestKind; + _FlatTypeMaskKind bestKind; int bestSize; for (ClassEntity candidate in candidates) { bool isInstantiatedStrictSubclass(cls) => @@ -145,13 +148,13 @@ class UnionTypeMask implements TypeMask { domain._closedWorld.classHierarchy.isSubclassOf(cls, candidate); int size; - int kind; + _FlatTypeMaskKind kind; if (useSubclass && masksBases.every(isInstantiatedStrictSubclass)) { // If both [this] and [other] are subclasses of the supertype, // then we prefer to construct a subclass type mask because it // will always be at least as small as the corresponding // subtype type mask. - kind = FlatTypeMask.SUBCLASS; + kind = _FlatTypeMaskKind.subclass; // TODO(sigmund, johnniwinther): computing length here (and below) is // expensive. If we can't prevent `flatten` from being called a lot, it // might be worth caching results. @@ -160,7 +163,7 @@ class UnionTypeMask implements TypeMask { assert(size <= domain._closedWorld.classHierarchy.strictSubtypeCount(candidate)); } else { - kind = FlatTypeMask.SUBTYPE; + kind = _FlatTypeMaskKind.subtype; size = domain._closedWorld.classHierarchy.strictSubtypeCount(candidate); } // Update the best candidate if the new one is better. @@ -170,56 +173,57 @@ class UnionTypeMask implements TypeMask { bestKind = kind; } } - return new TypeMask(bestElement, bestKind, includeNull, domain); + int flags = FlatTypeMask._computeFlags(bestKind, hasNull: includeNull); + return FlatTypeMask.normalized(bestElement, flags, domain); } @override - TypeMask union(dynamic other, CommonMasks domain) { + TypeMask union(TypeMask other, CommonMasks domain) { other = TypeMask.nonForwardingMask(other); + bool isNullable = this.isNullable || other.isNullable; if (other is UnionTypeMask) { - if (_containsNonNullableUnion(other)) { - return other.isNullable ? nullable() : this; + if (_containsDisjointMasks(other)) { + return withFlags(isNullable: isNullable); } - if (other._containsNonNullableUnion(this)) { - return isNullable ? other.nullable() : other; + if (other._containsDisjointMasks(this)) { + return other.withFlags(isNullable: isNullable); } } else { - if (disjointMasks.contains(other.nonNullable())) { - return other.isNullable ? nullable() : this; + if (disjointMasks.contains(other.withoutFlags())) { + return withFlags(isNullable: isNullable); } } - List newList = new List.of(disjointMasks); - if (!other.isUnion) { - newList.add(other); - } else { - assert(other is UnionTypeMask); + List newList = List.of(disjointMasks); + if (other is UnionTypeMask) { newList.addAll(other.disjointMasks); + } else { + newList.add(other); } - TypeMask newMask = new TypeMask.unionOf(newList, domain); - return isNullable || other.isNullable ? newMask.nullable() : newMask; + TypeMask newMask = TypeMask.unionOf(newList, domain); + return newMask.withFlags(isNullable: isNullable); } @override - TypeMask intersection(dynamic other, CommonMasks domain) { + TypeMask intersection(TypeMask other, CommonMasks domain) { other = TypeMask.nonForwardingMask(other); + bool isNullable = this.isNullable && other.isNullable; if (other is UnionTypeMask) { - if (_containsNonNullableUnion(other)) { - return isNullable ? other : other.nonNullable(); + if (_containsDisjointMasks(other)) { + return other.withFlags(isNullable: isNullable); } - if (other._containsNonNullableUnion(this)) { - return other.isNullable ? this : nonNullable(); + if (other._containsDisjointMasks(this)) { + return withFlags(isNullable: isNullable); } } else { - TypeMask otherNonNullable = other.nonNullable(); - if (disjointMasks.contains(otherNonNullable)) { - return isNullable ? other : otherNonNullable; + if (disjointMasks.contains(other.withoutFlags())) { + return other.withFlags(isNullable: isNullable); } } List intersections = []; for (TypeMask current in disjointMasks) { - if (other.isUnion) { + if (other is UnionTypeMask) { if (other.disjointMasks.contains(current)) { intersections.add(current); } else { @@ -232,7 +236,7 @@ class UnionTypeMask implements TypeMask { } } TypeMask newMask = TypeMask.unionOf(intersections, domain); - return isNullable && other.isNullable ? newMask.nullable() : newMask; + return newMask.withFlags(isNullable: isNullable); } @override @@ -245,21 +249,17 @@ class UnionTypeMask implements TypeMask { } @override - TypeMask nullable() { - if (isNullable) return this; - List newList = new List.of(disjointMasks); - return new UnionTypeMask._internal(newList, true); + UnionTypeMask withFlags({bool isNullable}) { + isNullable ??= this.isNullable; + if (isNullable == this.isNullable) { + return this; + } + List newList = List.of(disjointMasks); + return UnionTypeMask._internal(newList, isNullable: isNullable); } @override - TypeMask nonNullable() { - if (!isNullable) return this; - List newList = new List.of(disjointMasks); - return new UnionTypeMask._internal(newList, false); - } - - @override - bool get isEmptyOrNull => false; + bool get isEmptyOrFlagged => false; @override bool get isEmpty => false; @override @@ -292,7 +292,7 @@ class UnionTypeMask implements TypeMask { assert(!other.isUnion); // Likewise, nullness should be covered. assert(isNullable || !other.isNullable); - other = other.nonNullable(); + other = other.withoutFlags(); // Ensure the cheap test fails. assert(!disjointMasks.any((mask) => mask.containsMask(other, closedWorld))); // If we cover object, we should never get here. @@ -342,7 +342,7 @@ class UnionTypeMask implements TypeMask { other = TypeMask.nonForwardingMask(other); if (other.isNullable && !isNullable) return false; if (other.isUnion) return other.isInMask(this, closedWorld); - other = other.nonNullable(); + other = other.withoutFlags(); bool contained = disjointMasks.any((mask) => mask.containsMask(other, closedWorld)); if (PERFORM_EXTRA_CONTAINS_CHECK && @@ -448,20 +448,16 @@ class UnionTypeMask implements TypeMask { return other is UnionTypeMask && other.isNullable == isNullable && other.disjointMasks.length == disjointMasks.length && - _containsNonNullableUnion(other); + _containsDisjointMasks(other); } @override int get hashCode { - int hashCode = isNullable ? 86 : 43; // The order of the masks in [disjointMasks] must not affect the // hashCode. - for (var mask in disjointMasks) { - hashCode = (hashCode ^ mask.hashCode) & 0x3fffffff; - } - return hashCode; + return Hashing.setHash(disjointMasks, isNullable.hashCode); } - bool _containsNonNullableUnion(UnionTypeMask other) => + bool _containsDisjointMasks(UnionTypeMask other) => other.disjointMasks.every((e) => disjointMasks.contains(e)); } diff --git a/pkg/compiler/lib/src/inferrer/typemasks/value_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/value_type_mask.dart index 38486aba6aa..6c301d5b74d 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/value_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/value_type_mask.dart @@ -13,16 +13,16 @@ class ValueTypeMask extends ForwardingTypeMask { final TypeMask forwardTo; final PrimitiveConstantValue value; - ValueTypeMask(this.forwardTo, this.value); + const ValueTypeMask(this.forwardTo, this.value); /// Deserializes a [ValueTypeMask] object from [source]. factory ValueTypeMask.readFromDataSource( DataSource source, CommonMasks domain) { source.begin(tag); - TypeMask forwardTo = new TypeMask.readFromDataSource(source, domain); + TypeMask forwardTo = TypeMask.readFromDataSource(source, domain); ConstantValue constant = source.readConstant(); source.end(tag); - return new ValueTypeMask(forwardTo, constant); + return ValueTypeMask(forwardTo, constant); } /// Serializes this [ValueTypeMask] to [sink]. @@ -36,40 +36,38 @@ class ValueTypeMask extends ForwardingTypeMask { } @override - TypeMask nullable() { - return isNullable ? this : new ValueTypeMask(forwardTo.nullable(), value); - } - - @override - TypeMask nonNullable() { - return isNullable - ? new ValueTypeMask(forwardTo.nonNullable(), value) - : this; + ValueTypeMask withFlags({bool isNullable}) { + isNullable ??= this.isNullable; + if (isNullable == this.isNullable) { + return this; + } + return ValueTypeMask(forwardTo.withFlags(isNullable: isNullable), value); } @override bool get isValue => true; @override - bool equalsDisregardNull(other) { + TypeMask _unionSpecialCases(TypeMask other, CommonMasks domain, + {bool isNullable}) { + assert(isNullable != null); + if (other is ValueTypeMask && + forwardTo.withoutFlags() == other.forwardTo.withoutFlags() && + value == other.value) { + return withFlags(isNullable: isNullable); + } + return null; + } + + @override + bool operator ==(other) { + if (identical(this, other)) return true; if (other is! ValueTypeMask) return false; - return super.equalsDisregardNull(other) && value == other.value; + return super == other && value == other.value; } @override - TypeMask intersection(TypeMask other, CommonMasks domain) { - TypeMask forwardIntersection = forwardTo.intersection(other, domain); - if (forwardIntersection.isEmptyOrNull) return forwardIntersection; - return forwardIntersection.isNullable ? nullable() : nonNullable(); - } - - @override - bool operator ==(other) => super == other; - - @override - int get hashCode { - return computeHashCode(value, isNullable, forwardTo); - } + int get hashCode => Hashing.objectHash(value, super.hashCode); @override String toString() { diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart index 47298dea422..30d7f0c1242 100644 --- a/pkg/compiler/lib/src/ssa/nodes.dart +++ b/pkg/compiler/lib/src/ssa/nodes.dart @@ -1159,9 +1159,6 @@ abstract class HInstruction implements Spannable { AbstractBool isPrimitiveBoolean(AbstractValueDomain domain) => domain.isPrimitiveBoolean(instructionType); - AbstractBool isPrimitiveArray(AbstractValueDomain domain) => - domain.isPrimitiveArray(instructionType); - AbstractBool isIndexablePrimitive(AbstractValueDomain domain) => domain.isIndexablePrimitive(instructionType); diff --git a/pkg/compiler/lib/src/util/util.dart b/pkg/compiler/lib/src/util/util.dart index 6a7894fba93..7c5f50ba262 100644 --- a/pkg/compiler/lib/src/util/util.dart +++ b/pkg/compiler/lib/src/util/util.dart @@ -233,15 +233,6 @@ void writeJsonEscapedCharsOn(String string, buffer) { buffer.write(string); } -int computeHashCode(part1, [part2, part3, part4, part5]) { - return (part1.hashCode ^ - part2.hashCode ^ - part3.hashCode ^ - part4.hashCode ^ - part5.hashCode) & - 0x3fffffff; -} - class Pair { final A a; final B b; diff --git a/pkg/compiler/test/analyses/dart2js_allowed.json b/pkg/compiler/test/analyses/dart2js_allowed.json index 242faa0ca05..d9943f4f3a5 100644 --- a/pkg/compiler/test/analyses/dart2js_allowed.json +++ b/pkg/compiler/test/analyses/dart2js_allowed.json @@ -47,66 +47,10 @@ "Dynamic access of 'memberContext'.": 1, "Dynamic access of 'name'.": 1 }, - "pkg/compiler/lib/src/inferrer/typemasks/container_type_mask.dart": { - "Dynamic access of 'isNullable'.": 2, - "Dynamic access of 'isEmptyOrNull'.": 1, - "Dynamic access of 'isContainer'.": 1, - "Dynamic access of 'elementType'.": 2, - "Dynamic access of 'length'.": 1, - "Dynamic access of 'forwardTo'.": 1, - "Dynamic access of 'allocationNode'.": 1, - "Dynamic access of 'allocationElement'.": 1 - }, - "pkg/compiler/lib/src/inferrer/typemasks/dictionary_type_mask.dart": { - "Dynamic access of 'isNullable'.": 2, - "Dynamic access of 'isEmptyOrNull'.": 1, - "Dynamic access of 'isDictionary'.": 1, - "Dynamic access of 'forwardTo'.": 2, - "Dynamic access of 'keyType'.": 3, - "Dynamic access of 'valueType'.": 3, - "Dynamic access of 'masks::_typeMap'.": 2, - "Dynamic invocation of 'containsKey'.": 1, - "Dynamic invocation of 'nullable'.": 2, - "Dynamic invocation of 'union'.": 1, - "Dynamic invocation of 'forEach'.": 1, - "Dynamic access of 'isMap'.": 1 - }, - "pkg/compiler/lib/src/inferrer/typemasks/forwarding_type_mask.dart": { - "Dynamic access of 'isNullable'.": 1 - }, - "pkg/compiler/lib/src/inferrer/typemasks/map_type_mask.dart": { - "Dynamic access of 'isNullable'.": 2, - "Dynamic access of 'isEmptyOrNull'.": 1, - "Dynamic access of 'isMap'.": 1, - "Dynamic access of 'keyType'.": 4, - "Dynamic access of 'valueType'.": 2, - "Dynamic access of 'forwardTo'.": 2, - "Dynamic access of 'isDictionary'.": 1, - "Dynamic invocation of 'union'.": 1, - "Dynamic access of 'typeMap'.": 1, - "Dynamic access of 'values'.": 1, - "Dynamic invocation of 'fold'.": 1, - "Dynamic access of 'allocationNode'.": 1, - "Dynamic access of 'allocationElement'.": 1 - }, - "pkg/compiler/lib/src/inferrer/typemasks/set_type_mask.dart": { - "Dynamic access of 'isNullable'.": 2, - "Dynamic access of 'isEmptyOrNull'.": 1, - "Dynamic access of 'isSet'.": 1, - "Dynamic access of 'elementType'.": 2, - "Dynamic access of 'forwardTo'.": 1 - }, "pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart": { "Dynamic access of 'isForwarding'.": 1, "Dynamic access of 'forwardTo'.": 1 }, - "pkg/compiler/lib/src/inferrer/typemasks/union_type_mask.dart": { - "Dynamic invocation of 'nonNullable'.": 2, - "Dynamic access of 'isNullable'.": 3, - "Dynamic access of 'isUnion'.": 2, - "Dynamic access of 'disjointMasks'.": 3, - "Dynamic invocation of 'contains'.": 1 - }, "pkg/compiler/lib/src/helpers/expensive_map.dart": { "Dynamic access of 'length'.": 1, "Dynamic access of 'isEmpty'.": 1, diff --git a/pkg/compiler/test/inference/type_combination_test.dart b/pkg/compiler/test/inference/type_combination_test.dart index 517ac768d46..05882166450 100644 --- a/pkg/compiler/test/inference/type_combination_test.dart +++ b/pkg/compiler/test/inference/type_combination_test.dart @@ -69,19 +69,23 @@ class Pair { class RuleSet { final name; final operate; - final Set typesSeen = new Set(); - final Set pairsSeen = new Set(); + final Set typesSeen = {}; + final Set pairsSeen = {}; RuleSet(this.name, this.operate); void rule(type1, type2, result) { - typesSeen..add(type1)..add(type2); - var pair1 = new Pair(type1, type2); - var pair2 = new Pair(type2, type1); + typesSeen + ..add(type1) + ..add(type2); + var pair1 = Pair(type1, type2); + var pair2 = Pair(type2, type1); if (pairsSeen.contains(pair1)) { Expect.isFalse(true, 'Redundant rule ($type1, $type2, ...)'); } - pairsSeen..add(pair1)..add(pair2); + pairsSeen + ..add(pair1) + ..add(pair2); var r1 = operate(type1, type2); var r2 = operate(type2, type1); @@ -90,8 +94,10 @@ class RuleSet { } void check(type1, type2, predicate) { - typesSeen..add(type1)..add(type2); - var pair = new Pair(type1, type2); + typesSeen + ..add(type1) + ..add(type2); + var pair = Pair(type1, type2); pairsSeen..add(pair); var result = operate(type1, type2); Expect.isTrue(predicate(result)); @@ -100,7 +106,7 @@ class RuleSet { void validateCoverage() { for (var type1 in typesSeen) { for (var type2 in typesSeen) { - var pair = new Pair(type1, type2); + var pair = Pair(type1, type2); if (!pairsSeen.contains(pair)) { Expect.isTrue(false, 'Missing rule: $name($type1, $type2)'); } @@ -111,7 +117,7 @@ class RuleSet { void testUnion(JClosedWorld closedWorld) { AbstractValueDomain commonMasks = closedWorld.abstractValueDomain; - RuleSet ruleSet = new RuleSet( + RuleSet ruleSet = RuleSet( 'union', (t1, t2) => simplify(t1.union(t2, commonMasks), commonMasks)); rule(type1, type2, result) => ruleSet.rule(type1, type2, result); check(type1, type2, predicate) => ruleSet.check(type1, type2, predicate); @@ -422,7 +428,7 @@ void testUnion(JClosedWorld closedWorld) { } void testIntersection(JClosedWorld closedWorld) { - RuleSet ruleSet = new RuleSet('intersection', + RuleSet ruleSet = RuleSet('intersection', (t1, t2) => t1.intersection(t2, closedWorld.abstractValueDomain)); rule(type1, type2, result) => ruleSet.rule(type1, type2, result); @@ -566,12 +572,12 @@ void testIntersection(JClosedWorld closedWorld) { rule( jsIndexable, potentialArray, - new TypeMask.nonNullSubtype( + TypeMask.nonNullSubtype( closedWorld.commonElements.jsArrayClass, closedWorld)); rule( jsIndexable, potentialString, - new TypeMask.nonNullSubtype( + TypeMask.nonNullSubtype( closedWorld.commonElements.jsStringClass, closedWorld)); rule(jsIndexable, jsBooleanOrNull, emptyType); rule(jsIndexable, jsNumberOrNull, emptyType); @@ -738,7 +744,7 @@ void testIntersection(JClosedWorld closedWorld) { void testRegressions(JClosedWorld closedWorld) { TypeMask nonNullPotentialString = - new TypeMask.nonNullSubtype(patternClass, closedWorld); + TypeMask.nonNullSubtype(patternClass, closedWorld); Expect.equals( potentialString, jsStringOrNull.union( @@ -776,67 +782,67 @@ runTests() async { LibraryEntity coreLibrary = commonElements.coreLibrary; patternClass = elementEnvironment.lookupClass(coreLibrary, 'Pattern'); - nonPrimitive1 = new TypeMask.nonNullSubtype( - closedWorld.commonElements.mapClass, closedWorld); - nonPrimitive2 = new TypeMask.nonNullSubtype( + nonPrimitive1 = + TypeMask.nonNullSubtype(closedWorld.commonElements.mapClass, closedWorld); + nonPrimitive2 = TypeMask.nonNullSubtype( closedWorld.commonElements.functionClass, closedWorld); potentialArray = - new TypeMask.subtype(closedWorld.commonElements.listClass, closedWorld); - potentialString = new TypeMask.subtype(patternClass, closedWorld); - jsInterceptor = new TypeMask.nonNullSubclass( + TypeMask.subtype(closedWorld.commonElements.listClass, closedWorld); + potentialString = TypeMask.subtype(patternClass, closedWorld); + jsInterceptor = TypeMask.nonNullSubclass( closedWorld.commonElements.jsInterceptorClass, closedWorld); - jsArrayOrNull = new TypeMask.subclass( + jsArrayOrNull = + TypeMask.subclass(closedWorld.commonElements.jsArrayClass, closedWorld); + jsReadableArray = TypeMask.nonNullSubclass( closedWorld.commonElements.jsArrayClass, closedWorld); - jsReadableArray = new TypeMask.nonNullSubclass( - closedWorld.commonElements.jsArrayClass, closedWorld); - jsMutableArrayOrNull = new TypeMask.subclass( + jsMutableArrayOrNull = TypeMask.subclass( closedWorld.commonElements.jsMutableArrayClass, closedWorld); - jsMutableArray = new TypeMask.nonNullSubclass( + jsMutableArray = TypeMask.nonNullSubclass( closedWorld.commonElements.jsMutableArrayClass, closedWorld); - jsFixedArrayOrNull = new TypeMask.exact( + jsFixedArrayOrNull = + TypeMask.exact(closedWorld.commonElements.jsFixedArrayClass, closedWorld); + jsFixedArray = TypeMask.nonNullExact( closedWorld.commonElements.jsFixedArrayClass, closedWorld); - jsFixedArray = new TypeMask.nonNullExact( - closedWorld.commonElements.jsFixedArrayClass, closedWorld); - jsExtendableArrayOrNull = new TypeMask.exact( + jsExtendableArrayOrNull = TypeMask.exact( closedWorld.commonElements.jsExtendableArrayClass, closedWorld); - jsExtendableArray = new TypeMask.nonNullExact( + jsExtendableArray = TypeMask.nonNullExact( closedWorld.commonElements.jsExtendableArrayClass, closedWorld); - jsUnmodifiableArrayOrNull = new TypeMask.exact( + jsUnmodifiableArrayOrNull = TypeMask.exact( closedWorld.commonElements.jsUnmodifiableArrayClass, closedWorld); - jsUnmodifiableArray = new TypeMask.nonNullExact( + jsUnmodifiableArray = TypeMask.nonNullExact( closedWorld.commonElements.jsUnmodifiableArrayClass, closedWorld); - jsIndexableOrNull = new TypeMask.subtype( + jsIndexableOrNull = TypeMask.subtype( closedWorld.commonElements.jsIndexableClass, closedWorld); - jsIndexable = new TypeMask.nonNullSubtype( + jsIndexable = TypeMask.nonNullSubtype( closedWorld.commonElements.jsIndexableClass, closedWorld); - jsInterceptorOrNull = new TypeMask.subclass( + jsInterceptorOrNull = TypeMask.subclass( closedWorld.commonElements.jsInterceptorClass, closedWorld); jsStringOrNull = - new TypeMask.exact(closedWorld.commonElements.jsStringClass, closedWorld); - jsString = new TypeMask.nonNullExact( + TypeMask.exact(closedWorld.commonElements.jsStringClass, closedWorld); + jsString = TypeMask.nonNullExact( closedWorld.commonElements.jsStringClass, closedWorld); - jsBoolean = new TypeMask.nonNullExact( + jsBoolean = TypeMask.nonNullExact( closedWorld.commonElements.jsBoolClass, closedWorld); - jsNumber = new TypeMask.nonNullSubclass( + jsNumber = TypeMask.nonNullSubclass( closedWorld.commonElements.jsNumberClass, closedWorld); - jsInteger = new TypeMask.nonNullExact( - closedWorld.commonElements.jsIntClass, closedWorld); - jsNumNotInt = new TypeMask.nonNullExact( + jsInteger = + TypeMask.nonNullExact(closedWorld.commonElements.jsIntClass, closedWorld); + jsNumNotInt = TypeMask.nonNullExact( closedWorld.commonElements.jsNumNotIntClass, closedWorld); jsBooleanOrNull = - new TypeMask.exact(closedWorld.commonElements.jsBoolClass, closedWorld); - jsNumberOrNull = new TypeMask.subclass( - closedWorld.commonElements.jsNumberClass, closedWorld); + TypeMask.exact(closedWorld.commonElements.jsBoolClass, closedWorld); + jsNumberOrNull = + TypeMask.subclass(closedWorld.commonElements.jsNumberClass, closedWorld); jsIntegerOrNull = - new TypeMask.exact(closedWorld.commonElements.jsIntClass, closedWorld); - jsNumNotIntOrNull = new TypeMask.exact( - closedWorld.commonElements.jsNumNotIntClass, closedWorld); - nullType = const TypeMask.empty(); - objectType = new TypeMask.nonNullSubclass( - closedWorld.commonElements.objectClass, closedWorld); - emptyType = const TypeMask.nonNullEmpty(); - dynamicType = new TypeMask.subclass( + TypeMask.exact(closedWorld.commonElements.jsIntClass, closedWorld); + jsNumNotIntOrNull = + TypeMask.exact(closedWorld.commonElements.jsNumNotIntClass, closedWorld); + nullType = TypeMask.empty(); + objectType = TypeMask.nonNullSubclass( closedWorld.commonElements.objectClass, closedWorld); + emptyType = TypeMask.nonNullEmpty(); + dynamicType = + TypeMask.subclass(closedWorld.commonElements.objectClass, closedWorld); jsInterceptorOrComparable = interceptorOrComparable(closedWorld, nullable: false); diff --git a/pkg/compiler/test/inference/type_mask2_test.dart b/pkg/compiler/test/inference/type_mask2_test.dart index e5056f820bb..e32853bebb0 100644 --- a/pkg/compiler/test/inference/type_mask2_test.dart +++ b/pkg/compiler/test/inference/type_mask2_test.dart @@ -41,12 +41,13 @@ checkMasks(JClosedWorld closedWorld, List allClasses, 'Unexpected disjoint masks: $disjoint, expected $disjointMasks.'); if (flattened == null) { Expect.throws( - () => UnionTypeMask.flatten(disjoint, isNullable, commonMasks), + () => UnionTypeMask.flatten(disjoint, commonMasks, + includeNull: isNullable), (e) => e is ArgumentError, 'Expect argument error on flattening of $disjoint.'); } else { TypeMask flattenResult = - UnionTypeMask.flatten(disjoint, isNullable, commonMasks); + UnionTypeMask.flatten(disjoint, commonMasks, includeNull: isNullable); Expect.equals( flattened, flattenResult, @@ -120,16 +121,16 @@ Future testUnionTypeMaskFlatten() async { containedClasses: containedClasses); } - TypeMask empty = const TypeMask.nonNullEmpty(); - TypeMask subclassObject = new TypeMask.nonNullSubclass(Object_, closedWorld); - TypeMask exactA = new TypeMask.nonNullExact(A, closedWorld); - TypeMask subclassA = new TypeMask.nonNullSubclass(A, closedWorld); - TypeMask subtypeA = new TypeMask.nonNullSubtype(A, closedWorld); - TypeMask exactB = new TypeMask.nonNullExact(B, closedWorld); - TypeMask subclassB = new TypeMask.nonNullSubclass(B, closedWorld); - TypeMask exactC = new TypeMask.nonNullExact(C, closedWorld); - TypeMask exactD = new TypeMask.nonNullExact(D, closedWorld); - TypeMask exactE = new TypeMask.nonNullExact(E, closedWorld); + TypeMask empty = TypeMask.nonNullEmpty(); + TypeMask subclassObject = TypeMask.nonNullSubclass(Object_, closedWorld); + TypeMask exactA = TypeMask.nonNullExact(A, closedWorld); + TypeMask subclassA = TypeMask.nonNullSubclass(A, closedWorld); + TypeMask subtypeA = TypeMask.nonNullSubtype(A, closedWorld); + TypeMask exactB = TypeMask.nonNullExact(B, closedWorld); + TypeMask subclassB = TypeMask.nonNullSubclass(B, closedWorld); + TypeMask exactC = TypeMask.nonNullExact(C, closedWorld); + TypeMask exactD = TypeMask.nonNullExact(D, closedWorld); + TypeMask exactE = TypeMask.nonNullExact(E, closedWorld); check([], result: empty, @@ -241,11 +242,10 @@ Future testStringSubtypes() async { Expect.isFalse(closedWorld.classHierarchy.isIndirectlyInstantiated(JSString)); Expect.isTrue(closedWorld.classHierarchy.isInstantiated(JSString)); - TypeMask subtypeString = new TypeMask.nonNullSubtype(String_, closedWorld); - TypeMask exactJSString = new TypeMask.nonNullExact(JSString, closedWorld); - TypeMask subtypeJSString = new TypeMask.nonNullSubtype(JSString, closedWorld); - TypeMask subclassJSString = - new TypeMask.nonNullSubclass(JSString, closedWorld); + TypeMask subtypeString = TypeMask.nonNullSubtype(String_, closedWorld); + TypeMask exactJSString = TypeMask.nonNullExact(JSString, closedWorld); + TypeMask subtypeJSString = TypeMask.nonNullSubtype(JSString, closedWorld); + TypeMask subclassJSString = TypeMask.nonNullSubclass(JSString, closedWorld); Expect.equals(exactJSString, subtypeString); Expect.equals(exactJSString, subtypeJSString); diff --git a/pkg/compiler/test/inference/type_mask_test_helper.dart b/pkg/compiler/test/inference/type_mask_test_helper.dart index ba29b08884e..182c00b76ae 100644 --- a/pkg/compiler/test/inference/type_mask_test_helper.dart +++ b/pkg/compiler/test/inference/type_mask_test_helper.dart @@ -16,7 +16,8 @@ AbstractValue simplify(AbstractValue value, AbstractValueDomain domain) { if (value is ForwardingTypeMask) { return simplify(value.forwardTo, domain); } else if (value is UnionTypeMask) { - return UnionTypeMask.flatten(value.disjointMasks, value.isNullable, domain); + return UnionTypeMask.flatten(value.disjointMasks, domain, + includeNull: value.isNullable); } else { return value; }