diff --git a/AK/ArbitrarySizedEnum.h b/AK/ArbitrarySizedEnum.h index 3e740866124..d176522fa8b 100644 --- a/AK/ArbitrarySizedEnum.h +++ b/AK/ArbitrarySizedEnum.h @@ -113,13 +113,12 @@ struct ArbitrarySizedEnum : public T { } }; -#define AK_MAKE_ARBITRARY_SIZED_ENUM(EnumName, T, ...) \ - namespace EnumName { \ - using EnumName = ArbitrarySizedEnum>; \ - using Type = EnumName; \ - using UnderlyingType = T; \ - inline constexpr static EnumName __VA_ARGS__; \ +#define AK_MAKE_ARBITRARY_SIZED_ENUM(EnumName, T, ...) \ + namespace EnumName { \ + using EnumName = ArbitrarySizedEnum>; \ + using Type = EnumName; \ + using UnderlyingType = T; \ + inline constexpr static EnumName __VA_ARGS__; \ } } diff --git a/AK/DistinctNumeric.h b/AK/DistinctNumeric.h index 38156f57d3c..13ae37d5b56 100644 --- a/AK/DistinctNumeric.h +++ b/AK/DistinctNumeric.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2020, Ben Wiederhake * Copyright (c) 2021, Andreas Kling + * Copyright (c) 2022, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ @@ -19,20 +20,19 @@ namespace AK { * want simply gets different values for `fn_length` and `line`. The macros * `TYPEDEF_DISTINCT_NUMERIC_*()` at the bottom of `DistinctNumeric.h`. * - * `Incr`, `Cmp`, `Bool`, `Flags`, `Shift`, and `Arith` simply split up the - * space of operators into 6 simple categories: + * The tags in `DistinctNumericFeature` simply split up the space of operators into 6 simple categories: * - No matter the values of these, `DistinctNumeric` always implements `==` and `!=`. - * - If `Incr` is true, then `++a`, `a++`, `--a`, and `a--` are implemented. - * - If `Cmp` is true, then `a>b`, `a=b`, and `a<=b` are implemented. - * - If `Bool` is true, then `!a`, `a&&b`, and `a||b` are implemented (but not `operator bool()`, because of overzealous integer promotion rules). - * - If `Flags` is true, then `~a`, `a&b`, `a|b`, `a^b`, `a&=b`, `a|=b`, and `a^=b` are implemented. - * - If `Shift` is true, then `a<>b`, `a<<=b`, `a>>=b` are implemented. - * - If `Arith` is true, then `a+b`, `a-b`, `+a`, `-a`, `a*b`, `a/b`, `a%b`, and the respective `a_=b` versions are implemented. + * - If `Arithmetic` is present, then `a+b`, `a-b`, `+a`, `-a`, `a*b`, `a/b`, `a%b`, and the respective `a_=b` versions are implemented. + * - If `CastToBool` is present, then `!a`, `a&&b`, and `a||b` are implemented (but not `operator bool()`, because of overzealous integer promotion rules). + * - If `Comparison` is present, then `a>b`, `a=b`, and `a<=b` are implemented. + * - If `Flags` is present, then `~a`, `a&b`, `a|b`, `a^b`, `a&=b`, `a|=b`, and `a^=b` are implemented. + * - If `Increment` is present, then `++a`, `a++`, `--a`, and `a--` are implemented. + * - If `Shift` is present, then `a<>b`, `a<<=b`, `a>>=b` are implemented. * The semantics are always those of the underlying basic type `T`. * * These can be combined arbitrarily. Want a numeric type that supports `++a` * and `a >> b` but not `a > b`? Sure thing, just set - * `Incr=true, Cmp=false, Shift=true` and you're done! + * `Increment, Comparison, Shift` and you're done! * Furthermore, some of these overloads make more sense with specific types, like `a&&b` which should be able to operate * * I intentionally decided against overloading `&a` because these shall remain @@ -47,9 +47,46 @@ namespace AK { * There are many more operators that do not make sense for numerical types, * or cannot be overloaded in the first place. Naturally, they are not implemented. */ -template + +namespace DistinctNumericFeature { +enum Arithmetic {}; +enum CastToBool {}; +enum Comparison {}; +enum Flags {}; +enum Increment {}; +enum Shift {}; +}; + +template class DistinctNumeric { - using Self = DistinctNumeric; + using Self = DistinctNumeric; + + struct Option { + template + consteval Option(K option, Os... other_options) + : Option(other_options...) + { + set(option); + } + + consteval Option() { } + + constexpr void set(DistinctNumericFeature::Arithmetic const&) { arithmetic = true; } + constexpr void set(DistinctNumericFeature::CastToBool const&) { cast_to_bool = true; } + constexpr void set(DistinctNumericFeature::Comparison const&) { comparisons = true; } + constexpr void set(DistinctNumericFeature::Flags const&) { flags = true; } + constexpr void set(DistinctNumericFeature::Increment const&) { increment = true; } + constexpr void set(DistinctNumericFeature::Shift const&) { shift = true; } + + bool arithmetic { false }; + bool cast_to_bool { false }; + bool comparisons { false }; + bool flags { false }; + bool increment { false }; + bool shift { false }; + }; + + constexpr static Option options { Opts()... }; public: constexpr DistinctNumeric() = default; @@ -59,7 +96,7 @@ public: { } - constexpr const T& value() const { return m_value; } + constexpr T const& value() const { return m_value; } constexpr T& value() { return m_value; } // Always implemented: identity. @@ -68,61 +105,61 @@ public: return this->m_value == other.m_value; } - // Only implemented when `Incr` is true: + // Only implemented when `Increment` is true: constexpr Self& operator++() { - static_assert(Incr, "'++a' is only available for DistinctNumeric types with 'Incr'."); + static_assert(options.increment, "'++a' is only available for DistinctNumeric types with 'Increment'."); this->m_value += 1; return *this; } constexpr Self operator++(int) { - static_assert(Incr, "'a++' is only available for DistinctNumeric types with 'Incr'."); + static_assert(options.increment, "'a++' is only available for DistinctNumeric types with 'Increment'."); Self ret = this->m_value; this->m_value += 1; return ret; } constexpr Self& operator--() { - static_assert(Incr, "'--a' is only available for DistinctNumeric types with 'Incr'."); + static_assert(options.increment, "'--a' is only available for DistinctNumeric types with 'Increment'."); this->m_value -= 1; return *this; } constexpr Self operator--(int) { - static_assert(Incr, "'a--' is only available for DistinctNumeric types with 'Incr'."); + static_assert(options.increment, "'a--' is only available for DistinctNumeric types with 'Increment'."); Self ret = this->m_value; this->m_value -= 1; return ret; } - // Only implemented when `Cmp` is true: + // Only implemented when `Comparison` is true: constexpr bool operator>(Self const& other) const { - static_assert(Cmp, "'a>b' is only available for DistinctNumeric types with 'Cmp'."); + static_assert(options.comparisons, "'a>b' is only available for DistinctNumeric types with 'Comparison'."); return this->m_value > other.m_value; } constexpr bool operator<(Self const& other) const { - static_assert(Cmp, "'am_value < other.m_value; } constexpr bool operator>=(Self const& other) const { - static_assert(Cmp, "'a>=b' is only available for DistinctNumeric types with 'Cmp'."); + static_assert(options.comparisons, "'a>=b' is only available for DistinctNumeric types with 'Comparison'."); return this->m_value >= other.m_value; } constexpr bool operator<=(Self const& other) const { - static_assert(Cmp, "'a<=b' is only available for DistinctNumeric types with 'Cmp'."); + static_assert(options.comparisons, "'a<=b' is only available for DistinctNumeric types with 'Comparison'."); return this->m_value <= other.m_value; } // 'operator<=>' cannot be implemented. See class comment. - // Only implemented when `bool` is true: + // Only implemented when `CastToBool` is true: constexpr bool operator!() const { - static_assert(Bool, "'!a' is only available for DistinctNumeric types with 'Bool'."); + static_assert(options.cast_to_bool, "'!a' is only available for DistinctNumeric types with 'CastToBool'."); return !this->m_value; } // Intentionally don't define `operator bool() const` here. C++ is a bit @@ -133,39 +170,39 @@ public: // Only implemented when `Flags` is true: constexpr Self operator~() const { - static_assert(Flags, "'~a' is only available for DistinctNumeric types with 'Flags'."); + static_assert(options.flags, "'~a' is only available for DistinctNumeric types with 'Flags'."); return ~this->m_value; } constexpr Self operator&(Self const& other) const { - static_assert(Flags, "'a&b' is only available for DistinctNumeric types with 'Flags'."); + static_assert(options.flags, "'a&b' is only available for DistinctNumeric types with 'Flags'."); return this->m_value & other.m_value; } constexpr Self operator|(Self const& other) const { - static_assert(Flags, "'a|b' is only available for DistinctNumeric types with 'Flags'."); + static_assert(options.flags, "'a|b' is only available for DistinctNumeric types with 'Flags'."); return this->m_value | other.m_value; } constexpr Self operator^(Self const& other) const { - static_assert(Flags, "'a^b' is only available for DistinctNumeric types with 'Flags'."); + static_assert(options.flags, "'a^b' is only available for DistinctNumeric types with 'Flags'."); return this->m_value ^ other.m_value; } constexpr Self& operator&=(Self const& other) { - static_assert(Flags, "'a&=b' is only available for DistinctNumeric types with 'Flags'."); + static_assert(options.flags, "'a&=b' is only available for DistinctNumeric types with 'Flags'."); this->m_value &= other.m_value; return *this; } constexpr Self& operator|=(Self const& other) { - static_assert(Flags, "'a|=b' is only available for DistinctNumeric types with 'Flags'."); + static_assert(options.flags, "'a|=b' is only available for DistinctNumeric types with 'Flags'."); this->m_value |= other.m_value; return *this; } constexpr Self& operator^=(Self const& other) { - static_assert(Flags, "'a^=b' is only available for DistinctNumeric types with 'Flags'."); + static_assert(options.flags, "'a^=b' is only available for DistinctNumeric types with 'Flags'."); this->m_value ^= other.m_value; return *this; } @@ -174,90 +211,90 @@ public: // TODO: Should this take `int` instead? constexpr Self operator<<(Self const& other) const { - static_assert(Shift, "'a<m_value << other.m_value; } constexpr Self operator>>(Self const& other) const { - static_assert(Shift, "'a>>b' is only available for DistinctNumeric types with 'Shift'."); + static_assert(options.shift, "'a>>b' is only available for DistinctNumeric types with 'Shift'."); return this->m_value >> other.m_value; } constexpr Self& operator<<=(Self const& other) { - static_assert(Shift, "'a<<=b' is only available for DistinctNumeric types with 'Shift'."); + static_assert(options.shift, "'a<<=b' is only available for DistinctNumeric types with 'Shift'."); this->m_value <<= other.m_value; return *this; } constexpr Self& operator>>=(Self const& other) { - static_assert(Shift, "'a>>=b' is only available for DistinctNumeric types with 'Shift'."); + static_assert(options.shift, "'a>>=b' is only available for DistinctNumeric types with 'Shift'."); this->m_value >>= other.m_value; return *this; } - // Only implemented when `Arith` is true: + // Only implemented when `Arithmetic` is true: constexpr Self operator+(Self const& other) const { - static_assert(Arith, "'a+b' is only available for DistinctNumeric types with 'Arith'."); + static_assert(options.arithmetic, "'a+b' is only available for DistinctNumeric types with 'Arithmetic'."); return this->m_value + other.m_value; } constexpr Self operator-(Self const& other) const { - static_assert(Arith, "'a-b' is only available for DistinctNumeric types with 'Arith'."); + static_assert(options.arithmetic, "'a-b' is only available for DistinctNumeric types with 'Arithmetic'."); return this->m_value - other.m_value; } constexpr Self operator+() const { - static_assert(Arith, "'+a' is only available for DistinctNumeric types with 'Arith'."); + static_assert(options.arithmetic, "'+a' is only available for DistinctNumeric types with 'Arithmetic'."); return +this->m_value; } constexpr Self operator-() const { - static_assert(Arith, "'-a' is only available for DistinctNumeric types with 'Arith'."); + static_assert(options.arithmetic, "'-a' is only available for DistinctNumeric types with 'Arithmetic'."); return -this->m_value; } constexpr Self operator*(Self const& other) const { - static_assert(Arith, "'a*b' is only available for DistinctNumeric types with 'Arith'."); + static_assert(options.arithmetic, "'a*b' is only available for DistinctNumeric types with 'Arithmetic'."); return this->m_value * other.m_value; } constexpr Self operator/(Self const& other) const { - static_assert(Arith, "'a/b' is only available for DistinctNumeric types with 'Arith'."); + static_assert(options.arithmetic, "'a/b' is only available for DistinctNumeric types with 'Arithmetic'."); return this->m_value / other.m_value; } constexpr Self operator%(Self const& other) const { - static_assert(Arith, "'a%b' is only available for DistinctNumeric types with 'Arith'."); + static_assert(options.arithmetic, "'a%b' is only available for DistinctNumeric types with 'Arithmetic'."); return this->m_value % other.m_value; } constexpr Self& operator+=(Self const& other) { - static_assert(Arith, "'a+=b' is only available for DistinctNumeric types with 'Arith'."); + static_assert(options.arithmetic, "'a+=b' is only available for DistinctNumeric types with 'Arithmetic'."); this->m_value += other.m_value; return *this; } constexpr Self& operator-=(Self const& other) { - static_assert(Arith, "'a+=b' is only available for DistinctNumeric types with 'Arith'."); + static_assert(options.arithmetic, "'a+=b' is only available for DistinctNumeric types with 'Arithmetic'."); this->m_value += other.m_value; return *this; } constexpr Self& operator*=(Self const& other) { - static_assert(Arith, "'a*=b' is only available for DistinctNumeric types with 'Arith'."); + static_assert(options.arithmetic, "'a*=b' is only available for DistinctNumeric types with 'Arithmetic'."); this->m_value *= other.m_value; return *this; } constexpr Self& operator/=(Self const& other) { - static_assert(Arith, "'a/=b' is only available for DistinctNumeric types with 'Arith'."); + static_assert(options.arithmetic, "'a/=b' is only available for DistinctNumeric types with 'Arithmetic'."); this->m_value /= other.m_value; return *this; } constexpr Self& operator%=(Self const& other) { - static_assert(Arith, "'a%=b' is only available for DistinctNumeric types with 'Arith'."); + static_assert(options.arithmetic, "'a%=b' is only available for DistinctNumeric types with 'Arithmetic'."); this->m_value %= other.m_value; return *this; } @@ -266,29 +303,34 @@ private: T m_value {}; }; -template -struct Formatter> : Formatter { - ErrorOr format(FormatBuilder& builder, DistinctNumeric value) +template +struct Formatter> : Formatter { + ErrorOr format(FormatBuilder& builder, DistinctNumeric value) { return Formatter::format(builder, value.value()); } }; - -// TODO: When 'consteval' sufficiently-well supported by host compilers, try to -// provide a more usable interface like this one: -// https://gist.github.com/alimpfard/a3b750e8c3a2f44fb3a2d32038968ddf - } -#define AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(T, Incr, Cmp, Bool, Flags, Shift, Arith, NAME) \ - using NAME = DistinctNumeric; -#define AK_TYPEDEF_DISTINCT_ORDERED_ID(T, NAME) AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(T, false, true, true, false, false, false, NAME) +#define AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(T, NAME, ...) \ + struct NAME##_decl { \ + using Arithmetic [[maybe_unused]] = AK::DistinctNumericFeature::Arithmetic; \ + using CastToBool [[maybe_unused]] = AK::DistinctNumericFeature::CastToBool; \ + using Comparison [[maybe_unused]] = AK::DistinctNumericFeature::Comparison; \ + using Flags [[maybe_unused]] = AK::DistinctNumericFeature::Flags; \ + using Increment [[maybe_unused]] = AK::DistinctNumericFeature::Increment; \ + using Shift [[maybe_unused]] = AK::DistinctNumericFeature::Shift; \ + using NAME [[maybe_unused]] = DistinctNumeric; \ + }; \ + using NAME = typename NAME##_decl::NAME; + +#define AK_TYPEDEF_DISTINCT_ORDERED_ID(T, NAME) AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(T, NAME, Comparison, CastToBool) // TODO: Further type aliases? -template -struct Traits> : public GenericTraits> { +template +struct Traits> : public GenericTraits> { static constexpr bool is_trivial() { return true; } - static constexpr auto hash(DistinctNumeric const& d) { return Traits::hash(d.value()); } + static constexpr auto hash(DistinctNumeric const& d) { return Traits::hash(d.value()); } }; using AK::DistinctNumeric; diff --git a/Tests/AK/TestDistinctNumeric.cpp b/Tests/AK/TestDistinctNumeric.cpp index 4399e19411e..a00c286b700 100644 --- a/Tests/AK/TestDistinctNumeric.cpp +++ b/Tests/AK/TestDistinctNumeric.cpp @@ -13,7 +13,7 @@ class ForType { public: static void check_size() { - AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(T, false, false, false, false, false, false, TheNumeric); + AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(T, TheNumeric); EXPECT_EQ(sizeof(T), sizeof(TheNumeric)); } }; @@ -34,14 +34,14 @@ TEST_CASE(check_size) ForType::check_size(); } -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, false, false, false, false, false, false, BareNumeric); -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, true, false, false, false, false, false, IncrNumeric); -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, false, true, false, false, false, false, CmpNumeric); -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, false, false, true, false, false, false, BoolNumeric); -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, false, false, false, true, false, false, FlagsNumeric); -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, false, false, false, false, true, false, ShiftNumeric); -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, false, false, false, false, false, true, ArithNumeric); -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, true, true, true, true, true, true, GeneralNumeric); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, BareNumeric); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, IncrNumeric, Increment); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, CmpNumeric, Comparison); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, BoolNumeric, CastToBool); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, FlagsNumeric, Flags); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, ShiftNumeric, Shift); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, ArithNumeric, Arithmetic); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, GeneralNumeric, Arithmetic, CastToBool, Comparison, Flags, Increment, Shift); TEST_CASE(address_identity) { @@ -230,21 +230,21 @@ TEST_CASE(negative_incr) { BareNumeric a = 12; a++; - // error: static assertion failed: 'a++' is only available for DistinctNumeric types with 'Incr'. + // error: static assertion failed: 'a++' is only available for DistinctNumeric types with 'Increment'. } TEST_CASE(negative_cmp) { BareNumeric a = 12; [[maybe_unused]] auto res = (a < a); - // error: static assertion failed: 'a other_errors; }; -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, false, false, true, FunctionAddress); -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, false, false, true, ExternAddress); -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, false, false, true, TableAddress); -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, false, false, true, GlobalAddress); -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, false, false, true, ElementAddress); -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, false, false, true, DataAddress); -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, false, false, true, MemoryAddress); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, FunctionAddress, Arithmetic, Comparison, Increment); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, ExternAddress, Arithmetic, Comparison, Increment); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, TableAddress, Arithmetic, Comparison, Increment); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, GlobalAddress, Arithmetic, Comparison, Increment); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, ElementAddress, Arithmetic, Comparison, Increment); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, DataAddress, Arithmetic, Comparison, Increment); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, MemoryAddress, Arithmetic, Comparison, Increment); // FIXME: These should probably be made generic/virtual if/when we decide to do something more // fancy than just a dumb interpreter. diff --git a/Userland/Libraries/LibWasm/Types.h b/Userland/Libraries/LibWasm/Types.h index 3725cb0425d..0b021740bba 100644 --- a/Userland/Libraries/LibWasm/Types.h +++ b/Userland/Libraries/LibWasm/Types.h @@ -57,7 +57,7 @@ AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, LocalIndex); AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, GlobalIndex); AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, LabelIndex); AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, DataIndex); -AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, true, false, true, InstructionPointer); +AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, InstructionPointer, Arithmetic, Comparison, Flags, Increment); ParseError with_eof_check(InputStream const& stream, ParseError error_if_not_eof);