Index: include/llvm/ADT/OptionSet.h =================================================================== --- /dev/null +++ include/llvm/ADT/OptionSet.h @@ -0,0 +1,135 @@ +//===--- OptionSet.h - Sets of boolean options ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the OptionSet class template. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ADT_OPTIONSET_H +#define LLVM_ADT_OPTIONSET_H + +#include "llvm/ADT/None.h" +#include "llvm/Support/Compiler.h" + +#include +#include + +namespace llvm { + +/// The class template \c OptionSet captures a set of options stored as the +/// bits in an unsigned integral value. +/// +/// Each option corresponds to a particular flag value in the provided +/// enumeration type (\c Flags). The option set provides ways to add options, +/// remove options, intersect sets, etc., providing a thin type-safe layer +/// over the underlying unsigned value. +/// +/// \tparam Flags An enumeration type that provides the individual flags +/// for options. Each enumerator should have a power-of-two value, indicating +/// which bit it is associated with. +/// +/// \tparam StorageType The unsigned integral type to use to store the flags +/// enabled within this option set. This defaults to the unsigned form of the +/// underlying type of the enumeration. +template::type + >::type> +class OptionSet { + StorageType Storage; + +public: + /// Create an empty option set. + OptionSet() : Storage() { } + + /// Create an empty option set. + OptionSet(llvm::NoneType) : Storage() { } + + /// Create an option set with only the given option set. + OptionSet(Flags flag) : Storage(static_cast(flag)) { } + + /// Create an option set from raw storage. + explicit OptionSet(StorageType storage) : Storage(storage) { } + + /// Check whether an option set is non-empty. + explicit operator bool() const { return Storage != 0; } + + /// Explicitly convert an option set to its underlying storage. + explicit operator StorageType() const { return Storage; } + + /// Explicitly convert an option set to intptr_t, for use in + /// llvm::PointerIntPair. + /// + /// This member is not present if the underlying type is bigger than + /// a pointer. + template + explicit operator typename std::enable_if::type () const { + return static_cast(Storage); + } + + /// Retrieve the "raw" representation of this option set. + StorageType toRaw() const { return Storage; } + + /// Determine whether this option set contains all of the options in the + /// given set. + bool contains(OptionSet set) const { + return !static_cast(set - *this); + } + + /// Produce the union of two option sets. + friend OptionSet operator|(OptionSet lhs, OptionSet rhs) { + return OptionSet(lhs.Storage | rhs.Storage); + } + + /// Produce the union of two option sets. + friend OptionSet &operator|=(OptionSet &lhs, OptionSet rhs) { + lhs.Storage |= rhs.Storage; + return lhs; + } + + /// Produce the intersection of two option sets. + friend OptionSet operator&(OptionSet lhs, OptionSet rhs) { + return OptionSet(lhs.Storage & rhs.Storage); + } + + /// Produce the intersection of two option sets. + friend OptionSet &operator&=(OptionSet &lhs, OptionSet rhs) { + lhs.Storage &= rhs.Storage; + return lhs; + } + + /// Produce the difference of two option sets. + friend OptionSet operator-(OptionSet lhs, OptionSet rhs) { + return OptionSet(lhs.Storage & ~rhs.Storage); + } + + /// Produce the intersection of two option sets. + friend OptionSet &operator-=(OptionSet &lhs, OptionSet rhs) { + lhs.Storage &= ~rhs.Storage; + return lhs; + } + +private: +#if !defined(_MSC_VER) || LLVM_MSC_PREREQ(1900) + // MSVC 2013 has an ICE due to the following. + template + static auto _checkResultTypeOperatorOr(T t) -> decltype(t | t) { return T(); } + + static void _checkResultTypeOperatorOr(...) {} + + static_assert(!std::is_same::value, + "operator| should produce an OptionSet"); +#endif +}; + +} + +#endif Index: unittests/ADT/CMakeLists.txt =================================================================== --- unittests/ADT/CMakeLists.txt +++ unittests/ADT/CMakeLists.txt @@ -24,6 +24,7 @@ MakeUniqueTest.cpp MapVectorTest.cpp OptionalTest.cpp + OptionSetTest.cpp PackedVectorTest.cpp PointerEmbeddedIntTest.cpp PointerIntPairTest.cpp Index: unittests/ADT/OptionSetTest.cpp =================================================================== --- /dev/null +++ unittests/ADT/OptionSetTest.cpp @@ -0,0 +1,117 @@ +//===- llvm/unittests/ADT/OptionSetTest.cpp -------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/OptionSet.h" +#include "gtest/gtest.h" + +using namespace llvm; + +TEST(OptionSet, contains) { + enum class Flags { + A = 1 << 0, + B = 1 << 1, + C = 1 << 2 + }; + + OptionSet emptySet; + OptionSet aSet = Flags::A; + OptionSet abSet = aSet | Flags::B; + OptionSet abcSet = abSet | Flags::C; + OptionSet bcSet = abcSet - Flags::A; + OptionSet cSet = bcSet - Flags::B; + + EXPECT_TRUE(emptySet.contains(emptySet)); + EXPECT_FALSE(emptySet.contains(aSet)); + EXPECT_FALSE(emptySet.contains(abSet)); + EXPECT_FALSE(emptySet.contains(abcSet)); + EXPECT_FALSE(emptySet.contains(bcSet)); + EXPECT_FALSE(emptySet.contains(cSet)); + + EXPECT_TRUE(aSet.contains(emptySet)); + EXPECT_TRUE(aSet.contains(aSet)); + EXPECT_FALSE(aSet.contains(abSet)); + EXPECT_FALSE(aSet.contains(abcSet)); + EXPECT_FALSE(aSet.contains(bcSet)); + EXPECT_FALSE(aSet.contains(cSet)); + + EXPECT_TRUE(abSet.contains(emptySet)); + EXPECT_TRUE(abSet.contains(aSet)); + EXPECT_TRUE(abSet.contains(abSet)); + EXPECT_FALSE(abSet.contains(abcSet)); + EXPECT_FALSE(abSet.contains(bcSet)); + EXPECT_FALSE(abSet.contains(cSet)); + + EXPECT_TRUE(abcSet.contains(emptySet)); + EXPECT_TRUE(abcSet.contains(aSet)); + EXPECT_TRUE(abcSet.contains(abSet)); + EXPECT_TRUE(abcSet.contains(abcSet)); + EXPECT_TRUE(abcSet.contains(bcSet)); + EXPECT_TRUE(abcSet.contains(cSet)); +} + + +TEST(OptionSet, intptr_t) { + enum class Small : int8_t { + A = 1 << 0 + }; + + OptionSet small = Small::A; + EXPECT_EQ(static_cast(Small::A), static_cast(small)); + + +#if !defined(_MSC_VER) || LLVM_MSC_PREREQ(1900) + // Fails to compile in MSVC 2013 because it lacks proper constexpr support + // for std::numeric_limits. + enum class UPtr : uintptr_t { + A = std::numeric_limits::max() + }; + + OptionSet uptr = UPtr::A; + EXPECT_EQ(static_cast(UPtr::A), static_cast(uptr)); + + + enum class Ptr : intptr_t { + A = std::numeric_limits::min() + }; + + OptionSet ptr = Ptr::A; + EXPECT_EQ(static_cast(Ptr::A), static_cast(ptr)); +#endif +} + +#if !defined(_MSC_VER) || LLVM_MSC_PREREQ(1900) +// This fails on MSVC 2013; is_constructible returns true despite +// sizeof(intptr_t) == 4 and sizeof(long long) == 8. +TEST(OptionSet, intptr_t_isConstructible) { + // First check that std::is_constructible counts explicit conversion + // operators. + class AlwaysConvertible { + public: + explicit operator intptr_t () const { return 0; } + }; + + if (!std::is_constructible::value) { + // std::is_constructible doesn't test what we want it to. Just exit early. + return; + } + + enum class LongLong : unsigned long long { + A = 1 + }; + bool isConvertible = + std::is_constructible>::value; + + if (sizeof(intptr_t) < sizeof(long long)) { + EXPECT_FALSE(isConvertible); + } else { + EXPECT_TRUE(isConvertible); + } +} +#endif +