diff options
author | Robin Burchell <robin+git@viroteck.net> | 2019-07-30 19:45:40 +0200 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-07-31 09:06:39 +0200 |
commit | 28362fcc57835bffc91f66585e17b25890509da9 (patch) | |
tree | cec5cc4dd97d046f9bbb8f758ff2f9ef3e1b6d1a /AK | |
parent | 993ab84a0d07591352f7a02ba0c2b3269b44ca1e (diff) | |
download | serenity-28362fcc57835bffc91f66585e17b25890509da9.zip |
Optional: Add consumable checks
These will, when building with clang, prevent using Optional::value
without first checking Optional::has_value() - at compile time.
Diffstat (limited to 'AK')
-rw-r--r-- | AK/Optional.h | 31 |
1 files changed, 24 insertions, 7 deletions
diff --git a/AK/Optional.h b/AK/Optional.h index 9af81ac13e..ee6320925e 100644 --- a/AK/Optional.h +++ b/AK/Optional.h @@ -1,12 +1,15 @@ #pragma once #include <AK/Assertions.h> +#include <AK/Platform.h> template<typename T> -class alignas(T) Optional { +class CONSUMABLE(unknown) alignas(T) Optional { public: + RETURN_TYPESTATE(unknown) Optional() {} + RETURN_TYPESTATE(unknown) Optional(T&& value) : m_has_value(true) { @@ -14,29 +17,33 @@ public: } template<typename U> + RETURN_TYPESTATE(unknown) Optional(U&& value) : m_has_value(true) { new (&m_storage) T(move(value)); } + RETURN_TYPESTATE(unknown) Optional(Optional&& other) : m_has_value(other.m_has_value) { if (m_has_value) { - new (&m_storage) T(move(other.value())); + new (&m_storage) T(move(other.value_without_consume_state())); other.m_has_value = false; } } + RETURN_TYPESTATE(unknown) Optional(const Optional& other) : m_has_value(other.m_has_value) { if (m_has_value) { - new (&m_storage) T(other.value()); + new (&m_storage) T(other.value_without_consume_state()); } } + RETURN_TYPESTATE(unknown) Optional& operator=(const Optional& other) { if (this != &other) { @@ -49,6 +56,7 @@ public: return *this; } + RETURN_TYPESTATE(unknown) Optional& operator=(Optional&& other) { if (this != &other) { @@ -74,20 +82,23 @@ public: } } + SET_TYPESTATE(consumed) bool has_value() const { return m_has_value; } + CALLABLE_WHEN(consumed) T& value() { ASSERT(m_has_value); return *reinterpret_cast<T*>(&m_storage); } + CALLABLE_WHEN(consumed) const T& value() const { - ASSERT(m_has_value); - return *reinterpret_cast<const T*>(&m_storage); + return value_without_consume_state(); } + CALLABLE_WHEN(consumed) T release_value() { ASSERT(m_has_value); @@ -98,14 +109,20 @@ public: T value_or(const T& fallback) const { - if (has_value()) + if (m_has_value) return value(); return fallback; } - operator bool() const { return has_value(); } + operator bool() const { return m_has_value; } private: + // Call when we don't want to alter the consume state + const T& value_without_consume_state() const + { + ASSERT(m_has_value); + return *reinterpret_cast<const T*>(&m_storage); + } char m_storage[sizeof(T)]; bool m_has_value { false }; }; |