diff --git a/include/error.hh b/include/error.hh index 458cbd6..d6f1f22 100644 --- a/include/error.hh +++ b/include/error.hh @@ -1,5 +1,9 @@ #pragma once +#include +#include +#include + #include "exopt.h" #include "optional.h" @@ -7,16 +11,24 @@ namespace exopt { + // Report generated from an error. (virtual base, not abstract.) class Report { + public: virtual ~Report(); }; + // This is propagated returned (base, abstract) class Error { - constexpr Error() = default; + public: constexpr Error(Error const&) = default; constexpr Error(Error &&) = default; constexpr Error& operator=(Error &&) = 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 MaybeOwnedRef inner() noexcept { return {}; } //TODO: Maybe just use `std::optional>` @@ -25,7 +37,37 @@ namespace exopt { constexpr virtual ~Error() = default; 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 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: + 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 } diff --git a/include/optional.h b/include/optional.h index c4b92eb..4cb464a 100644 --- a/include/optional.h +++ b/include/optional.h @@ -21,6 +21,16 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] { constexpr static inline auto sentinel = nullptr; }; + template + struct null_optimise > { constexpr static inline bool value = true; + using held_type = T __restrict__*; + using type = ptr::Unique; + constexpr static decltype(auto) convert_to_held(std::add_rvalue_reference_t t) noexcept { return t.get(); } + constexpr static decltype(auto) convert_from_held(std::add_rvalue_reference_t t) noexcept { return ptr::Unique::new_unchecked(t); } + + constexpr static inline auto sentinel = nullptr; + }; + template struct null_optimise { constexpr static inline bool value = true; using held_type = T*; @@ -535,6 +545,8 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] { static_assert(alignof(Option) == alignof(void*), "Option: Bad null_opt align"); static_assert(sizeof(Option>) == sizeof(int*), "Option>: Bad null_opt size"); static_assert(alignof(Option>) == alignof(int*), "Option>: Bad null_opt align"); + static_assert(sizeof(Option>) == sizeof(int*), "Option>: Bad null_opt size"); + static_assert(alignof(Option>) == alignof(int*), "Option>: Bad null_opt align"); static_assert(sizeof(Option) == sizeof(double&), "Option: Bad null_opt size"); static_assert(alignof(Option) == alignof(double*), "Option: Bad null_opt align"); static_assert(sizeof(Option) == sizeof(char*), "Option: Bad null_opt size"); diff --git a/include/pointer.h b/include/pointer.h index 992b296..be2c384 100644 --- a/include/pointer.h +++ b/include/pointer.h @@ -13,9 +13,11 @@ namespace exopt::ptr { using RawRef = T&; template - using UniquePtr = T* __restrict__; + using UniquePtr = T __restrict__*; template - using UniqueRef = T& __restrict__; + using UniqueRef = T __restrict__&; + template + using UniqueMoveRef = T __restrict__&&; template using AliasedPtr = T __attribute__((__may_alias__))*; @@ -136,6 +138,8 @@ namespace exopt::ptr { constexpr virtual ~NullException() noexcept {} constexpr std::string_view message() const noexcept /*override*/ { return "pointer was null"; } + + std::runtime_error as_runtime() &&noexcept; /*TODO: override*/ }; template requires(!std::is_reference_v) @@ -158,7 +162,7 @@ namespace exopt::ptr { constexpr ~NonNull() noexcept = default; constexpr static NonNull try_new(T* ptr) { - if(!ptr) _throw_null(); + if(__builtin_expect(!ptr, false)) _throw_null(); return NonNull{ptr}; } #ifdef DEBUG @@ -233,16 +237,97 @@ namespace exopt::ptr { constexpr T* operator->() noexcept { return ptr_; } [[gnu::returns_nonnull]] constexpr const T* operator->() const noexcept { return ptr_; } - private: + private: [[noreturn, gnu::noinline, gnu::cold]] - constexpr void _throw_null() { + constexpr static void _throw_null() { if consteval { throw NullException{}; } 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()); } T* ptr_; }; + + /// Holds a *unique reference* that cannot be aliased, and cannot be null. + template requires(!std::is_reference_v) + struct Unique { + using value_type = std::remove_cv_t>; + + using reference_type = UniqueRef; + using const_reference_type = UniqueRef; + using move_reference_type = UniqueMoveRef; + + using pointer_type = UniquePtr; + using const_pointer_type = UniquePtr; + + constexpr static inline bool is_mutable = !std::is_same_v; + + constexpr static Unique try_new(pointer_type const&& ptr) + { + return { NonNull::try_new(ptr) }; + } +#ifdef DEBUG + [[gnu::nonnull(1)]] +#endif + constexpr static Unique new_unchecked(pointer_type ptr) noexcept { +#ifndef DEBUG + _EO_ASSUME(ptr!=nullptr); +#endif + return { NonNull{ ptr } }; } + + constexpr Unique(std::convertible_to> 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() 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 m_ptr; + }; +#if 0 + consteval bool uniq_check() noexcept { + int ptr = 0; + int* const ptr0 = &ptr; + return Unique::try_new(std::move(ptr0)).get() == &ptr; + } + static_assert(uniq_check(), "Invalid Unique<>.get()"); +#endif } diff --git a/include/util.hh b/include/util.hh index 442efa5..4dba281 100644 --- a/include/util.hh +++ b/include/util.hh @@ -63,4 +63,41 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] { }; 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 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(); + } } } diff --git a/src/ptr.cpp b/src/ptr.cpp new file mode 100644 index 0000000..c7ac118 --- /dev/null +++ b/src/ptr.cpp @@ -0,0 +1,9 @@ + + +#include + +#include "pointer.h" + +namespace exopt::ptr { + std::runtime_error NullException::as_runtime() &&noexcept { return std::runtime_error{std::string{message()} }; } +} diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000..cda1369 --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,15 @@ + +#include +#include + +#include + +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)}); + } + } +}