Added `ptr::Unique<T>`: `NonNull<T>` that cannot be aliased.

Fortune for libexopt's current commit: Future small blessing − 末小吉
boxed_is_boxed_value
Avril 2 years ago
parent d4c494c6c1
commit ee3893907c
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -1,5 +1,9 @@
#pragma once #pragma once
#include <stdexcept>
#include <stacktrace>
#include <source_location>
#include "exopt.h" #include "exopt.h"
#include "optional.h" #include "optional.h"
@ -7,16 +11,24 @@
namespace exopt { namespace exopt {
// Report generated from an error. (virtual base, not abstract.)
class Report { class Report {
public:
virtual ~Report(); virtual ~Report();
}; };
// This is propagated returned (base, abstract)
class Error { class Error {
constexpr Error() = default; public:
constexpr Error(Error const&) = default; constexpr Error(Error const&) = default;
constexpr Error(Error &&) = default; constexpr Error(Error &&) = default;
constexpr Error& operator=(Error &&) = default; constexpr Error& operator=(Error &&) = default;
constexpr Error& operator=(Error const&) = default; constexpr Error& operator=(Error const&) = default;
constexpr virtual std::source_location const& location() const noexcept { return m_location; }
constexpr operator std::string_view() const noexcept { return message(); }
constexpr virtual std::string_view message() const noexcept =0; constexpr virtual std::string_view message() const noexcept =0;
constexpr virtual MaybeOwnedRef<Error> inner() noexcept { return {}; } //TODO: Maybe just use `std::optional<std::reference_wrapper<Error>>` constexpr virtual MaybeOwnedRef<Error> inner() noexcept { return {}; } //TODO: Maybe just use `std::optional<std::reference_wrapper<Error>>`
@ -25,7 +37,37 @@ namespace exopt {
constexpr virtual ~Error() = default; constexpr virtual ~Error() = default;
protected: protected:
constexpr Error(std::source_location loc = std::source_location::current()) noexcept
: m_location(std::move(loc)) {}
private:
std::source_location m_location;
//Moved this to non-constexpr subclass `TracedError` \
std::unique_ptr<std::stacktrace> m_trace{nullptr}; //TODO: Change to exopt::types::Box<> to make copy ctor/assign work
};
// This is propagated returned, along with a stack trace. (base, abstract)
class TracedError : Error {
public:
TracedError(TracedError const &) = default;
TracedError(TracedError &&) = default;
TracedError& operator=(TracedError &&) = default;
TracedError& operator=(TracedError const&) = default;
virtual ~TracedError() = default;
inline virtual std::stacktrace const& stacktrace() const noexcept { return m_trace; }
protected:
inline TracedError(
std::source_location loc = std::source_location::current()
, std::stacktrace trace = std::stacktrace::current()) noexcept
: Error(std::move(loc)), m_trace(std::move(trace)) {}
private: private:
std::stacktrace m_trace; //TODO: Maybe box this? idk how big it is...
};
// This is thrown. (virtual base, abstract)
class Panic : /*virtual?? I think it should be XXX*/ Error {
//TODO: How to make a `constexpr` deduced panic that has stack-trace at runtime only? Is it needed? Hnm...
}; };
//TODO: Fatal(string) : public virtual Panic
} }

@ -21,6 +21,16 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] {
constexpr static inline auto sentinel = nullptr; constexpr static inline auto sentinel = nullptr;
}; };
template<typename T>
struct null_optimise<ptr::Unique<T> > { constexpr static inline bool value = true;
using held_type = T __restrict__*;
using type = ptr::Unique<T>;
constexpr static decltype(auto) convert_to_held(std::add_rvalue_reference_t<type> t) noexcept { return t.get(); }
constexpr static decltype(auto) convert_from_held(std::add_rvalue_reference_t<held_type> t) noexcept { return ptr::Unique<T>::new_unchecked(t); }
constexpr static inline auto sentinel = nullptr;
};
template<typename T> template<typename T>
struct null_optimise<T&> { constexpr static inline bool value = true; struct null_optimise<T&> { constexpr static inline bool value = true;
using held_type = T*; using held_type = T*;
@ -535,6 +545,8 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] {
static_assert(alignof(Option<void*>) == alignof(void*), "Option<T*>: Bad null_opt align"); static_assert(alignof(Option<void*>) == alignof(void*), "Option<T*>: Bad null_opt align");
static_assert(sizeof(Option<ptr::NonNull<int>>) == sizeof(int*), "Option<NonNull<T>>: Bad null_opt size"); static_assert(sizeof(Option<ptr::NonNull<int>>) == sizeof(int*), "Option<NonNull<T>>: Bad null_opt size");
static_assert(alignof(Option<ptr::NonNull<int>>) == alignof(int*), "Option<NonNull<T>>: Bad null_opt align"); static_assert(alignof(Option<ptr::NonNull<int>>) == alignof(int*), "Option<NonNull<T>>: Bad null_opt align");
static_assert(sizeof(Option<ptr::Unique<int>>) == sizeof(int*), "Option<Unique<T>>: Bad null_opt size");
static_assert(alignof(Option<ptr::Unique<int>>) == alignof(int*), "Option<Unique<T>>: Bad null_opt align");
static_assert(sizeof(Option<double&>) == sizeof(double&), "Option<T&>: Bad null_opt size"); static_assert(sizeof(Option<double&>) == sizeof(double&), "Option<T&>: Bad null_opt size");
static_assert(alignof(Option<double&>) == alignof(double*), "Option<T&>: Bad null_opt align"); static_assert(alignof(Option<double&>) == alignof(double*), "Option<T&>: Bad null_opt align");
static_assert(sizeof(Option<long long int&&>) == sizeof(char*), "Option<T&&>: Bad null_opt size"); static_assert(sizeof(Option<long long int&&>) == sizeof(char*), "Option<T&&>: Bad null_opt size");

@ -13,9 +13,11 @@ namespace exopt::ptr {
using RawRef = T&; using RawRef = T&;
template<typename T> template<typename T>
using UniquePtr = T* __restrict__; using UniquePtr = T __restrict__*;
template<typename T> template<typename T>
using UniqueRef = T& __restrict__; using UniqueRef = T __restrict__&;
template<typename T>
using UniqueMoveRef = T __restrict__&&;
template<typename T> template<typename T>
using AliasedPtr = T __attribute__((__may_alias__))*; using AliasedPtr = T __attribute__((__may_alias__))*;
@ -136,6 +138,8 @@ namespace exopt::ptr {
constexpr virtual ~NullException() noexcept {} constexpr virtual ~NullException() noexcept {}
constexpr std::string_view message() const noexcept /*override*/ { return "pointer was null"; } constexpr std::string_view message() const noexcept /*override*/ { return "pointer was null"; }
std::runtime_error as_runtime() &&noexcept; /*TODO: override*/
}; };
template<typename T> requires(!std::is_reference_v<T>) template<typename T> requires(!std::is_reference_v<T>)
@ -158,7 +162,7 @@ namespace exopt::ptr {
constexpr ~NonNull() noexcept = default; constexpr ~NonNull() noexcept = default;
constexpr static NonNull<T> try_new(T* ptr) { constexpr static NonNull<T> try_new(T* ptr) {
if(!ptr) _throw_null(); if(__builtin_expect(!ptr, false)) _throw_null();
return NonNull<T>{ptr}; return NonNull<T>{ptr};
} }
#ifdef DEBUG #ifdef DEBUG
@ -235,14 +239,95 @@ namespace exopt::ptr {
constexpr const T* operator->() const noexcept { return ptr_; } constexpr const T* operator->() const noexcept { return ptr_; }
private: private:
[[noreturn, gnu::noinline, gnu::cold]] [[noreturn, gnu::noinline, gnu::cold]]
constexpr void _throw_null() { constexpr static void _throw_null() {
if consteval { if consteval {
throw NullException{}; throw NullException{};
} else { } else {
throw std::runtime_error(NullException{}.message()); //std::terminate();
throw NullException{}.as_runtime();
//::exopt::util::throw_runtime(NullException{}); //XXX: Wut?
} }
//throw error::as_runtime_error(error::comptime_fail<NullException>()); //throw error::as_runtime_error(error::comptime_fail<NullException>());
} }
T* ptr_; T* ptr_;
}; };
/// Holds a *unique reference* that cannot be aliased, and cannot be null.
template<typename T> requires(!std::is_reference_v<T>)
struct Unique {
using value_type = std::remove_cv_t<std::decay_t<T>>;
using reference_type = UniqueRef<value_type>;
using const_reference_type = UniqueRef<value_type const>;
using move_reference_type = UniqueMoveRef<value_type>;
using pointer_type = UniquePtr<value_type>;
using const_pointer_type = UniquePtr<value_type const>;
constexpr static inline bool is_mutable = !std::is_same_v<pointer_type, const_pointer_type>;
constexpr static Unique<T> try_new(pointer_type const&& ptr)
{
return { NonNull<T>::try_new(ptr) };
}
#ifdef DEBUG
[[gnu::nonnull(1)]]
#endif
constexpr static Unique<T> new_unchecked(pointer_type ptr) noexcept {
#ifndef DEBUG
_EO_ASSUME(ptr!=nullptr);
#endif
return { NonNull<T>{ ptr } }; }
constexpr Unique(std::convertible_to<NonNull<value_type>> auto const&& ptr) noexcept
: m_ptr(ptr) {}
[[gnu::nonnull(1,2)]]
constexpr explicit Unique(T* __restrict__ ptr) noexcept
: m_ptr(ptr) {}
constexpr Unique(Unique&&) noexcept = default;
constexpr Unique& operator=(Unique&&) noexcept = default;
constexpr Unique(const Unique&) = delete;
constexpr Unique& operator=(const Unique&) = delete;
constexpr ~Unique() noexcept = default;
constexpr friend bool operator==(std::nullptr_t, const Unique& __restrict__) noexcept { return false; }
constexpr friend bool operator==(const Unique& __restrict__, std::nullptr_t) noexcept { return false; }
constexpr friend bool operator!=(std::nullptr_t, const Unique& __restrict__) noexcept { return true; }
constexpr friend bool operator!=(const Unique& __restrict__, std::nullptr_t) noexcept { return true; }
constexpr friend auto operator<=>(const Unique& __restrict__ a, const Unique& __restrict__ b) noexcept { return a.get() <=> b.get(); }
constexpr friend auto operator<=>(const Unique& __restrict__ a, T* __restrict__ b) noexcept { return (!b) ? (true<=>false) : (a.get() <=> b); }
constexpr friend auto operator<=>(T* __restrict__ a, const Unique& __restrict__ b) noexcept { return (!a) ? (false<=>true) : (a <=> b.get()); }
[[gnu::returns_nonnull]]
constexpr pointer_type get() const __restrict__ noexcept { return m_ptr.get(); }
[[gnu::returns_nonnull]]
constexpr operator pointer_type() const __restrict__ noexcept { return get(); }
constexpr explicit operator NonNull<T>() const noexcept { return m_ptr; }
constexpr reference_type operator*() __restrict__ & noexcept { return *get(); }
constexpr const_reference_type operator*() const __restrict__& noexcept { return *get(); }
constexpr move_reference_type operator*() __restrict__ &&noexcept { return std::move(*get()); }
constexpr decltype(auto) operator*() const __restrict__ &&noexcept { return *get(); }
[[gnu::returns_nonnull]]
constexpr pointer_type operator->() __restrict__ noexcept { return get(); }
[[gnu::returns_nonnull]]
constexpr const_pointer_type operator->() const __restrict__ noexcept { return get(); }
private:
NonNull<T> m_ptr;
};
#if 0
consteval bool uniq_check() noexcept {
int ptr = 0;
int* const ptr0 = &ptr;
return Unique<int>::try_new(std::move(ptr0)).get() == &ptr;
}
static_assert(uniq_check(), "Invalid Unique<>.get()");
#endif
} }

