Revert "maybe_t: make maybe_t<T> trivially copyable if T is"

This reverts commit 9d303a74e3.
This reverts commit 0305c842e6.

9d303a7 broke 32-bit armhf builds for unknown reasons, specifically in
settings where a trivial copy of `maybe_t<int>` was performed. A caller
would pass a literal int in the place of a `maybe_t<int>` parameter and
the callee would see a populated `maybe_t` but with a value of `0`
rather than the actual value that was passed in. It was too painful to
debug to a resolution under qemu.
This commit is contained in:
Mahmoud Al-Qudsi 2022-10-29 10:08:48 -05:00
parent 8d7662335e
commit 1c92d4c5db

View File

@ -7,11 +7,10 @@
#include <utility>
namespace maybe_detail {
// Template magic to make maybe_t<T> (trivially) copyable iff T is.
// This is an unsafe implementation: it is always trivially copyable.
// Template magic to make maybe_t<T> copyable iff T is copyable.
// maybe_impl_t is the "too aggressive" implementation: it is always copyable.
template <typename T>
struct maybe_impl_trivially_copyable_t {
struct maybe_impl_t {
alignas(T) char storage[sizeof(T)];
bool filled = false;
@ -39,13 +38,11 @@ struct maybe_impl_trivially_copyable_t {
return res;
}
maybe_impl_trivially_copyable_t() = default;
maybe_impl_t() = default;
// Move construction/assignment from a T.
explicit maybe_impl_trivially_copyable_t(T &&v) : filled(true) {
new (storage) T(std::forward<T>(v));
}
maybe_impl_trivially_copyable_t &operator=(T &&v) {
explicit maybe_impl_t(T &&v) : filled(true) { new (storage) T(std::forward<T>(v)); }
maybe_impl_t &operator=(T &&v) {
if (filled) {
value() = std::move(v);
} else {
@ -56,8 +53,8 @@ struct maybe_impl_trivially_copyable_t {
}
// Copy construction/assignment from a T.
explicit maybe_impl_trivially_copyable_t(const T &v) : filled(true) { new (storage) T(v); }
maybe_impl_trivially_copyable_t &operator=(const T &v) {
explicit maybe_impl_t(const T &v) : filled(true) { new (storage) T(v); }
maybe_impl_t &operator=(const T &v) {
if (filled) {
value() = v;
} else {
@ -66,27 +63,15 @@ struct maybe_impl_trivially_copyable_t {
}
return *this;
}
};
// This is an unsafe implementation: it is always copyable.
template <typename T>
struct maybe_impl_not_trivially_copyable_t : public maybe_impl_trivially_copyable_t<T> {
using base_t = maybe_impl_trivially_copyable_t<T>;
using base_t::maybe_impl_trivially_copyable_t;
using base_t::operator=;
using base_t::filled;
using base_t::reset;
using base_t::storage;
// Move construction/assignment from another instance.
maybe_impl_not_trivially_copyable_t(maybe_impl_not_trivially_copyable_t &&v) {
filled = v.filled;
// Move construction/assignment from a maybe_impl.
maybe_impl_t(maybe_impl_t &&v) : filled(v.filled) {
if (filled) {
new (storage) T(std::move(v.value()));
}
}
maybe_impl_not_trivially_copyable_t &operator=(maybe_impl_not_trivially_copyable_t &&v) {
maybe_impl_t &operator=(maybe_impl_t &&v) {
if (!v.filled) {
reset();
} else {
@ -95,15 +80,14 @@ struct maybe_impl_not_trivially_copyable_t : public maybe_impl_trivially_copyabl
return *this;
}
// Copy construction/assignment from another instance.
maybe_impl_not_trivially_copyable_t(const maybe_impl_not_trivially_copyable_t &v) : base_t() {
filled = v.filled;
// Copy construction/assignment from a maybe_impl.
maybe_impl_t(const maybe_impl_t &v) : filled(v.filled) {
if (v.filled) {
new (storage) T(v.value());
}
}
maybe_impl_not_trivially_copyable_t &operator=(const maybe_impl_not_trivially_copyable_t &v) {
maybe_impl_t &operator=(const maybe_impl_t &v) {
if (&v == this) return *this;
if (!v.filled) {
reset();
@ -113,8 +97,7 @@ struct maybe_impl_not_trivially_copyable_t : public maybe_impl_trivially_copyabl
return *this;
}
maybe_impl_not_trivially_copyable_t() = default;
~maybe_impl_not_trivially_copyable_t() { reset(); }
~maybe_impl_t() { reset(); }
};
struct copyable_t {};
@ -147,15 +130,7 @@ inline constexpr none_t none() { return none_t::none; }
// This is a value-type class that stores a value of T in aligned storage.
template <typename T>
class maybe_t : private maybe_detail::conditionally_copyable_t<T> {
#if __GNUG__ && __GNUC__ < 5
using maybe_impl_t = maybe_detail::maybe_impl_not_trivially_copyable_t<T>;
#else
using maybe_impl_t =
typename std::conditional<std::is_trivially_copyable<T>::value,
maybe_detail::maybe_impl_trivially_copyable_t<T>,
maybe_detail::maybe_impl_not_trivially_copyable_t<T> >::type;
#endif
maybe_impl_t impl_;
maybe_detail::maybe_impl_t<T> impl_;
public:
// return whether the receiver contains a value.