Add SetTypeMask.

Change-Id: I9f202506c364cc1387efa8cea4cca6038f350470
Reviewed-on: https://dart-review.googlesource.com/c/92146
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
index 4a118d1..abd60ae 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
@@ -29,6 +29,7 @@
 part 'flat_type_mask.dart';
 part 'forwarding_type_mask.dart';
 part 'map_type_mask.dart';
+part 'set_type_mask.dart';
 part 'type_mask.dart';
 part 'union_type_mask.dart';
 part 'value_type_mask.dart';
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/set_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/set_type_mask.dart
new file mode 100644
index 0000000..fb0a7e2
--- /dev/null
+++ b/pkg/compiler/lib/src/inferrer/typemasks/set_type_mask.dart
@@ -0,0 +1,116 @@
+// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of masks;
+
+/// A [SetTypeMask] is a [TypeMask] for a specific allocation site of a set
+/// (currently only the internal Set class) that will get specialized once the
+/// [TypeGraphInferrer] phase finds an element type for it.
+class SetTypeMask extends AllocationTypeMask {
+  /// Tag used for identifying serialized [SetTypeMask] objects in a debugging
+  /// data stream.
+  static const String tag = 'set-type-mask';
+
+  final TypeMask forwardTo;
+
+  // The [Node] where this type mask was created.
+  final ir.TreeNode allocationNode;
+
+  // The [Entity] where this type mask was created.
+  final MemberEntity allocationElement;
+
+  // The element type of this set.
+  final TypeMask elementType;
+
+  SetTypeMask(this.forwardTo, this.allocationNode, this.allocationElement,
+      this.elementType);
+
+  /// Deserializes a [SetTypeMask] object from [source].
+  factory SetTypeMask.readFromDataSource(
+      DataSource source, JClosedWorld closedWorld) {
+    source.begin(tag);
+    TypeMask forwardTo = new TypeMask.readFromDataSource(source, closedWorld);
+    ir.TreeNode allocationNode = source.readTreeNodeOrNull();
+    MemberEntity allocationElement = source.readMemberOrNull();
+    TypeMask elementType = new TypeMask.readFromDataSource(source, closedWorld);
+    source.end(tag);
+    return new SetTypeMask(
+        forwardTo, allocationNode, allocationElement, elementType);
+  }
+
+  /// Serializes this [SetTypeMask] to [sink].
+  @override
+  void writeToDataSink(DataSink sink) {
+    sink.writeEnum(TypeMaskKind.set);
+    sink.begin(tag);
+    forwardTo.writeToDataSink(sink);
+    sink.writeTreeNodeOrNull(allocationNode);
+    sink.writeMemberOrNull(allocationElement);
+    elementType.writeToDataSink(sink);
+    sink.end(tag);
+  }
+
+  @override
+  TypeMask nullable() => isNullable
+      ? this
+      : new SetTypeMask(
+          forwardTo.nullable(), allocationNode, allocationElement, elementType);
+
+  @override
+  TypeMask nonNullable() => isNullable
+      ? new SetTypeMask(forwardTo.nonNullable(), allocationNode,
+          allocationElement, elementType)
+      : this;
+
+  @override
+  bool get isSet => true;
+
+  @override
+  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, JClosedWorld closedWorld) {
+    TypeMask forwardIntersection = forwardTo.intersection(other, closedWorld);
+    if (forwardIntersection.isEmptyOrNull) return forwardIntersection;
+    return forwardIntersection.isNullable ? nullable() : nonNullable();
+  }
+
+  @override
+  TypeMask union(dynamic other, JClosedWorld closedWorld) {
+    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 &&
+        elementType != null &&
+        other.elementType != null) {
+      TypeMask newElementType =
+          elementType.union(other.elementType, closedWorld);
+      TypeMask newForwardTo = forwardTo.union(other.forwardTo, closedWorld);
+      return new SetTypeMask(newForwardTo, null, null, newElementType);
+    } else {
+      return forwardTo.union(other, closedWorld);
+    }
+  }
+
+  @override
+  bool operator ==(other) => super == other;
+
+  @override
+  int get hashCode =>
+      computeHashCode(allocationNode, isNullable, elementType, forwardTo);
+
+  @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 2b5cae6..cb54ce7 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart
@@ -98,6 +98,7 @@
   flat,
   union,
   container,
+  set,
   map,
   dictionary,
   value,
@@ -230,6 +231,8 @@
         return new UnionTypeMask.readFromDataSource(source, closedWorld);
       case TypeMaskKind.container:
         return new ContainerTypeMask.readFromDataSource(source, closedWorld);
+      case TypeMaskKind.set:
+        return new SetTypeMask.readFromDataSource(source, closedWorld);
       case TypeMaskKind.map:
         return new MapTypeMask.readFromDataSource(source, closedWorld);
       case TypeMaskKind.dictionary:
diff --git a/tests/compiler/dart2js/analyses/dart2js_allowed.json b/tests/compiler/dart2js/analyses/dart2js_allowed.json
index 5f6fdad..8ebdcf1 100644
--- a/tests/compiler/dart2js/analyses/dart2js_allowed.json
+++ b/tests/compiler/dart2js/analyses/dart2js_allowed.json
@@ -90,6 +90,13 @@
     "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
@@ -278,4 +285,4 @@
     "Dynamic access of 'superclass'.": 1,
     "Dynamic access of 'needsTearOff'.": 1
   }
-}
+}
\ No newline at end of file