Remove undefined behaviour (#117)

* Add assertions for cases where the API documents undefined behaviour.
This affects operator->, operator*, and error() on a tl::expected
instance. The performance impact is likely negligible for typical uses.
refs #113

Signed-off-by: Daira Hopwood <daira@jacaranda.org>

* Add tests for assertions.

Signed-off-by: Daira Hopwood <daira@jacaranda.org>

* Add documentation for assertion behaviour.

Signed-off-by: Daira Hopwood <daira@jacaranda.org>

---------

Signed-off-by: Daira Hopwood <daira@jacaranda.org>
This commit is contained in:
Daira Hopwood
2023-02-15 15:53:21 +00:00
committed by GitHub
parent b74fecd444
commit 79a2068c9a
3 changed files with 53 additions and 6 deletions
+33 -6
View File
@@ -25,6 +25,11 @@
#include <type_traits>
#include <utility>
#if !defined(TL_ASSERT)
#include <cassert>
#define TL_ASSERT assert
#endif
#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
#define TL_EXPECTED_EXCEPTIONS_ENABLED
#endif
@@ -1890,27 +1895,37 @@ public:
}
}
constexpr const T *operator->() const { return valptr(); }
TL_EXPECTED_11_CONSTEXPR T *operator->() { return valptr(); }
constexpr const T *operator->() const {
TL_ASSERT(has_value());
return valptr();
}
TL_EXPECTED_11_CONSTEXPR T *operator->() {
TL_ASSERT(has_value());
return valptr();
}
template <class U = T,
detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
constexpr const U &operator*() const & {
TL_ASSERT(has_value());
return val();
}
template <class U = T,
detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
TL_EXPECTED_11_CONSTEXPR U &operator*() & {
TL_ASSERT(has_value());
return val();
}
template <class U = T,
detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
constexpr const U &&operator*() const && {
TL_ASSERT(has_value());
return std::move(val());
}
template <class U = T,
detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
TL_EXPECTED_11_CONSTEXPR U &&operator*() && {
TL_ASSERT(has_value());
return std::move(val());
}
@@ -1946,10 +1961,22 @@ public:
return std::move(val());
}
constexpr const E &error() const & { return err().value(); }
TL_EXPECTED_11_CONSTEXPR E &error() & { return err().value(); }
constexpr const E &&error() const && { return std::move(err().value()); }
TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(err().value()); }
constexpr const E &error() const & {
TL_ASSERT(!has_value());
return err().value();
}
TL_EXPECTED_11_CONSTEXPR E &error() & {
TL_ASSERT(!has_value());
return err().value();
}
constexpr const E &&error() const && {
TL_ASSERT(!has_value());
return std::move(err().value());
}
TL_EXPECTED_11_CONSTEXPR E &&error() && {
TL_ASSERT(!has_value());
return std::move(err().value());
}
template <class U> constexpr T value_or(U &&v) const & {
static_assert(std::is_copy_constructible<T>::value &&