//===--- OptionSet.h - Sets of boolean options ------------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file defines the OptionSet class template. // //===----------------------------------------------------------------------===// #ifndef SWIFT_BASIC_OPTIONSET_H #define SWIFT_BASIC_OPTIONSET_H #include #include #include #include namespace swift { /// The Swift standard library also has an `OptionSet` type that is imported /// when using C++ to Swift interop within the compiler. /// Since the Swift stdlib is also imported in the `swift` namespace, the two /// types would conflict. Move the compiler's OptionSet into a sub-namespace /// to avoid collisions. Below we do `using namespace optionset`, which makes /// the C++ `OptionSet` type available everywhere the `swift` namespace is used. namespace optionset { /// 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. constexpr OptionSet() : Storage() {} /// Create an empty option set. constexpr OptionSet(std::nullopt_t) : Storage() {} /// Create an option set with only the given option set. constexpr OptionSet(Flags flag) : Storage(static_cast(flag)) {} /// Create an option set containing the given options. constexpr OptionSet(std::initializer_list flags) : Storage(combineFlags(flags)) {} /// Create an option set from raw storage. explicit constexpr OptionSet(StorageType storage) : Storage(storage) {} /// Check whether an option set is non-empty. explicit constexpr operator bool() const { return Storage != 0; } /// Explicitly convert an option set to its underlying storage. explicit constexpr 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 constexpr 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. constexpr bool contains(OptionSet set) const { return !static_cast(set - *this); } /// Check if this option set contains the exact same options as the given set. constexpr bool containsOnly(OptionSet set) const { return Storage == set.Storage; } // '==' and '!=' are deliberately not defined because they provide a pitfall // where someone might use '==' but really want 'contains'. If you actually // want '==' behavior, use 'containsOnly'. /// Produce the union of two option sets. friend constexpr OptionSet operator|(OptionSet lhs, OptionSet rhs) { return OptionSet(lhs.Storage | rhs.Storage); } /// Produce the union of two option sets. friend constexpr OptionSet &operator|=(OptionSet &lhs, OptionSet rhs) { lhs.Storage |= rhs.Storage; return lhs; } /// Produce the intersection of two option sets. friend constexpr OptionSet operator&(OptionSet lhs, OptionSet rhs) { return OptionSet(lhs.Storage & rhs.Storage); } /// Produce the intersection of two option sets. friend constexpr OptionSet &operator&=(OptionSet &lhs, OptionSet rhs) { lhs.Storage &= rhs.Storage; return lhs; } /// Produce the difference of two option sets. friend constexpr OptionSet operator-(OptionSet lhs, OptionSet rhs) { return OptionSet(lhs.Storage & ~rhs.Storage); } /// Produce the difference of two option sets. friend constexpr OptionSet &operator-=(OptionSet &lhs, OptionSet rhs) { lhs.Storage &= ~rhs.Storage; return lhs; } private: template static auto _checkResultTypeOperatorOr(T t) -> decltype(t | t) { return T(); } static void _checkResultTypeOperatorOr(...) {} static constexpr StorageType combineFlags(const std::initializer_list &flags) { OptionSet result; for (Flags flag : flags) result |= flag; return result.Storage; } static_assert(!std::is_same::value, "operator| should produce an OptionSet"); }; } // end namespace optionset using namespace optionset; } // end namespace swift #endif // SWIFT_BASIC_OPTIONSET_H