Fix UB in class NullOr

NullOr now stores the references a pointers internally. This fixes UB
where the internal reference was initalized by dereferencing nullptr.

Test: Compiles
Bug: 121390225
Change-Id: I2073e5aeac401309aa63b08e05db3c467fab6b69
This commit is contained in:
Janis Danisevskis
2019-10-08 10:02:23 -07:00
parent c805505344
commit cf5d7e83fd

View File

@@ -280,39 +280,50 @@ inline KeyParameter Authorization(TypedTag<tag_type, tag> ttag, Args&&... args)
*/
template <typename ValueT>
class NullOr {
template <typename T>
struct reference_initializer {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnull-dereference"
static T&& init() { return *static_cast<std::remove_reference_t<T>*>(nullptr); }
#pragma GCC diagnostic pop
};
template <typename T>
struct pointer_initializer {
static T init() { return nullptr; }
};
template <typename T>
struct value_initializer {
static T init() { return T(); }
};
template <typename T>
using initializer_t =
std::conditional_t<std::is_lvalue_reference<T>::value, reference_initializer<T>,
std::conditional_t<std::is_pointer<T>::value, pointer_initializer<T>,
value_initializer<T>>>;
using internal_t = std::conditional_t<std::is_lvalue_reference<ValueT>::value,
std::remove_reference_t<ValueT>*, ValueT>;
public:
NullOr() : value_(initializer_t<ValueT>::init()), null_(true) {}
NullOr(ValueT&& value) : value_(std::forward<ValueT>(value)), null_(false) {}
struct pointer_initializer {
static std::nullptr_t init() { return nullptr; }
};
struct value_initializer {
static ValueT init() { return ValueT(); }
};
struct value_pointer_deref_t {
static ValueT& deref(ValueT& v) { return v; }
};
struct reference_deref_t {
static auto& deref(internal_t v) { return *v; }
};
using initializer_t = std::conditional_t<std::is_lvalue_reference<ValueT>::value ||
std::is_pointer<ValueT>::value,
pointer_initializer, value_initializer>;
using deref_t = std::conditional_t<std::is_lvalue_reference<ValueT>::value, reference_deref_t,
value_pointer_deref_t>;
public:
NullOr() : value_(initializer_t::init()), null_(true) {}
template <typename T>
NullOr(T&& value, typename std::enable_if<
!std::is_lvalue_reference<ValueT>::value &&
std::is_same<std::decay_t<ValueT>, std::decay_t<T>>::value,
int>::type = 0)
: value_(std::forward<ValueT>(value)), null_(false) {}
template <typename T>
NullOr(T& value, typename std::enable_if<
std::is_lvalue_reference<ValueT>::value &&
std::is_same<std::decay_t<ValueT>, std::decay_t<T>>::value,
int>::type = 0)
: value_(&value), null_(false) {}
bool isOk() const { return !null_; }
const ValueT& value() const & { return value_; }
ValueT& value() & { return value_; }
ValueT&& value() && { return std::move(value_); }
const ValueT& value() const& { return deref_t::deref(value_); }
ValueT& value() & { return deref_t::deref(value_); }
ValueT&& value() && { return std::move(deref_t::deref(value_)); }
private:
ValueT value_;
private:
internal_t value_;
bool null_;
};