mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-15 22:44:01 +00:00
Reintroduce trivially copyable maybe_t impl
This reverts commit 1c92d4c5db
and
reintroduces support for trivially copyable `maybe_t` impls but with a
GCC version check to disable the optimization for GNU GCC compiler
versions 9 and below.
GCC 8.3.0 armhf builds seem to have a problem with the trivially
copyable `maybe_t` impl that introduces odd heisenbugs that cause the
tests to fail. GDB reveals that `maybe_t` function parameters received
in the callee differ from what was passed-in by the caller.
This behavior appears to be (but has not been confirmed as) a
platform-specific compiler bug. Under the same system (32-bit Debian 10
armhf), compiling with clang 7.0.1 does not result in any bugs and
causes all the tests to pass while compiling with GCC 10.2 under 32-bit
Debian 11 armhf also doesn't run into any problems, so just expand the
existing GCC version check that gates support for trivially copyable
`maybe_t` impls to encompass both the troublesome GCC 8 version and the
untested GCC 9 version.
This commit is contained in:
parent
1c92d4c5db
commit
4f46abec9d
1 changed files with 41 additions and 16 deletions
57
src/maybe.h
57
src/maybe.h
|
@ -7,10 +7,11 @@
|
|||
#include <utility>
|
||||
|
||||
namespace maybe_detail {
|
||||
// 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 magic to make maybe_t<T> (trivially) copyable iff T is.
|
||||
|
||||
// This is an unsafe implementation: it is always trivially copyable.
|
||||
template <typename T>
|
||||
struct maybe_impl_t {
|
||||
struct maybe_impl_trivially_copyable_t {
|
||||
alignas(T) char storage[sizeof(T)];
|
||||
bool filled = false;
|
||||
|
||||
|
@ -38,11 +39,13 @@ struct maybe_impl_t {
|
|||
return res;
|
||||
}
|
||||
|
||||
maybe_impl_t() = default;
|
||||
maybe_impl_trivially_copyable_t() = default;
|
||||
|
||||
// Move construction/assignment from a T.
|
||||
explicit maybe_impl_t(T &&v) : filled(true) { new (storage) T(std::forward<T>(v)); }
|
||||
maybe_impl_t &operator=(T &&v) {
|
||||
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) {
|
||||
if (filled) {
|
||||
value() = std::move(v);
|
||||
} else {
|
||||
|
@ -53,8 +56,8 @@ struct maybe_impl_t {
|
|||
}
|
||||
|
||||
// Copy construction/assignment from a T.
|
||||
explicit maybe_impl_t(const T &v) : filled(true) { new (storage) T(v); }
|
||||
maybe_impl_t &operator=(const T &v) {
|
||||
explicit maybe_impl_trivially_copyable_t(const T &v) : filled(true) { new (storage) T(v); }
|
||||
maybe_impl_trivially_copyable_t &operator=(const T &v) {
|
||||
if (filled) {
|
||||
value() = v;
|
||||
} else {
|
||||
|
@ -63,15 +66,27 @@ struct maybe_impl_t {
|
|||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// Move construction/assignment from a maybe_impl.
|
||||
maybe_impl_t(maybe_impl_t &&v) : filled(v.filled) {
|
||||
// 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;
|
||||
if (filled) {
|
||||
new (storage) T(std::move(v.value()));
|
||||
}
|
||||
}
|
||||
|
||||
maybe_impl_t &operator=(maybe_impl_t &&v) {
|
||||
maybe_impl_not_trivially_copyable_t &operator=(maybe_impl_not_trivially_copyable_t &&v) {
|
||||
if (!v.filled) {
|
||||
reset();
|
||||
} else {
|
||||
|
@ -80,14 +95,15 @@ struct maybe_impl_t {
|
|||
return *this;
|
||||
}
|
||||
|
||||
// Copy construction/assignment from a maybe_impl.
|
||||
maybe_impl_t(const maybe_impl_t &v) : filled(v.filled) {
|
||||
// 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;
|
||||
if (v.filled) {
|
||||
new (storage) T(v.value());
|
||||
}
|
||||
}
|
||||
|
||||
maybe_impl_t &operator=(const maybe_impl_t &v) {
|
||||
maybe_impl_not_trivially_copyable_t &operator=(const maybe_impl_not_trivially_copyable_t &v) {
|
||||
if (&v == this) return *this;
|
||||
if (!v.filled) {
|
||||
reset();
|
||||
|
@ -97,7 +113,8 @@ struct maybe_impl_t {
|
|||
return *this;
|
||||
}
|
||||
|
||||
~maybe_impl_t() { reset(); }
|
||||
maybe_impl_not_trivially_copyable_t() = default;
|
||||
~maybe_impl_not_trivially_copyable_t() { reset(); }
|
||||
};
|
||||
|
||||
struct copyable_t {};
|
||||
|
@ -130,7 +147,15 @@ 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> {
|
||||
maybe_detail::maybe_impl_t<T> impl_;
|
||||
#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_;
|
||||
|
||||
public:
|
||||
// return whether the receiver contains a value.
|
||||
|
|
Loading…
Reference in a new issue