Refactor CapabilitySet into templated class EnumSet
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index d2a82af..2feac3f 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -129,8 +129,8 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/util/hex_float.h
   ${CMAKE_CURRENT_SOURCE_DIR}/assembly_grammar.h
   ${CMAKE_CURRENT_SOURCE_DIR}/binary.h
-  ${CMAKE_CURRENT_SOURCE_DIR}/capability_set.h
   ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/enum_set.h
   ${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.h
   ${CMAKE_CURRENT_SOURCE_DIR}/instruction.h
   ${CMAKE_CURRENT_SOURCE_DIR}/macro.h
@@ -149,7 +149,6 @@
 
   ${CMAKE_CURRENT_SOURCE_DIR}/assembly_grammar.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/binary.cpp
-  ${CMAKE_CURRENT_SOURCE_DIR}/capability_set.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.cpp
diff --git a/source/capability_set.h b/source/capability_set.h
deleted file mode 100644
index c13e48f..0000000
--- a/source/capability_set.h
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (c) 2016 Google Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and/or associated documentation files (the
-// "Materials"), to deal in the Materials without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Materials, and to
-// permit persons to whom the Materials are furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Materials.
-//
-// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
-// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
-// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
-//    https://www.khronos.org/registry/
-//
-// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-
-#ifndef LIBSPIRV_CAPABILITY_SET_H
-#define LIBSPIRV_CAPABILITY_SET_H
-
-#include <cstdint>
-#include <functional>
-#include <memory>
-#include <set>
-#include <utility>
-
-#include "spirv/1.1/spirv.h"
-
-namespace libspirv {
-
-// A set of values of type SpvCapability.
-// It is fast and compact for the common case, where capability values
-// are at most 63.  But it can represent capabilities with larger values,
-// as may appear in extensions.
-class CapabilitySet {
- private:
-  // The ForEach method will call the functor on capabilities in
-  // enum value order (lowest to highest).  To make that easier, use
-  // an ordered set for the overflow values.
-  using OverflowSetType = std::set<uint32_t>;
-
- public:
-  // Construct an empty set.
-  CapabilitySet() = default;
-  // Construct an set with just the given capability.
-  explicit CapabilitySet(SpvCapability c) { Add(c); }
-  // Construct an set from an initializer list of capabilities.
-  CapabilitySet(std::initializer_list<SpvCapability> cs) {
-    for (auto c : cs) Add(c);
-  }
-  // Copy constructor.
-  CapabilitySet(const CapabilitySet& other) { *this = other; }
-  // Move constructor.  The moved-from set is emptied.
-  CapabilitySet(CapabilitySet&& other) {
-    mask_ = other.mask_;
-    overflow_ = std::move(other.overflow_);
-    other.mask_ = 0;
-    other.overflow_.reset(nullptr);
-  }
-  // Assignment operator.
-  CapabilitySet& operator=(const CapabilitySet& other) {
-    if (&other != this) {
-      mask_ = other.mask_;
-      overflow_.reset(other.overflow_ ? new OverflowSetType(*other.overflow_)
-                                      : nullptr);
-    }
-    return *this;
-  }
-
-  // Adds the given capability to the set.  This has no effect if the
-  // capability is already in the set.
-  void Add(SpvCapability c) { Add(static_cast<uint32_t>(c)); }
-  // Adds the given capability (as a 32-bit word) to the set.  This has no
-  // effect if the capability is already in the set.
-  void Add(uint32_t);
-
-  // Returns true if this capability is in the set.
-  bool Contains(SpvCapability c) const {
-    return Contains(static_cast<uint32_t>(c));
-  }
-  // Returns true if the capability represented as a 32-bit word is in the set.
-  bool Contains(uint32_t word) const;
-
-  // Applies f to each capability in the set, in order from smallest enum
-  // value to largest.
-  void ForEach(std::function<void(SpvCapability)> f) const;
-
- private:
-  // Returns true if the set has capabilities with value greater than 63.
-  bool HasOverflow() { return overflow_ != nullptr; }
-
-  // Ensures that overflow_set_ references a set.  A new empty set is
-  // allocated if one doesn't exist yet.  Returns overflow_set_.
-  OverflowSetType& Overflow() {
-    if (overflow_.get() == nullptr) {
-      overflow_.reset(new OverflowSetType);
-    }
-    return *overflow_;
-  }
-
-  // Capabilities with values up to 63 are stored as bits in this mask.
-  uint64_t mask_ = 0;
-  // Capabilities with values larger than 63 are stored in this set.
-  // This set should normally be empty or very small.
-  std::unique_ptr<OverflowSetType> overflow_ = {};
-};
-
-}  // namespace libspirv
-
-#endif  // LIBSPIRV_CAPABILITY_SET_H
diff --git a/source/capability_set.cpp b/source/enum_set.cpp
similarity index 90%
rename from source/capability_set.cpp
rename to source/enum_set.cpp
index f669fa2..d0f00e0 100644
--- a/source/capability_set.cpp
+++ b/source/enum_set.cpp
@@ -24,13 +24,13 @@
 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 
-#include "capability_set.h"
+#include "enum_set.h"
 
 #include "spirv/1.1/spirv.hpp"
 
 namespace {
 
-// Determines whether the given capability can be represented
+// Determines whether the given enum value can be represented
 // as a bit in a uint64_t mask. If so, then returns that mask bit.
 // Otherwise, returns 0.
 uint64_t AsMask(uint32_t word) {
@@ -41,7 +41,8 @@
 
 namespace libspirv {
 
-void CapabilitySet::Add(uint32_t word) {
+template<typename EnumType>
+void EnumSet<EnumType>::Add(uint32_t word) {
   if (auto new_bits = AsMask(word)) {
     mask_ |= new_bits;
   } else {
@@ -49,7 +50,8 @@
   }
 }
 
-bool CapabilitySet::Contains(uint32_t word) const {
+template<typename EnumType>
+bool EnumSet<EnumType>::Contains(uint32_t word) const {
   // We shouldn't call Overflow() since this is a const method.
   if (auto bits = AsMask(word)) {
     return mask_ & bits;
diff --git a/source/enum_set.h b/source/enum_set.h
new file mode 100644
index 0000000..90fb59a
--- /dev/null
+++ b/source/enum_set.h
@@ -0,0 +1,156 @@
+// Copyright (c) 2016 Google Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+//    https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#ifndef LIBSPIRV_ENUM_SET_H
+#define LIBSPIRV_ENUM_SET_H
+
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <set>
+#include <utility>
+
+#include "spirv/1.1/spirv.h"
+
+namespace libspirv {
+
+// A set of values of a 32-bit enum type.
+// It is fast and compact for the common case, where enum values
+// are at most 63.  But it can represent enums with larger values,
+// as may appear in extensions.
+template <typename EnumType>
+class EnumSet {
+ private:
+  // The ForEach method will call the functor on enum values in
+  // enum value order (lowest to highest).  To make that easier, use
+  // an ordered set for the overflow values.
+  using OverflowSetType = std::set<uint32_t>;
+
+ public:
+  // Construct an empty set.
+  EnumSet() = default;
+  // Construct an set with just the given enum value.
+  explicit EnumSet(EnumType c) { Add(c); }
+  // Construct an set from an initializer list of enum values.
+  EnumSet(std::initializer_list<EnumType> cs) {
+    for (auto c : cs) Add(c);
+  }
+  // Copy constructor.
+  EnumSet(const EnumSet& other) { *this = other; }
+  // Move constructor.  The moved-from set is emptied.
+  EnumSet(EnumSet&& other) {
+    mask_ = other.mask_;
+    overflow_ = std::move(other.overflow_);
+    other.mask_ = 0;
+    other.overflow_.reset(nullptr);
+  }
+  // Assignment operator.
+  EnumSet& operator=(const EnumSet& other) {
+    if (&other != this) {
+      mask_ = other.mask_;
+      overflow_.reset(other.overflow_ ? new OverflowSetType(*other.overflow_)
+                                      : nullptr);
+    }
+    return *this;
+  }
+
+  // Adds the given enum value to the set.  This has no effect if the
+  // enum value is already in the set.
+  void Add(EnumType c) { Add(ToWord(c)); }
+  // Adds the given enum value (as a 32-bit word) to the set.  This has no
+  // effect if the enum value is already in the set.
+  void Add(uint32_t word) {
+    if (auto new_bits = AsMask(word)) {
+      mask_ |= new_bits;
+    } else {
+      Overflow().insert(word);
+    }
+  }
+
+  // Returns true if this enum value is in the set.
+  bool Contains(EnumType c) const { return Contains(ToWord(c)); }
+  // Returns true if the enum represented as a 32-bit word is in the set.
+  bool Contains(uint32_t word) const {
+    // We shouldn't call Overflow() since this is a const method.
+    if (auto bits = AsMask(word)) {
+      return mask_ & bits;
+    } else if (auto overflow = overflow_.get()) {
+      return overflow->find(word) != overflow->end();
+    }
+    // The word is large, but the set doesn't have large members, so
+    // it doesn't have an overflow set.
+    return false;
+  }
+
+  // Applies f to each enum in the set, in order from smallest enum
+  // value to largest.
+  void ForEach(std::function<void(EnumType)> f) const {
+    for (uint32_t i = 0; i < 64; ++i) {
+      if (mask_ & AsMask(i)) f(static_cast<EnumType>(i));
+    }
+    if (overflow_) {
+      for (uint32_t c : *overflow_) f(static_cast<EnumType>(c));
+    }
+  }
+
+ private:
+  // Returns the enum value as a uint32_t.
+  uint32_t ToWord(EnumType value) const {
+    static_assert(sizeof(EnumType) <= sizeof(uint32_t),
+                  "EnumType must statically castable to uint32_t");
+    return static_cast<uint32_t>(value);
+  }
+
+  // Determines whether the given enum value can be represented
+  // as a bit in a uint64_t mask. If so, then returns that mask bit.
+  // Otherwise, returns 0.
+  uint64_t AsMask(uint32_t word) const {
+    if (word > 63) return 0;
+    return uint64_t(1) << word;
+  }
+
+  // Ensures that overflow_set_ references a set.  A new empty set is
+  // allocated if one doesn't exist yet.  Returns overflow_set_.
+  OverflowSetType& Overflow() {
+    if (overflow_.get() == nullptr) {
+      overflow_.reset(new OverflowSetType);
+    }
+    return *overflow_;
+  }
+
+  // Enums with values up to 63 are stored as bits in this mask.
+  uint64_t mask_ = 0;
+  // Enums with values larger than 63 are stored in this set.
+  // This set should normally be empty or very small.
+  std::unique_ptr<OverflowSetType> overflow_ = {};
+};
+
+// A set of SpvCapability, optimized for small capability values.
+using CapabilitySet = EnumSet<SpvCapability>;
+
+}  // namespace libspirv
+
+#endif  // LIBSPIRV_ENUM_SET_H
diff --git a/source/table.h b/source/table.h
index 5b00677..aee63b3 100644
--- a/source/table.h
+++ b/source/table.h
@@ -29,7 +29,7 @@
 
 #include "spirv/1.1/spirv.h"
 
-#include "capability_set.h"
+#include "enum_set.h"
 #include "spirv-tools/libspirv.h"
 
 typedef struct spv_opcode_desc_t {
diff --git a/source/val/ValidationState.h b/source/val/ValidationState.h
index c7ae565..94c3366 100644
--- a/source/val/ValidationState.h
+++ b/source/val/ValidationState.h
@@ -34,8 +34,8 @@
 #include <vector>
 
 #include "assembly_grammar.h"
-#include "capability_set.h"
 #include "diagnostic.h"
+#include "enum_set.h"
 #include "spirv-tools/libspirv.h"
 #include "spirv/1.1/spirv.h"
 #include "spirv_definition.h"
diff --git a/source/validate_instruction.cpp b/source/validate_instruction.cpp
index 300a931..224922f 100644
--- a/source/validate_instruction.cpp
+++ b/source/validate_instruction.cpp
@@ -33,8 +33,8 @@
 #include <sstream>
 #include <string>
 
-#include "capability_set.h"
 #include "diagnostic.h"
+#include "enum_set.h"
 #include "opcode.h"
 #include "operand.h"
 #include "spirv_definition.h"
diff --git a/test/CapabilitySet.cpp b/test/CapabilitySet.cpp
index 9b3d8c4..791a855 100644
--- a/test/CapabilitySet.cpp
+++ b/test/CapabilitySet.cpp
@@ -24,11 +24,10 @@
 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 
-#include "capability_set.h"
-
 #include <vector>
 #include "gmock/gmock.h"
 
+#include "enum_set.h"
 #include "UnitSPIRV.h"
 
 namespace {
diff --git a/test/OpcodeRequiresCapabilities.cpp b/test/OpcodeRequiresCapabilities.cpp
index 80965cb..f1c7e25 100644
--- a/test/OpcodeRequiresCapabilities.cpp
+++ b/test/OpcodeRequiresCapabilities.cpp
@@ -26,7 +26,7 @@
 
 #include "UnitSPIRV.h"
 
-#include "capability_set.h"
+#include "enum_set.h"
 
 namespace {
 
diff --git a/test/OperandCapabilities.cpp b/test/OperandCapabilities.cpp
index ca2219d..0f124e2 100644
--- a/test/OperandCapabilities.cpp
+++ b/test/OperandCapabilities.cpp
@@ -30,7 +30,7 @@
 
 #include "gmock/gmock.h"
 
-#include "capability_set.h"
+#include "enum_set.h"
 #include "UnitSPIRV.h"
 
 namespace {
diff --git a/test/UnitSPIRV.h b/test/UnitSPIRV.h
index 2d91766..14e27ee 100644
--- a/test/UnitSPIRV.h
+++ b/test/UnitSPIRV.h
@@ -34,7 +34,7 @@
 
 #include "source/assembly_grammar.h"
 #include "source/binary.h"
-#include "source/capability_set.h"
+#include "source/enum_set.h"
 #include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_endian.h"
diff --git a/test/ValidationState.cpp b/test/ValidationState.cpp
index 9d5e81f..4fc1ac7 100644
--- a/test/ValidationState.cpp
+++ b/test/ValidationState.cpp
@@ -34,7 +34,7 @@
 #include "gtest/gtest.h"
 #include "spirv/1.1/spirv.h"
 
-#include "capability_set.h"
+#include "enum_set.h"
 #include "val/Construct.h"
 #include "val/Function.h"
 #include "validate.h"