diff --git a/AK/Checked.h b/AK/Checked.h index 75e8623ea67..ae8b1517d96 100644 --- a/AK/Checked.h +++ b/AK/Checked.h @@ -112,111 +112,111 @@ inline constexpr bool is_within_range(Source value) template class Checked { public: - Checked() = default; + constexpr Checked() = default; - Checked(T value) + constexpr Checked(T value) : m_value(value) { } template - Checked(U value) + constexpr Checked(U value) { m_overflow = !is_within_range(value); m_value = value; } - Checked(const Checked&) = default; + constexpr Checked(const Checked&) = default; - Checked(Checked&& other) + constexpr Checked(Checked&& other) : m_value(exchange(other.m_value, 0)) , m_overflow(exchange(other.m_overflow, false)) { } template - Checked& operator=(U value) + constexpr Checked& operator=(U value) { return *this = Checked(value); } - Checked& operator=(const Checked& other) = default; + constexpr Checked& operator=(const Checked& other) = default; - Checked& operator=(Checked&& other) + constexpr Checked& operator=(Checked&& other) { m_value = exchange(other.m_value, 0); m_overflow = exchange(other.m_overflow, false); return *this; } - bool has_overflow() const + constexpr bool has_overflow() const { return m_overflow; } - ALWAYS_INLINE bool operator!() const + ALWAYS_INLINE constexpr bool operator!() const { ASSERT(!m_overflow); return !m_value; } - ALWAYS_INLINE T value() const + ALWAYS_INLINE constexpr T value() const { ASSERT(!m_overflow); return m_value; } - void add(T other) + constexpr void add(T other) { m_overflow |= __builtin_add_overflow(m_value, other, &m_value); } - void sub(T other) + constexpr void sub(T other) { m_overflow |= __builtin_sub_overflow(m_value, other, &m_value); } - void mul(T other) + constexpr void mul(T other) { m_overflow |= __builtin_mul_overflow(m_value, other, &m_value); } - void div(T other) + constexpr void div(T other) { m_value /= other; } - Checked& operator+=(T other) + constexpr Checked& operator+=(T other) { add(other); return *this; } - Checked& operator-=(T other) + constexpr Checked& operator-=(T other) { sub(other); return *this; } - Checked& operator*=(T other) + constexpr Checked& operator*=(T other) { mul(other); return *this; } - Checked& operator/=(T other) + constexpr Checked& operator/=(T other) { div(other); return *this; } - Checked& operator++() + constexpr Checked& operator++() { add(1); return *this; } - Checked operator++(int) + constexpr Checked operator++(int) { Checked old { *this }; add(1); @@ -224,7 +224,7 @@ public: } template - static bool addition_would_overflow(U u, V v) + static constexpr bool addition_would_overflow(U u, V v) { #ifdef __clang__ Checked checked; @@ -237,7 +237,7 @@ public: } template - static bool multiplication_would_overflow(U u, V v) + static constexpr bool multiplication_would_overflow(U u, V v) { #ifdef __clang__ Checked checked; @@ -250,7 +250,7 @@ public: } template - static bool multiplication_would_overflow(U u, V v, X x) + static constexpr bool multiplication_would_overflow(U u, V v, X x) { Checked checked; checked = u; @@ -265,7 +265,7 @@ private: }; template -inline Checked operator+(const Checked& a, const Checked& b) +constexpr Checked operator+(const Checked& a, const Checked& b) { Checked c { a }; c.add(b.value()); @@ -273,7 +273,7 @@ inline Checked operator+(const Checked& a, const Checked& b) } template -inline Checked operator-(const Checked& a, const Checked& b) +constexpr Checked operator-(const Checked& a, const Checked& b) { Checked c { a }; c.sub(b.value()); @@ -281,7 +281,7 @@ inline Checked operator-(const Checked& a, const Checked& b) } template -inline Checked operator*(const Checked& a, const Checked& b) +constexpr Checked operator*(const Checked& a, const Checked& b) { Checked c { a }; c.mul(b.value()); @@ -289,7 +289,7 @@ inline Checked operator*(const Checked& a, const Checked& b) } template -inline Checked operator/(const Checked& a, const Checked& b) +constexpr inline Checked operator/(const Checked& a, const Checked& b) { Checked c { a }; c.div(b.value()); @@ -297,79 +297,79 @@ inline Checked operator/(const Checked& a, const Checked& b) } template -inline bool operator<(const Checked& a, T b) +constexpr bool operator<(const Checked& a, T b) { return a.value() < b; } template -inline bool operator>(const Checked& a, T b) +constexpr bool operator>(const Checked& a, T b) { return a.value() > b; } template -inline bool operator>=(const Checked& a, T b) +constexpr bool operator>=(const Checked& a, T b) { return a.value() >= b; } template -inline bool operator<=(const Checked& a, T b) +constexpr bool operator<=(const Checked& a, T b) { return a.value() <= b; } template -inline bool operator==(const Checked& a, T b) +constexpr bool operator==(const Checked& a, T b) { return a.value() == b; } template -inline bool operator!=(const Checked& a, T b) +constexpr bool operator!=(const Checked& a, T b) { return a.value() != b; } template -inline bool operator<(T a, const Checked& b) +constexpr bool operator<(T a, const Checked& b) { return a < b.value(); } template -inline bool operator>(T a, const Checked& b) +constexpr bool operator>(T a, const Checked& b) { return a > b.value(); } template -inline bool operator>=(T a, const Checked& b) +constexpr bool operator>=(T a, const Checked& b) { return a >= b.value(); } template -inline bool operator<=(T a, const Checked& b) +constexpr bool operator<=(T a, const Checked& b) { return a <= b.value(); } template -inline bool operator==(T a, const Checked& b) +constexpr bool operator==(T a, const Checked& b) { return a == b.value(); } template -inline bool operator!=(T a, const Checked& b) +constexpr bool operator!=(T a, const Checked& b) { return a != b.value(); } template -inline Checked make_checked(T value) +constexpr Checked make_checked(T value) { return Checked(value); } diff --git a/AK/StdLibExtras.h b/AK/StdLibExtras.h index e8e02703ee5..02032ea1e2c 100644 --- a/AK/StdLibExtras.h +++ b/AK/StdLibExtras.h @@ -82,7 +82,7 @@ inline constexpr T ceil_div(T a, U b) # pragma clang diagnostic ignored "-Wconsumed" #endif template -inline T&& move(T& arg) +constexpr T&& move(T& arg) { return static_cast(arg); } @@ -433,7 +433,7 @@ struct IsConst : TrueType { }; template -inline constexpr T exchange(T& slot, U&& value) +constexpr T exchange(T& slot, U&& value) { T old_value = move(slot); slot = forward(value); diff --git a/AK/Tests/TestChecked.cpp b/AK/Tests/TestChecked.cpp index e1a7a234a3a..20ea0032dca 100644 --- a/AK/Tests/TestChecked.cpp +++ b/AK/Tests/TestChecked.cpp @@ -27,6 +27,7 @@ #include #include +#include // These tests only check whether the usual operator semantics work. // TODO: Add tests about the actual `Check`ing itself! @@ -94,4 +95,272 @@ TEST_CASE(operator_arith) EXPECT_EQ(b / a, 28); } +TEST_CASE(should_constexpr_default_construct) +{ + constexpr Checked checked_value {}; + static_assert(!checked_value.has_overflow()); + static_assert(checked_value == int {}); +} + +TEST_CASE(should_constexpr_value_construct) +{ + constexpr Checked checked_value { 42 }; + static_assert(!checked_value.has_overflow()); + static_assert(checked_value == 42); +} + +TEST_CASE(should_constexpr_convert_construct) +{ + constexpr Checked checked_value { 42u }; + static_assert(!checked_value.has_overflow()); + static_assert(checked_value == 42); +} + +TEST_CASE(should_constexpr_copy_construct) +{ + constexpr auto checked_value = [] { + const Checked old_value { 42 }; + Checked value(old_value); + return value; + }(); + static_assert(!checked_value.has_overflow()); + static_assert(checked_value == 42); +} + +TEST_CASE(should_constexpr_move_construct) +{ + constexpr auto checked_value = [] { + Checked value(Checked { 42 }); + return value; + }(); + static_assert(!checked_value.has_overflow()); + static_assert(checked_value == 42); +} + +TEST_CASE(should_constexpr_copy_assign) +{ + constexpr auto checked_value = [] { + const Checked old_value { 42 }; + Checked value {}; + value = old_value; + return value; + }(); + static_assert(!checked_value.has_overflow()); + static_assert(checked_value == 42); +} + +TEST_CASE(should_constexpr_move_assign) +{ + constexpr auto checked_value = [] { + Checked value {}; + value = Checked { 42 }; + return value; + }(); + static_assert(!checked_value.has_overflow()); + static_assert(checked_value == 42); +} + +TEST_CASE(should_constexpr_convert_and_assign) +{ + constexpr auto checked_value = [] { + Checked value {}; + value = 42; + return value; + }(); + static_assert(!checked_value.has_overflow()); + static_assert(checked_value == 42); +} + +TEST_CASE(should_constexpr_not_operator) +{ + constexpr Checked value {}; + static_assert(!value); +} + +TEST_CASE(should_constexpr_value_accessor) +{ + constexpr Checked value { 42 }; + static_assert(value.value() == 42); +} + +TEST_CASE(should_constexpr_add) +{ + constexpr auto checked_value = [] { + Checked value { 42 }; + value.add(3); + return value; + }(); + static_assert(checked_value == 45); +} + +TEST_CASE(should_constexpr_sub) +{ + constexpr auto checked_value = [] { + Checked value { 42 }; + value.sub(3); + return value; + }(); + static_assert(checked_value == 39); +} + +TEST_CASE(should_constexpr_mul) +{ + constexpr auto checked_value = [] { + Checked value { 42 }; + value.mul(2); + return value; + }(); + static_assert(checked_value == 84); +} + +TEST_CASE(should_constexpr_div) +{ + constexpr auto checked_value = [] { + Checked value { 42 }; + value.div(3); + return value; + }(); + static_assert(checked_value == 14); +} + +TEST_CASE(should_constexpr_assignment_by_sum) +{ + constexpr auto checked_value = [] { + Checked value { 42 }; + value += 3; + return value; + }(); + static_assert(checked_value == 45); +} + +TEST_CASE(should_constexpr_assignment_by_diff) +{ + constexpr auto checked_value = [] { + Checked value { 42 }; + value -= 3; + return value; + }(); + static_assert(checked_value == 39); +} + +TEST_CASE(should_constexpr_assignment_by_product) +{ + constexpr auto checked_value = [] { + Checked value { 42 }; + value *= 2; + return value; + }(); + static_assert(checked_value == 84); +} + +TEST_CASE(should_constexpr_assignment_by_quotient) +{ + constexpr auto checked_value = [] { + Checked value { 42 }; + value /= 3; + return value; + }(); + static_assert(checked_value == 14); +} + +TEST_CASE(should_constexpr_prefix_increment) +{ + constexpr auto checked_value = [] { + Checked value { 42 }; + ++value; + return value; + }(); + static_assert(checked_value == 43); +} + +TEST_CASE(should_constexpr_postfix_increment) +{ + constexpr auto checked_value = [] { + Checked value { 42 }; + value++; + return value; + }(); + static_assert(checked_value == 43); +} + +TEST_CASE(should_constexpr_check_for_overflow_addition) +{ + static_assert(Checked::addition_would_overflow(NumericLimits::max(), 1)); +} + +TEST_CASE(should_constexpr_check_for_overflow_multiplication) +{ + static_assert(Checked::multiplication_would_overflow(NumericLimits::max(), 2)); + static_assert(Checked::multiplication_would_overflow(NumericLimits::max(), 1, 2)); +} + +TEST_CASE(should_constexpr_add_checked_values) +{ + constexpr Checked a { 42 }; + constexpr Checked b { 17 }; + constexpr Checked expected { 59 }; + static_assert(expected == (a + b).value()); +} + +TEST_CASE(should_constexpr_subtract_checked_values) +{ + constexpr Checked a { 42 }; + constexpr Checked b { 17 }; + constexpr Checked expected { 25 }; + static_assert(expected == (a - b).value()); +} + +TEST_CASE(should_constexpr_multiply_checked_values) +{ + constexpr Checked a { 3 }; + constexpr Checked b { 5 }; + constexpr Checked expected { 15 }; + static_assert(expected == (a * b).value()); +} + +TEST_CASE(should_constexpr_divide_checked_values) +{ + constexpr Checked a { 10 }; + constexpr Checked b { 2 }; + constexpr Checked expected { 5 }; + static_assert(expected == (a / b).value()); +} + +TEST_CASE(should_constexpr_compare_checked_values_lhs) +{ + constexpr Checked a { 10 }; + + static_assert(a > 5); + static_assert(a >= 10); + static_assert(a >= 5); + + static_assert(a < 20); + static_assert(a <= 30); + static_assert(a <= 20); + + static_assert(a == 10); + static_assert(a != 20); +} + +TEST_CASE(should_constexpr_compare_checked_values_rhs) +{ + constexpr Checked a { 10 }; + + static_assert(5 < a); + static_assert(10 <= a); + static_assert(5 <= a); + + static_assert(20 > a); + static_assert(30 >= a); + static_assert(30 >= a); + + static_assert(10 == a); + static_assert(20 != a); +} + +TEST_CASE(should_constexpr_make_via_factory) +{ + [[maybe_unused]] constexpr auto value = make_checked(42); +} + TEST_MAIN(Checked)