Add TypedInteger

This CL adds a TypedInteger helper which provides additional type
safety in Dawn. It is a compile-time restriction that prevents integers
of different types from being used interchangably in Debug builds.

It also adds ityp::{array,bitset,span} as helper classes to wrap std::
versions (not span). These accept a template paramter as the Index type
so that typed integers, or enum classes, may be used as a type-safe
index.

For now, bind group layout binding indices use TypedInteger. Future
CLs will convert other indices to be type-safe as well.

Bug: dawn:442
Change-Id: I5b63b1e4f6154322db0227a7788a4e9b8303410e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/19902
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/src/tests/unittests/BitSetIteratorTests.cpp b/src/tests/unittests/BitSetIteratorTests.cpp
index cc6c3ed..34cfb6c 100644
--- a/src/tests/unittests/BitSetIteratorTests.cpp
+++ b/src/tests/unittests/BitSetIteratorTests.cpp
@@ -15,6 +15,7 @@
 #include <gtest/gtest.h>
 
 #include "common/BitSetIterator.h"
+#include "common/ityp_bitset.h"
 
 // This is ANGLE's BitSetIterator_unittests.cpp file.
 
@@ -48,7 +49,7 @@
 // Test an empty iterator.
 TEST_F(BitSetIteratorTest, EmptySet) {
     // We don't use the FAIL gtest macro here since it returns immediately,
-    // causing an unreachable code warning in MSVS
+    // causing an unreachable code warning in MSVC
     bool sawBit = false;
     for (unsigned long bit : IterateBitSet(mStateBits)) {
         DAWN_UNUSED(bit);
@@ -82,3 +83,137 @@
 
     EXPECT_EQ((mStateBits & otherBits).count(), seenBits.size());
 }
+
+class EnumBitSetIteratorTest : public testing::Test {
+  protected:
+    enum class TestEnum { A, B, C, D, E, F, G, H, I, J, EnumCount };
+
+    static constexpr size_t kEnumCount = static_cast<size_t>(TestEnum::EnumCount);
+    ityp::bitset<TestEnum, kEnumCount> mStateBits;
+};
+
+// Simple iterator test.
+TEST_F(EnumBitSetIteratorTest, Iterator) {
+    std::set<TestEnum> originalValues;
+    originalValues.insert(TestEnum::B);
+    originalValues.insert(TestEnum::F);
+    originalValues.insert(TestEnum::C);
+    originalValues.insert(TestEnum::I);
+
+    for (TestEnum value : originalValues) {
+        mStateBits.set(value);
+    }
+
+    std::set<TestEnum> readValues;
+    for (TestEnum bit : IterateBitSet(mStateBits)) {
+        EXPECT_EQ(1u, originalValues.count(bit));
+        EXPECT_EQ(0u, readValues.count(bit));
+        readValues.insert(bit);
+    }
+
+    EXPECT_EQ(originalValues.size(), readValues.size());
+}
+
+// Test an empty iterator.
+TEST_F(EnumBitSetIteratorTest, EmptySet) {
+    // We don't use the FAIL gtest macro here since it returns immediately,
+    // causing an unreachable code warning in MSVC
+    bool sawBit = false;
+    for (TestEnum bit : IterateBitSet(mStateBits)) {
+        DAWN_UNUSED(bit);
+        sawBit = true;
+    }
+    EXPECT_FALSE(sawBit);
+}
+
+// Test iterating a result of combining two bitsets.
+TEST_F(EnumBitSetIteratorTest, NonLValueBitset) {
+    ityp::bitset<TestEnum, kEnumCount> otherBits;
+
+    mStateBits.set(TestEnum::B);
+    mStateBits.set(TestEnum::C);
+    mStateBits.set(TestEnum::D);
+    mStateBits.set(TestEnum::E);
+
+    otherBits.set(TestEnum::A);
+    otherBits.set(TestEnum::B);
+    otherBits.set(TestEnum::D);
+    otherBits.set(TestEnum::F);
+
+    std::set<TestEnum> seenBits;
+
+    for (TestEnum bit : IterateBitSet(mStateBits & otherBits)) {
+        EXPECT_EQ(0u, seenBits.count(bit));
+        seenBits.insert(bit);
+        EXPECT_TRUE(mStateBits[bit]);
+        EXPECT_TRUE(otherBits[bit]);
+    }
+
+    EXPECT_EQ((mStateBits & otherBits).count(), seenBits.size());
+}
+
+class ITypBitsetIteratorTest : public testing::Test {
+  protected:
+    using IntegerT = TypedInteger<struct Foo, uint32_t>;
+    ityp::bitset<IntegerT, 40> mStateBits;
+};
+
+// Simple iterator test.
+TEST_F(ITypBitsetIteratorTest, Iterator) {
+    std::set<IntegerT> originalValues;
+    originalValues.insert(IntegerT(2));
+    originalValues.insert(IntegerT(6));
+    originalValues.insert(IntegerT(8));
+    originalValues.insert(IntegerT(35));
+
+    for (IntegerT value : originalValues) {
+        mStateBits.set(value);
+    }
+
+    std::set<IntegerT> readValues;
+    for (IntegerT bit : IterateBitSet(mStateBits)) {
+        EXPECT_EQ(1u, originalValues.count(bit));
+        EXPECT_EQ(0u, readValues.count(bit));
+        readValues.insert(bit);
+    }
+
+    EXPECT_EQ(originalValues.size(), readValues.size());
+}
+
+// Test an empty iterator.
+TEST_F(ITypBitsetIteratorTest, EmptySet) {
+    // We don't use the FAIL gtest macro here since it returns immediately,
+    // causing an unreachable code warning in MSVC
+    bool sawBit = false;
+    for (IntegerT bit : IterateBitSet(mStateBits)) {
+        DAWN_UNUSED(bit);
+        sawBit = true;
+    }
+    EXPECT_FALSE(sawBit);
+}
+
+// Test iterating a result of combining two bitsets.
+TEST_F(ITypBitsetIteratorTest, NonLValueBitset) {
+    ityp::bitset<IntegerT, 40> otherBits;
+
+    mStateBits.set(IntegerT(1));
+    mStateBits.set(IntegerT(2));
+    mStateBits.set(IntegerT(3));
+    mStateBits.set(IntegerT(4));
+
+    otherBits.set(IntegerT(0));
+    otherBits.set(IntegerT(1));
+    otherBits.set(IntegerT(3));
+    otherBits.set(IntegerT(5));
+
+    std::set<IntegerT> seenBits;
+
+    for (IntegerT bit : IterateBitSet(mStateBits & otherBits)) {
+        EXPECT_EQ(0u, seenBits.count(bit));
+        seenBits.insert(bit);
+        EXPECT_TRUE(mStateBits[bit]);
+        EXPECT_TRUE(otherBits[bit]);
+    }
+
+    EXPECT_EQ((mStateBits & otherBits).count(), seenBits.size());
+}