@ -63,4 +63,41 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] {
}; };
return _array_create(std::move(a), _I{}); return _array_create(std::move(a), _I{});
} }
struct [[gnu::visibility("internal")]] CTInternalFatalError {
constexpr CTInternalFatalError() noexcept = default;
constexpr CTInternalFatalError(CTInternalFatalError const&) noexcept = default;
constexpr CTInternalFatalError(CTInternalFatalError &&) noexcept = default;
constexpr CTInternalFatalError& operator=(CTInternalFatalError const&) noexcept = default;
constexpr CTInternalFatalError& operator=(CTInternalFatalError&&) noexcept = default;
constexpr virtual ~CTInternalFatalError() noexcept = default;
constexpr operator std::string_view() const noexcept { return message(); }
consteval virtual std::string_view message() const noexcept =0;
};
[[noreturn, gnu::noinline, gnu::cold]]
void throw_runtime(std::string_view&&);
[[noreturn]]//, gnu::noinline, gnu::cold]]
constexpr void throw_runtime(std::convertible_to<std::string_view> auto&& msg) {
std::string_view view{msg};
if consteval {
using CTE = CTInternalFatalError;
struct CTIFRuntimeError : public CTE {
using CTE::CTE;
consteval CTIFRuntimeError(std::string_view&& view) noexcept
: CTE(), m_message(std::move(view)) {}
consteval std::string_view message() const noexcept override { return m_message; }
constexpr virtual ~CTIFRuntimeError() noexcept {}
private:
std::string_view m_message;
};
throw CTIFRuntimeError(std::move(view));
} else {
throw_runtime(std::move(view));
}
__builtin_unreachable();
}
} } } }

@ -0,0 +1,9 @@
#include <string>
#include "pointer.h"
namespace exopt::ptr {
std::runtime_error NullException::as_runtime() &&noexcept { return std::runtime_error{std::string{message()} }; }
}

@ -0,0 +1,15 @@
#include <string>
#include <stdexcept>
#include <util.hh>
namespace exopt {
namespace util [[gnu::visibility("internal")]] {
[[noreturn, gnu::noinline, gnu::cold]]
void throw_runtime(std::string_view&& msg) {
throw std::runtime_error(std::string{std::move(msg)});
}
}
}
Loading…
Cancel
Save