From 54a292a167a657ad7f0b9fef6032b10a5aa5d74b Mon Sep 17 00:00:00 2001 From: Christopher Di Bella Date: Mon, 28 Apr 2025 16:42:22 +0000 Subject: [PATCH 01/11] changes `sus_panic` from macro to function This allows us to export it in a module, which means that we don't need to include headers that might expose bugs in Clang's implementation of C++20 modules. --- sus/assertions/panic.h | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/sus/assertions/panic.h b/sus/assertions/panic.h index 79e59636a..c0c0963f9 100644 --- a/sus/assertions/panic.h +++ b/sus/assertions/panic.h @@ -109,10 +109,11 @@ void print_panic_location(const PanicLocation& location) noexcept; /// /// If `SUS_PROVIDE_PANIC_HANDLER()` is defined, the macro _must_ not return or /// Undefined Behaviour will result. -#define sus_panic() \ - _sus_panic_location_handler(::sus::assertions::PanicLocation::current()); \ - _sus_panic_handler(); \ - static_assert(true) +[[noreturn, gnu::always_inline, gnu::nodebug]] inline void sus_panic(::sus::assertions::PanicLocation loc = ::sus::assertions::PanicLocation::current()) noexcept +{ + _sus_panic_location_handler(loc); + _sus_panic_handler(); +} /// Terminate the program, after printing a message. /// @@ -129,8 +130,8 @@ void print_panic_location(const PanicLocation& location) noexcept; /// * A [`PanicLocation`]($sus::assertions::PanicLocation). /// If the `SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER` macro does not consume the /// `msg`, this macro will avoid instantiating it at all. -#define sus_panic_with_message(msg) \ - _sus_panic_message_handler((msg), \ - ::sus::assertions::PanicLocation::current()); \ - _sus_panic_handler(); \ - static_assert(true) +[[noreturn, gnu::always_inline, gnu::nodebug]] inline void sus_panic_with_message(std::string_view message, ::sus::assertions::PanicLocation loc = ::sus::assertions::PanicLocation::current()) noexcept +{ + _sus_panic_message_handler(message, loc); + _sus_panic_handler(); +} From c66ead1ea0812b851fa0f721a572c257f1934219 Mon Sep 17 00:00:00 2001 From: Christopher Di Bella Date: Tue, 29 Apr 2025 16:22:27 +0000 Subject: [PATCH 02/11] renamespaces `sus_panic` to `sus::assertions::panic` This takes the function out of the global namespace and keeps the function names consistent with others in the project. --- STYLE.md | 8 ++-- subdoc/lib/type.cc | 2 +- sus/assertions/check.h | 12 +++--- sus/assertions/debug_check.h | 2 +- sus/assertions/panic.h | 15 ++++--- sus/assertions/panic_unittest.cc | 8 ++-- sus/assertions/unreachable.h | 6 +-- sus/boxed/box.h | 2 +- sus/collections/iterators/drain.h | 2 +- sus/error/error.h | 6 +-- sus/fn/fn.h | 2 +- sus/fn/fn_concepts.h | 4 +- sus/num/types.h | 2 +- sus/result/__private/storage.h | 8 ++-- sus/result/result.h | 66 +++++++++++++++---------------- 15 files changed, 75 insertions(+), 70 deletions(-) diff --git a/STYLE.md b/STYLE.md index ba000774e..b2b1b4271 100644 --- a/STYLE.md +++ b/STYLE.md @@ -7,7 +7,7 @@ footguns, crashes, bugs, and UB. 1. All methods are `constexpr` unless they must call a non-`constexpr` function, or they expose floating point NaNs (since constexpr NaNs change their bit values). - * Consider `sus_panic()`/`sus_check()` as constexpr for these purposes, they will + * Consider `sus::panic()`/`sus_check()` as constexpr for these purposes, they will correctly prevent compiling if the condition fails. 1. If you override on `const&`, then explicitly provide or delete the `&&` override. @@ -71,7 +71,7 @@ footguns, crashes, bugs, and UB. `operator==(Option, Option)` and `operator==(Option, Option)` look redundant but they are not, as the former allows conversions to Option for the rhs to happen while the latter does not (it would have to deduce `U` and fails). - + ## Containers that hold references Container types that hold references require extra care in a number of ways. To @@ -87,13 +87,13 @@ properly build such a container type (e.g. `Option` and `Tuple`): from an rvalue is okay, but when holding a value, giving a reference to it from an rvalue is not. * Use `static_assert(SafelyConstructibleFromReference)` - in places that store the reference to ensure a reference to a temporary does not + in places that store the reference to ensure a reference to a temporary does not get created due to an implicit conversion. The `FromReferenceType&&` here is should be the input type as it's written in the function parameters. * If a ctor type deduction guide is provided, the deduction should strip qualifiers and references with `std::remove_cvref_t` on the deduced type arguments. * Consider providing a construction marker type such as `some() -> SomeMarker` which - captures the parameters as references and lazily constructs the final type. This + captures the parameters as references and lazily constructs the final type. This allows reference types to be preserved through to the construction of the container without requiring the full type defn to be written every time. * Notably, this is omitted for `Choice`, which needs to be reasonably used behind diff --git a/subdoc/lib/type.cc b/subdoc/lib/type.cc index 59b8a6931..60cdc3580 100644 --- a/subdoc/lib/type.cc +++ b/subdoc/lib/type.cc @@ -593,7 +593,7 @@ Type build_local_type_internal( if (auto_type->isConstrained()) { qualtype->dump(); loc.dump(sm); - sus_panic_with_message("constrained auto without a concept?"); + sus::panic_with_message("constrained auto without a concept?"); } if (auto_type->isDecltypeAuto()) { return sus::tuple("decltype(auto)", TypeCategory::TemplateVariable); diff --git a/sus/assertions/check.h b/sus/assertions/check.h index 582a6a8ba..a07288fc0 100644 --- a/sus/assertions/check.h +++ b/sus/assertions/check.h @@ -17,32 +17,32 @@ #include "sus/assertions/panic.h" /// Verifies that the input, evaluated to a `bool`, is true. Otherwise, it will -/// [`panic`]($sus_panic), printing a message and terminating the program. +/// [`panic`]($sus::panic), printing a message and terminating the program. /// /// See [`sus_check_with_message`]($sus_check_with_message) to add a /// message to the display of the panic. /// /// The displayed output can be controlled by overriding the behaviour of -/// [`sus_panic`]($sus_panic) as described there. +/// [`sus::panic`]($sus::panic) as described there. #define sus_check(...) \ if (![](bool x) { return x; }(__VA_ARGS__)) [[unlikely]] { \ - sus_panic(); \ + sus::panic(); \ } \ static_assert(true) /// Verifies that the input `cond`, evaluated to a `bool`, is true. Otherwise, -/// it will [`panic`]($sus_panic), printing a customized message, and +/// it will [`panic`]($sus::panic), printing a customized message, and /// terminating the program. /// /// Use [`sus_check`]($sus_check) when there's nothing useful to add /// in the message. /// /// The displayed output can be controlled by overriding the behaviour of -/// [`sus_panic`]($sus_panic) as described there. If the +/// [`sus::panic`]($sus::panic) as described there. If the /// `SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER` macro does not consume the `msg`, /// this macro will avoid instantiating it at all. #define sus_check_with_message(cond, msg) \ if (!(cond)) [[unlikely]] { \ - sus_panic_with_message(msg); \ + sus::panic_with_message(msg); \ } \ static_assert(true) diff --git a/sus/assertions/debug_check.h b/sus/assertions/debug_check.h index 411fbc0ce..f0353fcc1 100644 --- a/sus/assertions/debug_check.h +++ b/sus/assertions/debug_check.h @@ -18,7 +18,7 @@ #include "sus/macros/assume.h" #include "sus/macros/compiler.h" -/// Check a condition in debug builds, causing a `sus_panic()` if the condition +/// Check a condition in debug builds, causing a `sus::panic()` if the condition /// fails. Nothing is checked in release builds. /// /// The condition must not have side effects, and should not call any functions diff --git a/sus/assertions/panic.h b/sus/assertions/panic.h index c0c0963f9..92fbb62f6 100644 --- a/sus/assertions/panic.h +++ b/sus/assertions/panic.h @@ -24,7 +24,7 @@ namespace sus { /// Checking for (e.g. [`sus_check`]($sus_check)) and handling -/// (e.g. [`sus_panic`]($sus_panic), +/// (e.g. [`sus::panic`]($sus::panic), /// [`sus_unreachable`]($sus_unreachable)) unexpected runtime /// conditions. namespace assertions {} @@ -63,8 +63,6 @@ void print_panic_message(std::string_view msg, void print_panic_location(const PanicLocation& location) noexcept; } // namespace __private -} // namespace sus::assertions - #if defined(SUS_PROVIDE_PRINT_PANIC_LOCATION_HANDLER) # define _sus_panic_location_handler(loc) \ SUS_PROVIDE_PRINT_PANIC_LOCATION_HANDLER(loc) @@ -109,7 +107,7 @@ void print_panic_location(const PanicLocation& location) noexcept; /// /// If `SUS_PROVIDE_PANIC_HANDLER()` is defined, the macro _must_ not return or /// Undefined Behaviour will result. -[[noreturn, gnu::always_inline, gnu::nodebug]] inline void sus_panic(::sus::assertions::PanicLocation loc = ::sus::assertions::PanicLocation::current()) noexcept +[[noreturn, gnu::always_inline, gnu::nodebug]] inline void panic(::sus::assertions::PanicLocation loc = ::sus::assertions::PanicLocation::current()) noexcept { _sus_panic_location_handler(loc); _sus_panic_handler(); @@ -130,8 +128,15 @@ void print_panic_location(const PanicLocation& location) noexcept; /// * A [`PanicLocation`]($sus::assertions::PanicLocation). /// If the `SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER` macro does not consume the /// `msg`, this macro will avoid instantiating it at all. -[[noreturn, gnu::always_inline, gnu::nodebug]] inline void sus_panic_with_message(std::string_view message, ::sus::assertions::PanicLocation loc = ::sus::assertions::PanicLocation::current()) noexcept +[[noreturn, gnu::always_inline, gnu::nodebug]] inline void panic_with_message(std::string_view message, ::sus::assertions::PanicLocation loc = ::sus::assertions::PanicLocation::current()) noexcept { _sus_panic_message_handler(message, loc); _sus_panic_handler(); } + +} // namespace sus::assertions + +namespace sus { + using ::sus::assertions::panic; + using ::sus::assertions::panic_with_message; +} diff --git a/sus/assertions/panic_unittest.cc b/sus/assertions/panic_unittest.cc index 837410e80..82d6791f0 100644 --- a/sus/assertions/panic_unittest.cc +++ b/sus/assertions/panic_unittest.cc @@ -29,25 +29,25 @@ namespace { TEST(PanicDeathTest, Panic) { #if GTEST_HAS_DEATH_TEST - EXPECT_DEATH(sus_panic(), + EXPECT_DEATH(sus::panic(), "^PANIC! at .*panic_unittest.cc:" DIGIT "+:" DIGIT "+\n$"); #endif } TEST(PanicDeathTest, WithMessage) { #if GTEST_HAS_DEATH_TEST - EXPECT_DEATH(sus_panic_with_message("hello world"), + EXPECT_DEATH(sus::panic_with_message("hello world"), "^PANIC! at 'hello world', .*panic_unittest.cc:" DIGIT "+:" DIGIT "+\n$"); #endif #if GTEST_HAS_DEATH_TEST - EXPECT_DEATH(sus_panic_with_message( + EXPECT_DEATH(sus::panic_with_message( std::string_view("hello world123").substr(0u, 11u)), "^PANIC! at 'hello world', .*panic_unittest.cc:" DIGIT "+:" DIGIT "+\n$"); #endif #if GTEST_HAS_DEATH_TEST - EXPECT_DEATH(sus_panic_with_message(std::string("hello world")), + EXPECT_DEATH(sus::panic_with_message(std::string("hello world")), "^PANIC! at 'hello world', .*panic_unittest.cc:" DIGIT "+:" DIGIT "+\n$"); #endif diff --git a/sus/assertions/unreachable.h b/sus/assertions/unreachable.h index 2e9c16426..4b588ec99 100644 --- a/sus/assertions/unreachable.h +++ b/sus/assertions/unreachable.h @@ -25,7 +25,7 @@ #endif /// Indicates to the developer that the location should not be reached, and -/// terminates the program with a [`panic`]($sus_panic). +/// terminates the program with a [`panic`]($sus::panic). /// /// This is similar to [`std::unreachable!`]( /// https://doc.rust-lang.org/stable/std/macro.unreachable.html) in Rust, @@ -36,12 +36,12 @@ /// https://en.cppreference.com/w/cpp/utility/unreachable) in C++ which is /// Undefined Behaviour if reached. It is closer to [`std::abort`]( /// https://en.cppreference.com/w/cpp/utility/program/abort) except built on -/// top of [`sus_panic`]($sus_panic). +/// top of [`sus::panic`]($sus::panic). /// The Subspace library matches the safer behaviour of Rust to avoid confusion /// and security bugs when working across languages. Use /// [`sus_unreachable_unchecked`]($sus_unreachable_unchecked) to /// indicate to the compiler the code is not reachable. -#define sus_unreachable() sus_panic_with_message("entered unreachable code") +#define sus_unreachable() sus::panic_with_message("entered unreachable code") /// Indicates to the compiler that the location will never be reached, allowing /// it to optimize code generation accordingly. If this function is actually diff --git a/sus/boxed/box.h b/sus/boxed/box.h index 3109f5cc7..499120747 100644 --- a/sus/boxed/box.h +++ b/sus/boxed/box.h @@ -75,7 +75,7 @@ struct [[_sus_trivial_abi]] BoxBase /// https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique) /// but built into the type. /// A moved-from `Box` may not be used except to be assigned to or destroyed. -/// Using a moved-from `Box` will [`panic`]($sus_panic) and +/// Using a moved-from `Box` will [`panic`]($sus::panic) and /// terminate the program rather than operate on a null. This prevents /// Undefined Behaviour and memory bugs caused by dereferencing null or using /// null in unintended ways. diff --git a/sus/collections/iterators/drain.h b/sus/collections/iterators/drain.h index 1542fae4d..9bad17302 100644 --- a/sus/collections/iterators/drain.h +++ b/sus/collections/iterators/drain.h @@ -73,7 +73,7 @@ struct [[nodiscard]] Drain final /// /// Calling this function will always panic. constexpr Drain& operator=(Drain&&) noexcept { - sus_panic_with_message("attempt to assign to Drain iterator"); + sus::panic_with_message("attempt to assign to Drain iterator"); } ~Drain() noexcept { diff --git a/sus/error/error.h b/sus/error/error.h index dce97ba9b..ea86ae1c4 100644 --- a/sus/error/error.h +++ b/sus/error/error.h @@ -42,9 +42,9 @@ namespace sus { /// The following are the primary interfaces of the panic system and the /// responsibilities they cover: /// -/// * [`sus_panic`]($sus_panic) (Constructing, Propagating) -/// * [`SUS_PROVIDE_PRINT_PANIC_LOCATION_HANDLER`]($sus_panic) (Reporting) -/// * [`SUS_PROVIDE_PANIC_HANDLER`]($sus_panic) (Reacting) +/// * [`sus::panic`]($sus::panic) (Constructing, Propagating) +/// * [`SUS_PROVIDE_PRINT_PANIC_LOCATION_HANDLER`]($sus::panic) (Reporting) +/// * [`SUS_PROVIDE_PANIC_HANDLER`]($sus::panic) (Reacting) /// /// The following are the primary interfaces of the error system and the /// responsibilities they cover: diff --git a/sus/fn/fn.h b/sus/fn/fn.h index 51eb64826..bbfe9c76e 100644 --- a/sus/fn/fn.h +++ b/sus/fn/fn.h @@ -37,7 +37,7 @@ namespace sus { /// As these are concepts, not concrete types, they can not enforce any /// behaviour but rather represent a protocol of expectations. Types designed to /// satisfy these concepts should adhere to them, and safely handle misuse, such -/// as panicking (via [`panic`]($sus_panic)) if called twice when it is not +/// as panicking (via [`panic`]($sus::panic)) if called twice when it is not /// supported. /// /// To make a type satisfy [`Fn`]($sus::fn::Fn) it should have a diff --git a/sus/fn/fn_concepts.h b/sus/fn/fn_concepts.h index 68d5cfdad..aa82ced2e 100644 --- a/sus/fn/fn_concepts.h +++ b/sus/fn/fn_concepts.h @@ -81,10 +81,10 @@ struct Anything { /// called correctly. It is moved-from after calling, and it should only be /// called once. /// -/// Calling a `FnOnce` multiple times may [`panic`]($sus_panic) +/// Calling a `FnOnce` multiple times may [`panic`]($sus::panic) /// or cause Undefined Behaviour. /// Not moving the `FnOnce` when calling it may fail to compile, -/// [`panic`]($sus_panic), or cause Undefined Behaviour depending on the type +/// [`panic`]($sus::panic), or cause Undefined Behaviour depending on the type /// that is being used to satisfy `FnOnce`. /// /// # Type erasure diff --git a/sus/num/types.h b/sus/num/types.h index 3928916b8..50d708f2a 100644 --- a/sus/num/types.h +++ b/sus/num/types.h @@ -40,7 +40,7 @@ namespace sus { /// types, but are safer than primitive C++ types and eliminate many classes of /// bugs that often lead to security vulnerabilities: /// * Integer overflow is not allowed by default (see [Overflow behaviour]( -/// #overflow-behaviour)), and will [`panic`]($sus_panic) to terminate the +/// #overflow-behaviour)), and will [`panic`]($sus::panic) to terminate the /// program. /// Intentional overflow can be achieved through methods like /// [`wrapping_add`]($sus::num::i32::wrapping_add) or diff --git a/sus/result/__private/storage.h b/sus/result/__private/storage.h index 5884a0c39..d3ed460e9 100644 --- a/sus/result/__private/storage.h +++ b/sus/result/__private/storage.h @@ -138,7 +138,7 @@ struct StorageVoid { switch (o.state) { case Ok: break; case Err: std::construct_at(&u.err, o.u.err); break; - case Moved: sus_panic_with_message("Result used after move"); + case Moved: sus::panic_with_message("Result used after move"); } // After construct_at since it may write into the field if it's in tail // padding. @@ -189,7 +189,7 @@ struct StorageVoid { switch (o.state) { case Ok: break; case Err: std::construct_at(&u.err, ::sus::move(o.u.err)); break; - case Moved: sus_panic_with_message("Result used after move"); + case Moved: sus::panic_with_message("Result used after move"); } // After construct_at since it may write into the field if it's in tail // padding. @@ -384,7 +384,7 @@ struct StorageNonVoid { switch (o.state) { case Ok: std::construct_at(&u.ok, o.u.ok); break; case Err: std::construct_at(&u.err, o.u.err); break; - case Moved: sus_panic_with_message("Result used after move"); + case Moved: sus::panic_with_message("Result used after move"); } // After construct_at since it may write into the field if it's in tail // padding. @@ -441,7 +441,7 @@ struct StorageNonVoid { switch (o.state) { case Ok: std::construct_at(&u.ok, ::sus::move(o.u.ok)); break; case Err: std::construct_at(&u.err, ::sus::move(o.u.err)); break; - case Moved: sus_panic_with_message("Result used after move"); + case Moved: sus::panic_with_message("Result used after move"); } // After construct_at since it may write into the field if it's in tail // padding. diff --git a/sus/result/result.h b/sus/result/result.h index f120937b5..129accabe 100644 --- a/sus/result/result.h +++ b/sus/result/result.h @@ -241,7 +241,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return Result(WITH_ERR, ::sus::clone(storage_.get_err())); } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } @@ -251,7 +251,7 @@ class [[nodiscard]] Result final { !(::sus::mem::CopyOrRefOrVoid && ::sus::mem::Copy)) { if (source.storage_.is_moved()) [[unlikely]] { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } else if (&source == this) [[unlikely]] { // Nothing to do. } else if (storage_.is_moved()) { @@ -429,7 +429,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return sus::fn::ReturnOnce::with_err(storage_.take_err()); } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } template <::sus::fn::FnOnce<::sus::fn::NonVoid()> AndFn> @@ -444,7 +444,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return sus::fn::ReturnOnce::with_err(storage_.take_err()); } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } /// Converts from `Result` to [`Option`]($sus::option::Option). @@ -463,7 +463,7 @@ class [[nodiscard]] Result final { storage_.drop_err(); return Option(); } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } @@ -478,7 +478,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return Option(storage_.take_err()); } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } @@ -493,12 +493,12 @@ class [[nodiscard]] Result final { return storage_.template get_ok(); } else if (storage_.is_err()) { if constexpr (fmt::is_formattable::value) { - sus_panic_with_message(fmt::to_string(storage_.get_err())); + sus::panic_with_message(fmt::to_string(storage_.get_err())); } else { - sus_panic_with_message("Result has error state"); + sus::panic_with_message("Result has error state"); } } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } constexpr const std::remove_reference_t& as_value() && = delete; @@ -514,12 +514,12 @@ class [[nodiscard]] Result final { return storage_.template get_ok_mut(); } else if (storage_.is_err()) { if constexpr (fmt::is_formattable::value) { - sus_panic_with_message(fmt::to_string(storage_.get_err())); + sus::panic_with_message(fmt::to_string(storage_.get_err())); } else { - sus_panic_with_message("Result has error state"); + sus::panic_with_message("Result has error state"); } } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } /// Returns a const reference to the contained `Err` value. @@ -531,14 +531,14 @@ class [[nodiscard]] Result final { return storage_.get_err(); } else if (storage_.is_ok()) { if constexpr (std::is_void_v) { - sus_panic_with_message("Result has ok state"); + sus::panic_with_message("Result has ok state"); } else if constexpr (!fmt::is_formattable::value) { - sus_panic_with_message("Result has ok state"); + sus::panic_with_message("Result has ok state"); } else { - sus_panic_with_message(fmt::to_string(storage_.template get_ok())); + sus::panic_with_message(fmt::to_string(storage_.template get_ok())); } } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } constexpr const E& as_err() && = delete; @@ -557,12 +557,12 @@ class [[nodiscard]] Result final { return storage_.template take_ok(); } else if (storage_.is_err()) { if constexpr (fmt::is_formattable::value) { - sus_panic_with_message(fmt::to_string(storage_.get_err())); + sus::panic_with_message(fmt::to_string(storage_.get_err())); } else { - sus_panic_with_message("Result has error state"); + sus::panic_with_message("Result has error state"); } } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } @@ -580,12 +580,12 @@ class [[nodiscard]] Result final { return storage_.template take_ok(); } else if (storage_.is_err()) { if constexpr (fmt::is_formattable::value) { - sus_panic_with_message(fmt::format("{}: {}", msg, storage_.get_err())); + sus::panic_with_message(fmt::format("{}: {}", msg, storage_.get_err())); } else { - sus_panic_with_message(msg); + sus::panic_with_message(msg); } } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } @@ -606,7 +606,7 @@ class [[nodiscard]] Result final { else return; } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } @@ -638,14 +638,14 @@ class [[nodiscard]] Result final { return storage_.take_err(); } else if (storage_.is_ok()) { if constexpr (std::is_void_v) { - sus_panic_with_message("Result has ok state"); + sus::panic_with_message("Result has ok state"); } else if constexpr (!fmt::is_formattable::value) { - sus_panic_with_message("Result has ok state"); + sus::panic_with_message("Result has ok state"); } else { - sus_panic_with_message(fmt::to_string(storage_.template get_ok())); + sus::panic_with_message(fmt::to_string(storage_.template get_ok())); } } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } @@ -683,7 +683,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return ::sus::fn::call_once(::sus::move(op), storage_.take_err()); } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } @@ -700,7 +700,7 @@ class [[nodiscard]] Result final { return ::sus::option::OptionIter&>( Option&>()); } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } constexpr ::sus::option::OptionIter< @@ -717,7 +717,7 @@ class [[nodiscard]] Result final { return ::sus::option::OptionIter&>( Option&>()); } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } @@ -730,7 +730,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return ::sus::option::OptionIter(Option()); } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } constexpr ::sus::option::OptionIter iter_mut() && noexcept @@ -744,7 +744,7 @@ class [[nodiscard]] Result final { storage_.drop_err(); return ::sus::option::OptionIter(Option()); } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } @@ -758,7 +758,7 @@ class [[nodiscard]] Result final { storage_.drop_err(); return ::sus::option::OptionIter(Option()); } else { - sus_panic_with_message("Result used after move"); + sus::panic_with_message("Result used after move"); } } From f1b7aa3beb8d07cf5a87e5e9af7e403337641886 Mon Sep 17 00:00:00 2001 From: Christopher Di Bella Date: Tue, 29 Apr 2025 16:46:20 +0000 Subject: [PATCH 03/11] renames `panic_with_message` to `panic` Rust's [panic macro] is overloaded to be able to take a message. Now that we've changed the Subspace `sus_panic` macro to a function, we can create an overload set for `panic`. [panic macro]: https://doc.rust-lang.org/stable/std/macro.panic.html --- subdoc/lib/type.cc | 2 +- sus/assertions/check.h | 2 +- sus/assertions/panic.h | 3 +- sus/assertions/panic_unittest.cc | 6 +-- sus/assertions/unreachable.h | 2 +- sus/collections/iterators/drain.h | 2 +- sus/result/__private/storage.h | 8 ++-- sus/result/result.h | 66 +++++++++++++++---------------- 8 files changed, 45 insertions(+), 46 deletions(-) diff --git a/subdoc/lib/type.cc b/subdoc/lib/type.cc index 60cdc3580..fc8c5d6fc 100644 --- a/subdoc/lib/type.cc +++ b/subdoc/lib/type.cc @@ -593,7 +593,7 @@ Type build_local_type_internal( if (auto_type->isConstrained()) { qualtype->dump(); loc.dump(sm); - sus::panic_with_message("constrained auto without a concept?"); + sus::panic("constrained auto without a concept?"); } if (auto_type->isDecltypeAuto()) { return sus::tuple("decltype(auto)", TypeCategory::TemplateVariable); diff --git a/sus/assertions/check.h b/sus/assertions/check.h index a07288fc0..67a656c44 100644 --- a/sus/assertions/check.h +++ b/sus/assertions/check.h @@ -43,6 +43,6 @@ /// this macro will avoid instantiating it at all. #define sus_check_with_message(cond, msg) \ if (!(cond)) [[unlikely]] { \ - sus::panic_with_message(msg); \ + sus::panic(msg); \ } \ static_assert(true) diff --git a/sus/assertions/panic.h b/sus/assertions/panic.h index 92fbb62f6..a02f39bf7 100644 --- a/sus/assertions/panic.h +++ b/sus/assertions/panic.h @@ -128,7 +128,7 @@ void print_panic_location(const PanicLocation& location) noexcept; /// * A [`PanicLocation`]($sus::assertions::PanicLocation). /// If the `SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER` macro does not consume the /// `msg`, this macro will avoid instantiating it at all. -[[noreturn, gnu::always_inline, gnu::nodebug]] inline void panic_with_message(std::string_view message, ::sus::assertions::PanicLocation loc = ::sus::assertions::PanicLocation::current()) noexcept +[[noreturn, gnu::always_inline, gnu::nodebug]] inline void panic(std::string_view message, ::sus::assertions::PanicLocation loc = ::sus::assertions::PanicLocation::current()) noexcept { _sus_panic_message_handler(message, loc); _sus_panic_handler(); @@ -138,5 +138,4 @@ void print_panic_location(const PanicLocation& location) noexcept; namespace sus { using ::sus::assertions::panic; - using ::sus::assertions::panic_with_message; } diff --git a/sus/assertions/panic_unittest.cc b/sus/assertions/panic_unittest.cc index 82d6791f0..ee9614698 100644 --- a/sus/assertions/panic_unittest.cc +++ b/sus/assertions/panic_unittest.cc @@ -36,18 +36,18 @@ TEST(PanicDeathTest, Panic) { TEST(PanicDeathTest, WithMessage) { #if GTEST_HAS_DEATH_TEST - EXPECT_DEATH(sus::panic_with_message("hello world"), + EXPECT_DEATH(sus::panic("hello world"), "^PANIC! at 'hello world', .*panic_unittest.cc:" DIGIT "+:" DIGIT "+\n$"); #endif #if GTEST_HAS_DEATH_TEST - EXPECT_DEATH(sus::panic_with_message( + EXPECT_DEATH(sus::panic( std::string_view("hello world123").substr(0u, 11u)), "^PANIC! at 'hello world', .*panic_unittest.cc:" DIGIT "+:" DIGIT "+\n$"); #endif #if GTEST_HAS_DEATH_TEST - EXPECT_DEATH(sus::panic_with_message(std::string("hello world")), + EXPECT_DEATH(sus::panic(std::string("hello world")), "^PANIC! at 'hello world', .*panic_unittest.cc:" DIGIT "+:" DIGIT "+\n$"); #endif diff --git a/sus/assertions/unreachable.h b/sus/assertions/unreachable.h index 4b588ec99..fba2a861f 100644 --- a/sus/assertions/unreachable.h +++ b/sus/assertions/unreachable.h @@ -41,7 +41,7 @@ /// and security bugs when working across languages. Use /// [`sus_unreachable_unchecked`]($sus_unreachable_unchecked) to /// indicate to the compiler the code is not reachable. -#define sus_unreachable() sus::panic_with_message("entered unreachable code") +#define sus_unreachable() sus::panic("entered unreachable code") /// Indicates to the compiler that the location will never be reached, allowing /// it to optimize code generation accordingly. If this function is actually diff --git a/sus/collections/iterators/drain.h b/sus/collections/iterators/drain.h index 9bad17302..d781173ce 100644 --- a/sus/collections/iterators/drain.h +++ b/sus/collections/iterators/drain.h @@ -73,7 +73,7 @@ struct [[nodiscard]] Drain final /// /// Calling this function will always panic. constexpr Drain& operator=(Drain&&) noexcept { - sus::panic_with_message("attempt to assign to Drain iterator"); + sus::panic("attempt to assign to Drain iterator"); } ~Drain() noexcept { diff --git a/sus/result/__private/storage.h b/sus/result/__private/storage.h index d3ed460e9..d92de496c 100644 --- a/sus/result/__private/storage.h +++ b/sus/result/__private/storage.h @@ -138,7 +138,7 @@ struct StorageVoid { switch (o.state) { case Ok: break; case Err: std::construct_at(&u.err, o.u.err); break; - case Moved: sus::panic_with_message("Result used after move"); + case Moved: sus::panic("Result used after move"); } // After construct_at since it may write into the field if it's in tail // padding. @@ -189,7 +189,7 @@ struct StorageVoid { switch (o.state) { case Ok: break; case Err: std::construct_at(&u.err, ::sus::move(o.u.err)); break; - case Moved: sus::panic_with_message("Result used after move"); + case Moved: sus::panic("Result used after move"); } // After construct_at since it may write into the field if it's in tail // padding. @@ -384,7 +384,7 @@ struct StorageNonVoid { switch (o.state) { case Ok: std::construct_at(&u.ok, o.u.ok); break; case Err: std::construct_at(&u.err, o.u.err); break; - case Moved: sus::panic_with_message("Result used after move"); + case Moved: sus::panic("Result used after move"); } // After construct_at since it may write into the field if it's in tail // padding. @@ -441,7 +441,7 @@ struct StorageNonVoid { switch (o.state) { case Ok: std::construct_at(&u.ok, ::sus::move(o.u.ok)); break; case Err: std::construct_at(&u.err, ::sus::move(o.u.err)); break; - case Moved: sus::panic_with_message("Result used after move"); + case Moved: sus::panic("Result used after move"); } // After construct_at since it may write into the field if it's in tail // padding. diff --git a/sus/result/result.h b/sus/result/result.h index 129accabe..166025972 100644 --- a/sus/result/result.h +++ b/sus/result/result.h @@ -241,7 +241,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return Result(WITH_ERR, ::sus::clone(storage_.get_err())); } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } @@ -251,7 +251,7 @@ class [[nodiscard]] Result final { !(::sus::mem::CopyOrRefOrVoid && ::sus::mem::Copy)) { if (source.storage_.is_moved()) [[unlikely]] { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } else if (&source == this) [[unlikely]] { // Nothing to do. } else if (storage_.is_moved()) { @@ -429,7 +429,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return sus::fn::ReturnOnce::with_err(storage_.take_err()); } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } template <::sus::fn::FnOnce<::sus::fn::NonVoid()> AndFn> @@ -444,7 +444,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return sus::fn::ReturnOnce::with_err(storage_.take_err()); } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } /// Converts from `Result` to [`Option`]($sus::option::Option). @@ -463,7 +463,7 @@ class [[nodiscard]] Result final { storage_.drop_err(); return Option(); } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } @@ -478,7 +478,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return Option(storage_.take_err()); } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } @@ -493,12 +493,12 @@ class [[nodiscard]] Result final { return storage_.template get_ok(); } else if (storage_.is_err()) { if constexpr (fmt::is_formattable::value) { - sus::panic_with_message(fmt::to_string(storage_.get_err())); + sus::panic(fmt::to_string(storage_.get_err())); } else { - sus::panic_with_message("Result has error state"); + sus::panic("Result has error state"); } } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } constexpr const std::remove_reference_t& as_value() && = delete; @@ -514,12 +514,12 @@ class [[nodiscard]] Result final { return storage_.template get_ok_mut(); } else if (storage_.is_err()) { if constexpr (fmt::is_formattable::value) { - sus::panic_with_message(fmt::to_string(storage_.get_err())); + sus::panic(fmt::to_string(storage_.get_err())); } else { - sus::panic_with_message("Result has error state"); + sus::panic("Result has error state"); } } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } /// Returns a const reference to the contained `Err` value. @@ -531,14 +531,14 @@ class [[nodiscard]] Result final { return storage_.get_err(); } else if (storage_.is_ok()) { if constexpr (std::is_void_v) { - sus::panic_with_message("Result has ok state"); + sus::panic("Result has ok state"); } else if constexpr (!fmt::is_formattable::value) { - sus::panic_with_message("Result has ok state"); + sus::panic("Result has ok state"); } else { - sus::panic_with_message(fmt::to_string(storage_.template get_ok())); + sus::panic(fmt::to_string(storage_.template get_ok())); } } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } constexpr const E& as_err() && = delete; @@ -557,12 +557,12 @@ class [[nodiscard]] Result final { return storage_.template take_ok(); } else if (storage_.is_err()) { if constexpr (fmt::is_formattable::value) { - sus::panic_with_message(fmt::to_string(storage_.get_err())); + sus::panic(fmt::to_string(storage_.get_err())); } else { - sus::panic_with_message("Result has error state"); + sus::panic("Result has error state"); } } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } @@ -580,12 +580,12 @@ class [[nodiscard]] Result final { return storage_.template take_ok(); } else if (storage_.is_err()) { if constexpr (fmt::is_formattable::value) { - sus::panic_with_message(fmt::format("{}: {}", msg, storage_.get_err())); + sus::panic(fmt::format("{}: {}", msg, storage_.get_err())); } else { - sus::panic_with_message(msg); + sus::panic(msg); } } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } @@ -606,7 +606,7 @@ class [[nodiscard]] Result final { else return; } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } @@ -638,14 +638,14 @@ class [[nodiscard]] Result final { return storage_.take_err(); } else if (storage_.is_ok()) { if constexpr (std::is_void_v) { - sus::panic_with_message("Result has ok state"); + sus::panic("Result has ok state"); } else if constexpr (!fmt::is_formattable::value) { - sus::panic_with_message("Result has ok state"); + sus::panic("Result has ok state"); } else { - sus::panic_with_message(fmt::to_string(storage_.template get_ok())); + sus::panic(fmt::to_string(storage_.template get_ok())); } } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } @@ -683,7 +683,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return ::sus::fn::call_once(::sus::move(op), storage_.take_err()); } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } @@ -700,7 +700,7 @@ class [[nodiscard]] Result final { return ::sus::option::OptionIter&>( Option&>()); } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } constexpr ::sus::option::OptionIter< @@ -717,7 +717,7 @@ class [[nodiscard]] Result final { return ::sus::option::OptionIter&>( Option&>()); } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } @@ -730,7 +730,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return ::sus::option::OptionIter(Option()); } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } constexpr ::sus::option::OptionIter iter_mut() && noexcept @@ -744,7 +744,7 @@ class [[nodiscard]] Result final { storage_.drop_err(); return ::sus::option::OptionIter(Option()); } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } @@ -758,7 +758,7 @@ class [[nodiscard]] Result final { storage_.drop_err(); return ::sus::option::OptionIter(Option()); } else { - sus::panic_with_message("Result used after move"); + sus::panic("Result used after move"); } } From d3e62ebddbfe954fa8aa17d730aceb62b232541e Mon Sep 17 00:00:00 2001 From: Christopher Di Bella Date: Tue, 29 Apr 2025 16:50:33 +0000 Subject: [PATCH 04/11] changes `sus_unreachable` from macro to function This allows us to export it in a module, which means that we don't need to include headers that might expose bugs in Clang's implementation of C++20 modules. --- sus/assertions/unreachable.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sus/assertions/unreachable.h b/sus/assertions/unreachable.h index fba2a861f..708749253 100644 --- a/sus/assertions/unreachable.h +++ b/sus/assertions/unreachable.h @@ -41,7 +41,9 @@ /// and security bugs when working across languages. Use /// [`sus_unreachable_unchecked`]($sus_unreachable_unchecked) to /// indicate to the compiler the code is not reachable. -#define sus_unreachable() sus::panic("entered unreachable code") +[[noreturn, gnu::always_inline, gnu::nodebug]] inline void sus_unreachable() { + [[clang::always_inline]] sus::panic("entered unreachable code"); +} /// Indicates to the compiler that the location will never be reached, allowing /// it to optimize code generation accordingly. If this function is actually @@ -52,7 +54,6 @@ /// /// # Safety /// This function must never actually be reached, or Undefined Behaviour occurs. -#define sus_unreachable_unchecked(unsafe_fn_marker) \ - static_assert(std::same_as); \ - _sus_unreachable_unchecked_impl() +[[noreturn, gnu::always_inline, gnu::nodebug]] inline void sus_unreachable_unchecked(::sus::marker::UnsafeFnMarker) { + _sus_unreachable_unchecked_impl(); +} From 0e8cb216a64e659fca0f8a7f1ae523a25707746a Mon Sep 17 00:00:00 2001 From: Christopher Di Bella Date: Tue, 29 Apr 2025 16:58:34 +0000 Subject: [PATCH 05/11] renamespaces `sus_unreachable` to `sus::assertions::unreachable` This takes the function out of the global namespace and keeps the function names consistent with others in the project. --- subdoc/lib/gen/files.h | 16 +++++------ subdoc/lib/gen/generate.h | 4 +-- subdoc/lib/gen/generate_alias.cc | 4 +-- subdoc/lib/gen/generate_concept.cc | 2 +- subdoc/lib/gen/generate_cpp_path.cc | 4 +-- subdoc/lib/gen/generate_function.cc | 2 +- subdoc/lib/gen/generate_macro.cc | 2 +- subdoc/lib/gen/generate_namespace.cc | 4 +-- subdoc/lib/gen/generate_record.cc | 4 +-- subdoc/lib/visit.cc | 22 +++++++-------- subdoc/tests/cpp_version.h | 2 +- sus/assertions/panic.h | 2 +- sus/assertions/unreachable.h | 28 +++++++++++-------- sus/assertions/unreachable_unittest.cc | 4 +-- sus/choice/choice_unittest.cc | 2 +- .../__private/slice_mut_methods.inc | 2 +- sus/env/var.h | 2 +- sus/error/error.h | 2 +- sus/error/error_unittest.cc | 2 +- sus/iter/generator.h | 2 +- sus/num/__private/intrinsics.h | 4 +-- sus/num/try_from_int_error_impl.h | 2 +- sus/option/option.h | 20 ++++++------- sus/result/__private/storage.h | 8 +++--- sus/result/result.h | 4 +-- 25 files changed, 78 insertions(+), 72 deletions(-) diff --git a/subdoc/lib/gen/files.h b/subdoc/lib/gen/files.h index 5c53a8958..c71fccddd 100644 --- a/subdoc/lib/gen/files.h +++ b/subdoc/lib/gen/files.h @@ -101,7 +101,7 @@ inline std::filesystem::path construct_html_namespace_file_path( return fmt::format("namespace.{}", namespace_path[0u].as()); } - sus_unreachable(); + sus::unreachable(); }(); fname << name; @@ -316,7 +316,7 @@ inline Option construct_html_url_for_alias( // aliases. break; } - sus_unreachable(); + sus::unreachable(); }); } case AliasTarget::Tag::AliasOfConcept: { @@ -330,11 +330,11 @@ inline Option construct_html_url_for_alias( } case ConceptRefOrName::Tag::Name: return sus::none(); } - sus_unreachable(); + sus::unreachable(); } case AliasTarget::Tag::AliasOfMethod: { // TODO: Link to method. - sus_unreachable(); + sus::unreachable(); } case AliasTarget::Tag::AliasOfFunction: { const LinkedFunction& fun = @@ -347,11 +347,11 @@ inline Option construct_html_url_for_alias( } case FunctionRefOrName::Tag::Name: return sus::none(); } - sus_unreachable(); + sus::unreachable(); } case AliasTarget::Tag::AliasOfEnumConstant: { // TODO: Link to constant. - sus_unreachable(); + sus::unreachable(); } case AliasTarget::Tag::AliasOfVariable: { const LinkedVariable& var = @@ -364,10 +364,10 @@ inline Option construct_html_url_for_alias( } case VariableRefOrName::Tag::Name: return sus::none(); } - sus_unreachable(); + sus::unreachable(); } } - sus_unreachable(); + sus::unreachable(); } else { // TODO: Link to the alias' page. return sus::some("TODO"); diff --git a/subdoc/lib/gen/generate.h b/subdoc/lib/gen/generate.h index f84edbfeb..6fa9de650 100644 --- a/subdoc/lib/gen/generate.h +++ b/subdoc/lib/gen/generate.h @@ -70,7 +70,7 @@ struct sus::error::ErrorImpl { return fmt::format("parsing doc comment markdown"); } } - sus_unreachable(); + sus::unreachable(); } static Option source( const GenerateError& e) noexcept { @@ -88,6 +88,6 @@ struct sus::error::ErrorImpl { return sus::some(*p); } } - sus_unreachable(); + sus::unreachable(); } }; diff --git a/subdoc/lib/gen/generate_alias.cc b/subdoc/lib/gen/generate_alias.cc index 524d49cbf..a9a9ee66d 100644 --- a/subdoc/lib/gen/generate_alias.cc +++ b/subdoc/lib/gen/generate_alias.cc @@ -77,7 +77,7 @@ sus::Result get_alias_comment( } case AliasTarget::Tag::AliasOfMethod: { // TODO: Link to method. - sus_unreachable(); + sus::unreachable(); } case AliasTarget::Tag::AliasOfFunction: { const LinkedFunction& fun = @@ -94,7 +94,7 @@ sus::Result get_alias_comment( } case AliasTarget::Tag::AliasOfEnumConstant: { // TODO: Link to constant. - sus_unreachable(); + sus::unreachable(); } case AliasTarget::Tag::AliasOfVariable: { const LinkedVariable& var = diff --git a/subdoc/lib/gen/generate_concept.cc b/subdoc/lib/gen/generate_concept.cc index 03a278027..6ea038468 100644 --- a/subdoc/lib/gen/generate_concept.cc +++ b/subdoc/lib/gen/generate_concept.cc @@ -77,7 +77,7 @@ void generate_concept_overview(HtmlWriter::OpenDiv& record_div, break; // Macro can't be an ancesor of a concept. case CppPathConcept: return "concept-name"; } - sus_unreachable(); + sus::unreachable(); }()); ancestor_anchor.add_href(e.link_href); ancestor_anchor.write_text(e.name); diff --git a/subdoc/lib/gen/generate_cpp_path.cc b/subdoc/lib/gen/generate_cpp_path.cc index 7682c08cb..7f17ac1ce 100644 --- a/subdoc/lib/gen/generate_cpp_path.cc +++ b/subdoc/lib/gen/generate_cpp_path.cc @@ -41,7 +41,7 @@ Vec generate_with_ancestors( return std::string("(anonymous)"); case Namespace::Tag::Named: return sus::clone(ancestor.name); } - sus_unreachable(); + sus::unreachable(); }(), .link_href = construct_html_url_for_namespace(ancestor), .type = @@ -51,7 +51,7 @@ Vec generate_with_ancestors( case Namespace::Tag::Anonymous: return CppPathNamespace; case Namespace::Tag::Named: return CppPathNamespace; } - sus_unreachable(); + sus::unreachable(); }(), .search_weight = 1_f32, }); diff --git a/subdoc/lib/gen/generate_function.cc b/subdoc/lib/gen/generate_function.cc index 87a6038b6..ad94f1cab 100644 --- a/subdoc/lib/gen/generate_function.cc +++ b/subdoc/lib/gen/generate_function.cc @@ -321,7 +321,7 @@ sus::Result generate_function( case CppPathConcept: break; // Concept can't be an ancestor of a function. } - sus_unreachable(); + sus::unreachable(); }()); ancestor_anchor.add_href(e.link_href); ancestor_anchor.write_text(e.name); diff --git a/subdoc/lib/gen/generate_macro.cc b/subdoc/lib/gen/generate_macro.cc index 68e2ce44c..b9d0079e1 100644 --- a/subdoc/lib/gen/generate_macro.cc +++ b/subdoc/lib/gen/generate_macro.cc @@ -145,7 +145,7 @@ sus::Result generate_macro( case CppPathProject: return "project-name"; case CppPathMacro: return "macro-name"; default: - sus_unreachable(); // Macros are only in the global namespace. + sus::unreachable(); // Macros are only in the global namespace. } }()); ancestor_anchor.add_href(e.link_href); diff --git a/subdoc/lib/gen/generate_namespace.cc b/subdoc/lib/gen/generate_namespace.cc index 4863a2d9a..851334602 100644 --- a/subdoc/lib/gen/generate_namespace.cc +++ b/subdoc/lib/gen/generate_namespace.cc @@ -196,7 +196,7 @@ void generate_namespace_overview(HtmlWriter::OpenDiv& namespace_div, case CppPathConcept: break; // Concept can't be an ancestor of a namespace. } - sus_unreachable(); + sus::unreachable(); }()); ancestor_anchor.add_href(e.link_href); ancestor_anchor.write_text(e.name); @@ -615,7 +615,7 @@ sus::Result generate_namespace( json.add_string("split_name", split_for_search(options.project_name)); break; } - case Namespace::Tag::Anonymous: sus_unreachable(); + case Namespace::Tag::Anonymous: sus::unreachable(); case Namespace::Tag::Named: { json.add_string("type", "namespace"); json.add_string("name", element.name); diff --git a/subdoc/lib/gen/generate_record.cc b/subdoc/lib/gen/generate_record.cc index 89f4ff0e3..9d2757708 100644 --- a/subdoc/lib/gen/generate_record.cc +++ b/subdoc/lib/gen/generate_record.cc @@ -64,7 +64,7 @@ const FunctionElement& function_element_from_sorted( case MethodType::NonStaticOperators: return element.methods.at(s.at<3>()); case MethodType::Conversions: return element.conversions.at(s.at<3>()); } - sus_unreachable(); + sus::unreachable(); } /// Compares two `SortedFunctionByName` for ordering. It compares by ignoring @@ -135,7 +135,7 @@ void generate_record_overview(HtmlWriter::OpenDiv& record_div, case CppPathConcept: break; // Concept can't be an ancestor of a record. } - sus_unreachable(); + sus::unreachable(); }()); ancestor_anchor.add_href(e.link_href); ancestor_anchor.write_text(e.name); diff --git a/subdoc/lib/visit.cc b/subdoc/lib/visit.cc index 45487e17e..2854045d2 100644 --- a/subdoc/lib/visit.cc +++ b/subdoc/lib/visit.cc @@ -720,7 +720,7 @@ class Visitor : public clang::RecursiveASTVisitor { // unit. decl->dump(); decl->getDeclContext()->dumpAsDecl(); - sus_unreachable(); + sus::unreachable(); } auto te = AliasElement( @@ -758,7 +758,7 @@ class Visitor : public clang::RecursiveASTVisitor { // unit. decl->dump(); decl->getDeclContext()->dumpAsDecl(); - sus_unreachable(); + sus::unreachable(); } auto te = AliasElement( @@ -797,7 +797,7 @@ class Visitor : public clang::RecursiveASTVisitor { // The context for a concept is a namespace or translation unit. decl->dump(); decl->getDeclContext()->dumpAsDecl(); - sus_unreachable(); + sus::unreachable(); } Vec target_namespaces = @@ -833,7 +833,7 @@ class Visitor : public clang::RecursiveASTVisitor { decl->getBeginLoc().dump(decl->getASTContext().getSourceManager()); // TODO: Put these into static fields on a record, and const global // variables on a namespace. - //sus_unreachable(); + //sus::unreachable(); } else if (auto* vardecl = clang::dyn_cast(shadow->getTargetDecl())) { auto* context = @@ -844,7 +844,7 @@ class Visitor : public clang::RecursiveASTVisitor { // can't write an alias to a static class data member. decl->dump(); decl->getDeclContext()->dumpAsDecl(); - sus_unreachable(); + sus::unreachable(); } Vec target_namespaces = @@ -881,7 +881,7 @@ class Visitor : public clang::RecursiveASTVisitor { // The context for a using method is a record. decl->dump(); decl->getDeclContext()->dumpAsDecl(); - sus_unreachable(); + sus::unreachable(); } auto te = AliasElement( @@ -926,7 +926,7 @@ class Visitor : public clang::RecursiveASTVisitor { // The context for a function is a namespace or translation unit. decl->dump(); decl->getDeclContext()->dumpAsDecl(); - sus_unreachable(); + sus::unreachable(); } Vec target_namespaces = @@ -966,7 +966,7 @@ class Visitor : public clang::RecursiveASTVisitor { // The context for a function is a namespace or translation unit. decl->dump(); decl->getDeclContext()->dumpAsDecl(); - sus_unreachable(); + sus::unreachable(); } Vec target_namespaces = @@ -1002,7 +1002,7 @@ class Visitor : public clang::RecursiveASTVisitor { decl->getBeginLoc().dump(decl->getASTContext().getSourceManager()); fmt::println(stderr, ""); shadow->getTargetDecl()->dump(); - sus_unreachable(); + sus::unreachable(); } } @@ -1342,7 +1342,7 @@ class Visitor : public clang::RecursiveASTVisitor { else return MethodQualifier::MutableRValue; } - sus_unreachable(); + sus::unreachable(); }(), }); } @@ -1775,7 +1775,7 @@ bool VisitCx::should_include_decl_based_on_file(clang::Decl* decl) noexcept { return true; } } - sus_unreachable(); + sus::unreachable(); } } // namespace subdoc diff --git a/subdoc/tests/cpp_version.h b/subdoc/tests/cpp_version.h index 57ee59772..620a8e48c 100644 --- a/subdoc/tests/cpp_version.h +++ b/subdoc/tests/cpp_version.h @@ -28,7 +28,7 @@ inline std::string_view cpp_version_flag(SubDocCppVersion v) noexcept { switch (v) { case SubDocCppVersion::Cpp20: return "-std=c++20"; } - sus_unreachable(); + sus::unreachable(); } } // namespace subdoc::tests diff --git a/sus/assertions/panic.h b/sus/assertions/panic.h index a02f39bf7..2cff548e4 100644 --- a/sus/assertions/panic.h +++ b/sus/assertions/panic.h @@ -25,7 +25,7 @@ namespace sus { /// Checking for (e.g. [`sus_check`]($sus_check)) and handling /// (e.g. [`sus::panic`]($sus::panic), -/// [`sus_unreachable`]($sus_unreachable)) unexpected runtime +/// [`sus::unreachable`]($sus::unreachable)) unexpected runtime /// conditions. namespace assertions {} } // namespace sus diff --git a/sus/assertions/unreachable.h b/sus/assertions/unreachable.h index 708749253..d9c2caf71 100644 --- a/sus/assertions/unreachable.h +++ b/sus/assertions/unreachable.h @@ -14,16 +14,12 @@ #pragma once +#include "panic.h" #include "sus/assertions/panic.h" #include "sus/macros/builtin.h" #include "sus/marker/unsafe.h" -#if __has_builtin(__builtin_unreachable) -# define _sus_unreachable_unchecked_impl() __builtin_unreachable() -#else -# define _sus_unreachable_unchecked_impl() __assume(false) -#endif - +namespace sus::assertions { /// Indicates to the developer that the location should not be reached, and /// terminates the program with a [`panic`]($sus::panic). /// @@ -39,10 +35,10 @@ /// top of [`sus::panic`]($sus::panic). /// The Subspace library matches the safer behaviour of Rust to avoid confusion /// and security bugs when working across languages. Use -/// [`sus_unreachable_unchecked`]($sus_unreachable_unchecked) to +/// [`sus::unreachable_unchecked`]($sus::unreachable_unchecked) to /// indicate to the compiler the code is not reachable. -[[noreturn, gnu::always_inline, gnu::nodebug]] inline void sus_unreachable() { - [[clang::always_inline]] sus::panic("entered unreachable code"); +[[noreturn, gnu::always_inline, gnu::nodebug]] inline void unreachable(PanicLocation loc = PanicLocation::current()) { + [[clang::always_inline]] sus::panic("entered unreachable code", loc); } /// Indicates to the compiler that the location will never be reached, allowing @@ -54,6 +50,16 @@ /// /// # Safety /// This function must never actually be reached, or Undefined Behaviour occurs. -[[noreturn, gnu::always_inline, gnu::nodebug]] inline void sus_unreachable_unchecked(::sus::marker::UnsafeFnMarker) { - _sus_unreachable_unchecked_impl(); +[[noreturn, gnu::always_inline, gnu::nodebug]] inline void unreachable_unchecked(::sus::marker::UnsafeFnMarker) { +#if __has_builtin(__builtin_unreachable) + __builtin_unreachable(); +#else + __assume(false); +#endif +} +} // namespace ::sus::assertions + +namespace sus { + using sus::assertions::unreachable; + using sus::assertions::unreachable_unchecked; } diff --git a/sus/assertions/unreachable_unittest.cc b/sus/assertions/unreachable_unittest.cc index 0e2a5a0bb..169c04a2e 100644 --- a/sus/assertions/unreachable_unittest.cc +++ b/sus/assertions/unreachable_unittest.cc @@ -31,7 +31,7 @@ namespace { TEST(UnreachableDeathTest, Unreachable) { #if GTEST_HAS_DEATH_TEST - EXPECT_DEATH(sus_unreachable(), + EXPECT_DEATH(sus::unreachable(), "^PANIC! at .*unreachable_unittest.cc:" DIGIT "+:" DIGIT "+\n$"); #endif } @@ -42,7 +42,7 @@ TEST(Unreachable, Unchecked) { } // We can't actually land here or we'd introduce UB, but the test confirms // we can write it and it compiles without warnings. - sus_unreachable_unchecked(unsafe_fn); + sus::unreachable_unchecked(unsafe_fn); } } // namespace diff --git a/sus/choice/choice_unittest.cc b/sus/choice/choice_unittest.cc index 9b37418ba..e7add2202 100644 --- a/sus/choice/choice_unittest.cc +++ b/sus/choice/choice_unittest.cc @@ -48,7 +48,7 @@ struct fmt::formatter { case Second: return fmt::format_to(ctx.out(), "Second"); case Third: return fmt::format_to(ctx.out(), "Third"); } - sus_unreachable(); + sus::unreachable(); } }; diff --git a/sus/collections/__private/slice_mut_methods.inc b/sus/collections/__private/slice_mut_methods.inc index 1d84a66d6..efe48c599 100644 --- a/sus/collections/__private/slice_mut_methods.inc +++ b/sus/collections/__private/slice_mut_methods.inc @@ -597,7 +597,7 @@ constexpr RSplitNMut rsplitn_mut(usize n, index < len(), "partition_at_index index greater than length of slice"); // TODO: Requires Iterator::enumerate(), max_by(), min_by() - sus_unreachable(); + sus::unreachable(); } /// Reorder the slice with a key extraction function such that the element at diff --git a/sus/env/var.h b/sus/env/var.h index fc6a9c87b..5af23fb2e 100644 --- a/sus/env/var.h +++ b/sus/env/var.h @@ -84,6 +84,6 @@ struct sus::error::ErrorImpl<::sus::env::VarError> { case ::sus::env::VarError::InvalidKeyEncoding: return "InvalidKeyEncoding"; } - sus_unreachable(); + sus::unreachable(); } }; diff --git a/sus/error/error.h b/sus/error/error.h index ea86ae1c4..2d74460ce 100644 --- a/sus/error/error.h +++ b/sus/error/error.h @@ -293,7 +293,7 @@ concept HasErrorSource = requires(const T& t) { /// switch (self) { /// case ErrorReason::SomeReason: return "we saw SomeReason happen"; /// } -/// sus_unreachable(); +/// sus::unreachable(); /// } /// }; /// diff --git a/sus/error/error_unittest.cc b/sus/error/error_unittest.cc index ead9f30e3..e943f1127 100644 --- a/sus/error/error_unittest.cc +++ b/sus/error/error_unittest.cc @@ -47,7 +47,7 @@ struct sus::error::ErrorImpl { switch (self) { case ErrorReason::SomeReason: return "we saw SomeReason happen"; } - sus_unreachable(); + sus::unreachable(); } }; static_assert(sus::error::error_display(ErrorReason::SomeReason) == diff --git a/sus/iter/generator.h b/sus/iter/generator.h index abc98159b..28f53ed03 100644 --- a/sus/iter/generator.h +++ b/sus/iter/generator.h @@ -105,7 +105,7 @@ class IterPromise { constexpr auto initial_suspend() noexcept { return std::suspend_always(); } constexpr auto final_suspend() noexcept { return std::suspend_always(); } - constexpr void unhandled_exception() noexcept { sus_unreachable(); } + constexpr void unhandled_exception() noexcept { sus::unreachable(); } constexpr Option take() & noexcept { return yielded_.take(); } diff --git a/sus/num/__private/intrinsics.h b/sus/num/__private/intrinsics.h index 0d1ed4ded..e731f20ef 100644 --- a/sus/num/__private/intrinsics.h +++ b/sus/num/__private/intrinsics.h @@ -1768,7 +1768,7 @@ __sus_pure_const constexpr inline ::sus::num::FpCategory float_category( case norm: return ::sus::num::FpCategory::Normal; case subnorm: return ::sus::num::FpCategory::Subnormal; case zero: return ::sus::num::FpCategory::Zero; - default: sus_unreachable_unchecked(::sus::marker::unsafe_fn); + default: sus::unreachable_unchecked(::sus::marker::unsafe_fn); } } #else @@ -1790,7 +1790,7 @@ __sus_pure_const constexpr inline ::sus::num::FpCategory float_category( case FP_NORMAL: return ::sus::num::FpCategory::Normal; case FP_SUBNORMAL: return ::sus::num::FpCategory::Subnormal; case FP_ZERO: return ::sus::num::FpCategory::Zero; - default: sus_unreachable_unchecked(::sus::marker::unsafe_fn); + default: sus::unreachable_unchecked(::sus::marker::unsafe_fn); } } } diff --git a/sus/num/try_from_int_error_impl.h b/sus/num/try_from_int_error_impl.h index 092aed851..70184adc8 100644 --- a/sus/num/try_from_int_error_impl.h +++ b/sus/num/try_from_int_error_impl.h @@ -53,7 +53,7 @@ struct sus::error::ErrorImpl { switch (e.kind()) { case sus::num::TryFromIntError::Kind::OutOfBounds: return "out of bounds"; } - sus_unreachable_unchecked(::sus::marker::unsafe_fn); + sus::unreachable_unchecked(::sus::marker::unsafe_fn); } }; diff --git a/sus/option/option.h b/sus/option/option.h index d60201395..f2856360f 100644 --- a/sus/option/option.h +++ b/sus/option/option.h @@ -365,7 +365,7 @@ namespace sus { /// another [`Option`]($sus::option::Option) as input, and produce an /// [`Option`]($sus::option::Option) as output. /// Only the [`and_that`]($sus::option::Option::and_that) -/// method can produce an [`Option`]($sus::option::Option) value having a +/// method can produce an [`Option`]($sus::option::Option) value having a /// different inner type `U` than [`Option`]($sus::option::Option). /// /// | method | self | input | output | @@ -853,7 +853,7 @@ class Option final { // Result::unwrap_unchecked benefits from telling the compiler explicitly // that the other states are never set. Match that here until shown it's // actually not useful. - sus_unreachable_unchecked(::sus::marker::unsafe_fn); + sus::unreachable_unchecked(::sus::marker::unsafe_fn); } } constexpr inline T unwrap_unchecked( @@ -1724,7 +1724,7 @@ class Option final { r.as_value_unchecked(::sus::marker::unsafe_fn)); case None: return r.is_none(); } - sus_unreachable_unchecked(::sus::marker::unsafe_fn); + sus::unreachable_unchecked(::sus::marker::unsafe_fn); } template requires(::sus::cmp::Eq) @@ -1736,7 +1736,7 @@ class Option final { r.as_value_unchecked(::sus::marker::unsafe_fn)); case None: return r.is_none(); } - sus_unreachable_unchecked(::sus::marker::unsafe_fn); + sus::unreachable_unchecked(::sus::marker::unsafe_fn); } template @@ -1776,7 +1776,7 @@ class Option final { else return std::strong_ordering::equivalent; } - sus_unreachable_unchecked(::sus::marker::unsafe_fn); + sus::unreachable_unchecked(::sus::marker::unsafe_fn); } template requires(::sus::cmp::ExclusiveStrongOrd) @@ -1796,7 +1796,7 @@ class Option final { else return std::strong_ordering::equivalent; } - sus_unreachable_unchecked(::sus::marker::unsafe_fn); + sus::unreachable_unchecked(::sus::marker::unsafe_fn); } // sus::cmp::Ord> trait. @@ -1818,7 +1818,7 @@ class Option final { else return std::weak_ordering::equivalent; } - sus_unreachable_unchecked(::sus::marker::unsafe_fn); + sus::unreachable_unchecked(::sus::marker::unsafe_fn); } template requires(::sus::cmp::ExclusiveOrd) @@ -1838,7 +1838,7 @@ class Option final { else return std::weak_ordering::equivalent; } - sus_unreachable_unchecked(::sus::marker::unsafe_fn); + sus::unreachable_unchecked(::sus::marker::unsafe_fn); } // sus::cmp::PartialOrd> trait. @@ -1860,7 +1860,7 @@ class Option final { else return std::partial_ordering::equivalent; } - sus_unreachable_unchecked(::sus::marker::unsafe_fn); + sus::unreachable_unchecked(::sus::marker::unsafe_fn); } friend constexpr inline std::partial_ordering operator<=>( const Option& l, const Option& r) noexcept @@ -1880,7 +1880,7 @@ class Option final { else return std::partial_ordering::equivalent; } - sus_unreachable_unchecked(::sus::marker::unsafe_fn); + sus::unreachable_unchecked(::sus::marker::unsafe_fn); } template diff --git a/sus/result/__private/storage.h b/sus/result/__private/storage.h index d92de496c..9f01a73b3 100644 --- a/sus/result/__private/storage.h +++ b/sus/result/__private/storage.h @@ -171,7 +171,7 @@ struct StorageVoid { switch (o.state) { case Ok: break; case Err: std::construct_at(&u.err, o.u.err); break; - case Moved: sus_unreachable_unchecked(::sus::marker::unsafe_fn); + case Moved: sus::unreachable_unchecked(::sus::marker::unsafe_fn); } } // After construct_at since it may write into the field if it's in tail @@ -224,7 +224,7 @@ struct StorageVoid { std::construct_at(&u.err, ::sus::move(o.u.err)); std::destroy_at(&o.u.err); break; - case Moved: sus_unreachable_unchecked(::sus::marker::unsafe_fn); + case Moved: sus::unreachable_unchecked(::sus::marker::unsafe_fn); } } // After construct_at since it may write into the field if it's in tail @@ -419,7 +419,7 @@ struct StorageNonVoid { switch (o.state) { case Ok: std::construct_at(&u.ok, o.u.ok); break; case Err: std::construct_at(&u.err, o.u.err); break; - case Moved: sus_unreachable_unchecked(::sus::marker::unsafe_fn); + case Moved: sus::unreachable_unchecked(::sus::marker::unsafe_fn); } } // After construct_at since it may write into the field if it's in tail @@ -482,7 +482,7 @@ struct StorageNonVoid { std::construct_at(&u.err, ::sus::move(o.u.err)); std::destroy_at(&o.u.err); break; - case Moved: sus_unreachable_unchecked(::sus::marker::unsafe_fn); + case Moved: sus::unreachable_unchecked(::sus::marker::unsafe_fn); } } // After construct_at since it may write into the field if it's in tail diff --git a/sus/result/result.h b/sus/result/result.h index 166025972..a3ac8ed83 100644 --- a/sus/result/result.h +++ b/sus/result/result.h @@ -624,7 +624,7 @@ class [[nodiscard]] Result final { // construction, possibly because the `state_` gets clobbered below? // The signed code version at https://godbolt.org/z/Gax47shsb improves // greatly when the compiler is informed about the UB here. - sus_unreachable_unchecked(::sus::marker::unsafe_fn); + sus::unreachable_unchecked(::sus::marker::unsafe_fn); } return storage_.template take_ok(); } @@ -660,7 +660,7 @@ class [[nodiscard]] Result final { if (!storage_.is_err()) { // Match the code in unwrap_unchecked, and tell the compiler that the // `state_` is an Err before clobbering it. - sus_unreachable_unchecked(::sus::marker::unsafe_fn); + sus::unreachable_unchecked(::sus::marker::unsafe_fn); } return storage_.take_err(); } From a06b5b7e2ef5899054b8921e0206d54bf0e3099a Mon Sep 17 00:00:00 2001 From: Christopher Di Bella Date: Tue, 29 Apr 2025 18:26:58 +0000 Subject: [PATCH 06/11] adds `SUS_PANIC_ELIDE_MESSAGE` to suppress message handlers This makes it possible for any call to `sus::panic` to be equivalent to calling the panic handler directly. Users can then have a nice message emitted during debug builds, and an optimised binary in release builds. --- sus/CMakeLists.txt | 17 +++++- sus/assertions/panic.cc | 26 ++++----- sus/assertions/panic.h | 99 ++++++++++++-------------------- sus/assertions/panic_unittest.cc | 24 +++++--- sus/error/error.h | 2 +- 5 files changed, 83 insertions(+), 85 deletions(-) diff --git a/sus/CMakeLists.txt b/sus/CMakeLists.txt index c52d372d3..982a7c569 100644 --- a/sus/CMakeLists.txt +++ b/sus/CMakeLists.txt @@ -327,6 +327,10 @@ if(${SUBSPACE_BUILD_TESTS}) "tuple/tuple_unittest.cc" ) + add_executable(subspace_panic_elide_message_unittest + "assertions/panic_unittest.cc" + ) + add_executable(subspace_overflow_unittests "num/i8_overflow_unittest.cc" "num/i16_overflow_unittest.cc" @@ -354,6 +358,18 @@ if(${SUBSPACE_BUILD_TESTS}) ) gtest_discover_tests(subspace_unittests) + # Subspace panic unittests + subspace_test_default_compile_options(subspace_panic_elide_message_unittest) + target_compile_options(subspace_panic_elide_message_unittest PUBLIC + -DSUS_PANIC_ELIDE_MESSAGE + ) + target_link_libraries(subspace_panic_elide_message_unittest + subspace::lib + subspace::test_support + gtest_main + ) + gtest_discover_tests(subspace_panic_elide_message_unittest) + # Subspace overflow unittests subspace_test_default_compile_options(subspace_overflow_unittests) target_compile_options(subspace_overflow_unittests PUBLIC @@ -366,4 +382,3 @@ if(${SUBSPACE_BUILD_TESTS}) ) gtest_discover_tests(subspace_overflow_unittests) endif() - diff --git a/sus/assertions/panic.cc b/sus/assertions/panic.cc index b8dde7c95..81062d60a 100644 --- a/sus/assertions/panic.cc +++ b/sus/assertions/panic.cc @@ -20,24 +20,22 @@ namespace sus::assertions::__private { -// Defined outside the header to avoid fprintf in the header. -void print_panic_message(const char* msg, - const PanicLocation& location) noexcept { - fprintf(stderr, "PANIC! at '%s', %s:%u:%u\n", msg, location.file_name, - location.line, location.column); +static void print_panic_location(const PanicLocation& location) noexcept { + fprintf(stderr, "PANIC! at %s:%u:%u\n", location.file_name, location.line, + location.column); } +// Defined outside the header to avoid fprintf in the header. void print_panic_message(std::string_view msg, const PanicLocation& location) noexcept { - fprintf(stderr, "PANIC! at '"); - for (char c : msg) fprintf(stderr, "%c", c); - fprintf(stderr, "', %s:%u:%u\n", location.file_name, location.line, - location.column); -} - -void print_panic_location(const PanicLocation& location) noexcept { - fprintf(stderr, "PANIC! at %s:%u:%u\n", location.file_name, location.line, - location.column); + if (msg.empty()) { + print_panic_location(location); + } else { + fputs("PANIC! at '", stderr); + for (char c : msg) fprintf(stderr, "%c", c); + fprintf(stderr, "', %s:%u:%u\n", location.file_name, location.line, + location.column); + } } } // namespace sus::assertions::__private diff --git a/sus/assertions/panic.h b/sus/assertions/panic.h index 2cff548e4..68beb3090 100644 --- a/sus/assertions/panic.h +++ b/sus/assertions/panic.h @@ -56,82 +56,59 @@ struct PanicLocation { }; namespace __private { -void print_panic_message(const char& msg, - const PanicLocation& location) noexcept; void print_panic_message(std::string_view msg, const PanicLocation& location) noexcept; -void print_panic_location(const PanicLocation& location) noexcept; } // namespace __private -#if defined(SUS_PROVIDE_PRINT_PANIC_LOCATION_HANDLER) -# define _sus_panic_location_handler(loc) \ - SUS_PROVIDE_PRINT_PANIC_LOCATION_HANDLER(loc) -#else -# define _sus_panic_location_handler(loc) \ - ::sus::assertions::__private::print_panic_location(loc) -#endif - -#if defined(SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER) -# define _sus_panic_message_handler(msg, loc) \ - SUS_PROVIDE_PRINT_PANIC_LOCATION_HANDLER(msg, loc) -#else -# define _sus_panic_message_handler(msg, loc) \ - ::sus::assertions::__private::print_panic_message(msg, loc) -#endif - -#if defined(SUS_PROVIDE_PANIC_HANDLER) -# define _sus_panic_handler() SUS_PROVIDE_PANIC_HANDLER() -#elif __has_builtin(__builtin_trap) -# define _sus_panic_handler() __builtin_trap() -#else -# define _sus_panic_handler() std::abort() -#endif - -/// Terminate the program. +/// Terminate the program, after printing a message. /// /// The default behaviour of this function is to `__builtin_trap()` when /// possible and [`std::abort()`]( -/// https://en.cppreference.com/w/cpp/utility/program/abort) otherwise. -/// The behaviour of this function can be overridden by defining a -/// `SUS_PROVIDE_PANIC_HANDLER()` macro when compiling the library. +/// https://en.cppreference.com/w/cpp/utility/program/abort) otherwise. The +/// behaviour of this function can be overridden by defining a +/// `SUS_PROVIDE_PANIC_HANDLER()` macro when compiling the library. The panic +/// message will be printed to stderr before aborting. This behaviour can be +/// overridden by defining a `SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER()` macro +/// when compiling the library. Message handling can be suppressed entirely by +/// defining a `SUS_PANIC_ELIDE_MESSAGE` macro. This can be advantageous for +/// optimised builds, as it turns `panic` into just calling the panic handler. +/// `SUS_PROVIDE_PANIC_HANDLER`, `SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER`, and +/// `SUS_PANIC_ELIDE_MESSAGE` must all have a consistent definition when +/// building Subspace and any binaries that link against it (this means you can +/// change them for different build configurations, but cannot change them for +/// different targets within the same build configuration). If used as a shared +/// library, they cannot be modified by the calling code. /// -/// The panic message will be printed to stderr before aborting. This behaviour -/// can be overridden by defining a `SUS_PROVIDE_PRINT_PANIC_LOCATION_HANDLER()` -/// macro when compiling. The same handler must be used as when building the -/// library itself. So if used as a shared library, it can not be modified by -/// the calling code. The `SUS_PROVIDE_PRINT_PANIC_LOCATION_HANDLER()` macro -/// receives a single argument which is a [`PanicLocation`]( -/// $sus::assertions::PanicLocation). +/// The `SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER()` macro receives two arguments: +/// * A message, which is a `const char*`, a `std::string_view` or a +/// `std::string`. Overloads should be used to handle each case. +/// * A [`PanicLocation`]($sus::assertions::PanicLocation). +/// If the `SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER` macro does not consume the +/// `msg`, this macro will avoid instantiating it at all. /// /// # Safety /// /// If `SUS_PROVIDE_PANIC_HANDLER()` is defined, the macro _must_ not return or /// Undefined Behaviour will result. -[[noreturn, gnu::always_inline, gnu::nodebug]] inline void panic(::sus::assertions::PanicLocation loc = ::sus::assertions::PanicLocation::current()) noexcept +[[noreturn, gnu::always_inline, gnu::nodebug]] +inline void panic(std::string_view message = "", + PanicLocation loc = PanicLocation::current()) noexcept { - _sus_panic_location_handler(loc); - _sus_panic_handler(); -} +#if !defined(SUS_PANIC_ELIDE_MESSAGE) +# if defined(SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER) + SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER(message, loc); +# else + ::sus::assertions::__private::print_panic_message(message, loc); +# endif // SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER +#endif // SUS_PANIC_ELIDE_MESSAGE -/// Terminate the program, after printing a message. -/// -/// The default behaviour of this function is to abort(). The behaviour of this -/// function can be overridden by defining a `SUS_PROVIDE_PANIC_HANDLER()` macro -/// when compiling the library. -/// -/// The panic message will be printed to stderr before aborting. This behaviour -/// can be overridden by defining a `SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER()` -/// macro when compiling the library. The -/// `SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER()` macro receives two arguments: -/// * A message, which is a `const char*`, a `std::string_view` or a -/// `std::string`. Overloads should be used to handle each case. -/// * A [`PanicLocation`]($sus::assertions::PanicLocation). -/// If the `SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER` macro does not consume the -/// `msg`, this macro will avoid instantiating it at all. -[[noreturn, gnu::always_inline, gnu::nodebug]] inline void panic(std::string_view message, ::sus::assertions::PanicLocation loc = ::sus::assertions::PanicLocation::current()) noexcept -{ - _sus_panic_message_handler(message, loc); - _sus_panic_handler(); +#if defined(SUS_PROVIDE_PANIC_HANDLER) + SUS_PROVIDE_PANIC_HANDLER(); +#elif __has_builtin(__builtin_trap) + __builtin_trap(); +#else + std::abort(); +#endif // SUS_PROVIDE_PANIC_HANDLER } } // namespace sus::assertions diff --git a/sus/assertions/panic_unittest.cc b/sus/assertions/panic_unittest.cc index ee9614698..0647d0b4c 100644 --- a/sus/assertions/panic_unittest.cc +++ b/sus/assertions/panic_unittest.cc @@ -14,7 +14,9 @@ #include "sus/assertions/panic.h" +#include "fmt/format.h" #include "googletest/include/gtest/gtest.h" +#include "panic.h" // Incredibly, on Posix we can use [0-9] but on Windows we can't. Yet on Windows // we can use `\d` and on Posix we can't (or it doesn't match). @@ -24,32 +26,38 @@ # define DIGIT "[0-9]" #endif +// TODO: add cases for `SUS_PROVIDE_PANIC_HANDLER` and +// `SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER` when it's clear how to test them +// with modules. +#if defined(SUS_PANIC_ELIDE_MESSAGE) +# define EXPECTED_MESSAGE(message) "^$" +#else +# define EXPECTED_MESSAGE(message) "^PANIC! at " message ".*panic_unittest.cc:" DIGIT "+:" DIGIT "+\n$" +#endif + namespace sus { namespace { + TEST(PanicDeathTest, Panic) { #if GTEST_HAS_DEATH_TEST EXPECT_DEATH(sus::panic(), - "^PANIC! at .*panic_unittest.cc:" DIGIT "+:" DIGIT "+\n$"); + EXPECTED_MESSAGE("")); #endif } TEST(PanicDeathTest, WithMessage) { #if GTEST_HAS_DEATH_TEST - EXPECT_DEATH(sus::panic("hello world"), - "^PANIC! at 'hello world', .*panic_unittest.cc:" DIGIT "+:" DIGIT - "+\n$"); + EXPECT_DEATH(sus::panic("hello world"), EXPECTED_MESSAGE("'hello world', ")); #endif #if GTEST_HAS_DEATH_TEST EXPECT_DEATH(sus::panic( std::string_view("hello world123").substr(0u, 11u)), - "^PANIC! at 'hello world', .*panic_unittest.cc:" DIGIT "+:" DIGIT - "+\n$"); + EXPECTED_MESSAGE("'hello world', ")); #endif #if GTEST_HAS_DEATH_TEST EXPECT_DEATH(sus::panic(std::string("hello world")), - "^PANIC! at 'hello world', .*panic_unittest.cc:" DIGIT "+:" DIGIT - "+\n$"); + EXPECTED_MESSAGE("'hello world', ")); #endif } diff --git a/sus/error/error.h b/sus/error/error.h index 2d74460ce..8c8b77fa6 100644 --- a/sus/error/error.h +++ b/sus/error/error.h @@ -43,7 +43,7 @@ namespace sus { /// responsibilities they cover: /// /// * [`sus::panic`]($sus::panic) (Constructing, Propagating) -/// * [`SUS_PROVIDE_PRINT_PANIC_LOCATION_HANDLER`]($sus::panic) (Reporting) +/// * [`SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER`]($sus::panic) (Reporting) /// * [`SUS_PROVIDE_PANIC_HANDLER`]($sus::panic) (Reacting) /// /// The following are the primary interfaces of the error system and the From cd1dbdc2881d189ec274bdc6e0508ed51cc44ce5 Mon Sep 17 00:00:00 2001 From: Christopher Di Bella Date: Tue, 29 Apr 2025 18:37:47 +0000 Subject: [PATCH 07/11] suppresses GCC attribute warnings --- sus/assertions/panic.h | 3 +++ sus/assertions/unreachable.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/sus/assertions/panic.h b/sus/assertions/panic.h index 68beb3090..f82094dc9 100644 --- a/sus/assertions/panic.h +++ b/sus/assertions/panic.h @@ -90,7 +90,10 @@ void print_panic_message(std::string_view msg, /// /// If `SUS_PROVIDE_PANIC_HANDLER()` is defined, the macro _must_ not return or /// Undefined Behaviour will result. +#pragma GCC diagnostics push +#pragma GCC diagnostics ignored "-Wattributes" [[noreturn, gnu::always_inline, gnu::nodebug]] +#pragma GCC diagnostics pop inline void panic(std::string_view message = "", PanicLocation loc = PanicLocation::current()) noexcept { diff --git a/sus/assertions/unreachable.h b/sus/assertions/unreachable.h index d9c2caf71..379f08db2 100644 --- a/sus/assertions/unreachable.h +++ b/sus/assertions/unreachable.h @@ -20,6 +20,8 @@ #include "sus/marker/unsafe.h" namespace sus::assertions { +#pragma GCC diagnostics push +#pragma GCC diagnostics ignored "-Wattributes" /// Indicates to the developer that the location should not be reached, and /// terminates the program with a [`panic`]($sus::panic). /// @@ -57,6 +59,7 @@ namespace sus::assertions { __assume(false); #endif } +#pragma GCC diagnostics pop } // namespace ::sus::assertions namespace sus { From 62655060455a7ce7d0c5de3b4016e6cbd077a564 Mon Sep 17 00:00:00 2001 From: Christopher Di Bella Date: Tue, 29 Apr 2025 18:39:07 +0000 Subject: [PATCH 08/11] catches missed `sus_unreachable_unchecked` --- subdoc/lib/friendly_names.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subdoc/lib/friendly_names.h b/subdoc/lib/friendly_names.h index d09fd9bdb..03a435f8a 100644 --- a/subdoc/lib/friendly_names.h +++ b/subdoc/lib/friendly_names.h @@ -67,7 +67,7 @@ inline std::string friendly_record_type_name(RecordType t, case RecordType::Struct: return capitalize ? "Struct" : "struct"; case RecordType::Union: return capitalize ? "Union" : "union"; } - sus_unreachable_unchecked(unsafe_fn); + sus::unreachable_unchecked(unsafe_fn); } } // namespace subdoc From 87d039f44e070bb5da0c4fd9942c868864e87157 Mon Sep 17 00:00:00 2001 From: Christopher Di Bella Date: Tue, 29 Apr 2025 18:46:25 +0000 Subject: [PATCH 09/11] s/pragma GCC diagnostics/pragma GCC diagnostic/g --- sus/assertions/panic.h | 6 +++--- sus/assertions/unreachable.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sus/assertions/panic.h b/sus/assertions/panic.h index f82094dc9..8a2eadf54 100644 --- a/sus/assertions/panic.h +++ b/sus/assertions/panic.h @@ -90,10 +90,9 @@ void print_panic_message(std::string_view msg, /// /// If `SUS_PROVIDE_PANIC_HANDLER()` is defined, the macro _must_ not return or /// Undefined Behaviour will result. -#pragma GCC diagnostics push -#pragma GCC diagnostics ignored "-Wattributes" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wattributes" [[noreturn, gnu::always_inline, gnu::nodebug]] -#pragma GCC diagnostics pop inline void panic(std::string_view message = "", PanicLocation loc = PanicLocation::current()) noexcept { @@ -113,6 +112,7 @@ inline void panic(std::string_view message = "", std::abort(); #endif // SUS_PROVIDE_PANIC_HANDLER } +#pragma GCC diagnostic pop } // namespace sus::assertions diff --git a/sus/assertions/unreachable.h b/sus/assertions/unreachable.h index 379f08db2..2ddba1fbf 100644 --- a/sus/assertions/unreachable.h +++ b/sus/assertions/unreachable.h @@ -20,8 +20,8 @@ #include "sus/marker/unsafe.h" namespace sus::assertions { -#pragma GCC diagnostics push -#pragma GCC diagnostics ignored "-Wattributes" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wattributes" /// Indicates to the developer that the location should not be reached, and /// terminates the program with a [`panic`]($sus::panic). /// @@ -59,7 +59,7 @@ namespace sus::assertions { __assume(false); #endif } -#pragma GCC diagnostics pop +#pragma GCC diagnostic pop } // namespace ::sus::assertions namespace sus { From 9ca4a289ef89a4f09c34a03e29468ce7b67dc9da Mon Sep 17 00:00:00 2001 From: Christopher Di Bella Date: Tue, 29 Apr 2025 18:47:40 +0000 Subject: [PATCH 10/11] s/sus_unreachable/sus::unreachable/g --- subdoc/lib/requires.cc | 10 +++++----- subdoc/lib/type.cc | 22 +++++++++++----------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/subdoc/lib/requires.cc b/subdoc/lib/requires.cc index 2e9e97bb9..136f7941e 100644 --- a/subdoc/lib/requires.cc +++ b/subdoc/lib/requires.cc @@ -29,7 +29,7 @@ std::string template_arg_to_string(const clang::TemplateArgumentLoc& loc, // How can this happen in a concept instantiation? arg.dump(); fmt::println(""); - sus_unreachable(); + sus::unreachable(); case clang::TemplateArgument::ArgKind::Type: { if (arg.getAsType()->isDependentType()) { // A template argument that is a template parameter (from the function, @@ -43,7 +43,7 @@ std::string template_arg_to_string(const clang::TemplateArgumentLoc& loc, // How can this happen in a concept instantiation? arg.dump(); fmt::println(""); - sus_unreachable(); + sus::unreachable(); case clang::TemplateArgument::ArgKind::NullPtr: return "nullptr"; case clang::TemplateArgument::ArgKind::Integral: { return llvm_int_to_string(arg.getAsIntegral()); @@ -57,19 +57,19 @@ std::string template_arg_to_string(const clang::TemplateArgumentLoc& loc, // How can this happen in a concept instantiation? arg.dump(); fmt::println(""); - sus_unreachable(); + sus::unreachable(); case clang::TemplateArgument::ArgKind::TemplateExpansion: // How can this happen in a concept instantiation? arg.dump(); fmt::println(""); - sus_unreachable(); + sus::unreachable(); case clang::TemplateArgument::ArgKind::Expression: return stmt_to_string(*arg.getAsExpr(), context.getSourceManager(), preprocessor); case clang::TemplateArgument::ArgKind::Pack: return std::string("TODO: pack"); } - sus_unreachable(); + sus::unreachable(); }; void requires_constraints_add_expr(RequiresConstraints& constraints, diff --git a/subdoc/lib/type.cc b/subdoc/lib/type.cc index fc8c5d6fc..7aa7e8995 100644 --- a/subdoc/lib/type.cc +++ b/subdoc/lib/type.cc @@ -170,7 +170,7 @@ clang::DeclContext* find_context(const clang::Type* type, case clang::NestedNameSpecifier::Super: return spec->getAsRecordDecl()->getDeclContext(); } - sus_unreachable(); + sus::unreachable(); } else if (auto* tag_type = clang::dyn_cast(type)) { return tag_type->getDecl()->getDeclContext(); } else if (auto* spec_type = @@ -201,7 +201,7 @@ clang::DeclContext* find_context(const clang::Type* type, } else { type->dump(); loc.dump(sm); - sus_unreachable(); // Find the context. + sus::unreachable(); // Find the context. } } @@ -221,7 +221,7 @@ TypeOrValue build_template_param( arg.dump(); fmt::println(stderr, ""); loc.dump(sm); - sus_unreachable(); + sus::unreachable(); case clang::TemplateArgument::ArgKind::Type: return TypeOrValue(TypeOrValueChoice::with( build_local_type_internal(arg.getAsType(), template_params, cx, @@ -261,16 +261,16 @@ TypeOrValue build_template_param( arg.dump(); fmt::println(stderr, ""); loc.dump(sm); - sus_unreachable(); + sus::unreachable(); case clang::TemplateArgument::ArgKind::Expression: return TypeOrValue(TypeOrValueChoice::with( stmt_to_string(*arg.getAsExpr(), sm, preprocessor))); case clang::TemplateArgument::ArgKind::Pack: // Packs are handled at a higher level since they produce multiple types. loc.dump(sm); - sus_unreachable(); + sus::unreachable(); } - sus_unreachable(); + sus::unreachable(); }; clang::QualType unwrap_skipped_types(clang::QualType q) noexcept { @@ -348,7 +348,7 @@ Type build_local_type_internal( qualtype->dump(); loc.dump(sm); fmt::println(stderr, "\nkind: {}", (int)kind); - sus_unreachable(); + sus::unreachable(); } spec = spec->getPrefix(); } @@ -374,7 +374,7 @@ Type build_local_type_internal( if (is_pack) { qualtype->dump(); loc.dump(sm); - sus_unreachable(); + sus::unreachable(); } // Arrays come with the var name wrapped in parens, which must be removed. @@ -397,7 +397,7 @@ Type build_local_type_internal( if (auto* vararr = clang::dyn_cast(type)) { qualtype->dump(); loc.dump(sm); - sus_unreachable(); // This is a C thing, not C++. + sus::unreachable(); // This is a C thing, not C++. } // For arrays the root qualifiers come from the element type. @@ -496,7 +496,7 @@ Type build_local_type_internal( // Partial specialization in another type? partial->dump(); loc.dump(sm); - sus_unreachable(); + sus::unreachable(); } else if (auto* full = clang::dyn_cast( rec_type->getDecl())) { @@ -539,7 +539,7 @@ Type build_local_type_internal( // No template parameters. // qualtype->dump(); // loc.dump(sm); - // sus_unreachable(); + // sus::unreachable(); } // Find the context from which to collect the namespace/record paths. From 228cb49807d03ef1c1248928c19fb166b8bee44e Mon Sep 17 00:00:00 2001 From: Christopher Di Bella Date: Thu, 8 May 2025 08:41:00 -0700 Subject: [PATCH 11/11] Apply suggestions from code review Co-authored-by: Dana Jansens --- sus/assertions/check.h | 4 +- sus/assertions/panic.cc | 2 +- sus/assertions/panic.h | 10 +-- sus/assertions/unreachable.h | 2 +- .../__private/slice_mut_methods.inc | 2 +- sus/collections/iterators/drain.h | 2 +- sus/env/var.h | 2 +- sus/iter/generator.h | 2 +- sus/num/__private/intrinsics.h | 4 +- sus/num/try_from_int_error_impl.h | 2 +- sus/option/option.h | 18 ++--- sus/result/__private/storage.h | 16 ++--- sus/result/result.h | 70 +++++++++---------- 13 files changed, 68 insertions(+), 68 deletions(-) diff --git a/sus/assertions/check.h b/sus/assertions/check.h index 67a656c44..063ce8dbd 100644 --- a/sus/assertions/check.h +++ b/sus/assertions/check.h @@ -26,7 +26,7 @@ /// [`sus::panic`]($sus::panic) as described there. #define sus_check(...) \ if (![](bool x) { return x; }(__VA_ARGS__)) [[unlikely]] { \ - sus::panic(); \ + ::sus::panic(); \ } \ static_assert(true) @@ -43,6 +43,6 @@ /// this macro will avoid instantiating it at all. #define sus_check_with_message(cond, msg) \ if (!(cond)) [[unlikely]] { \ - sus::panic(msg); \ + ::sus::panic(msg); \ } \ static_assert(true) diff --git a/sus/assertions/panic.cc b/sus/assertions/panic.cc index 81062d60a..79ec4e7db 100644 --- a/sus/assertions/panic.cc +++ b/sus/assertions/panic.cc @@ -31,7 +31,7 @@ void print_panic_message(std::string_view msg, if (msg.empty()) { print_panic_location(location); } else { - fputs("PANIC! at '", stderr); + fprintf(stderr, "PANIC! at '"); for (char c : msg) fprintf(stderr, "%c", c); fprintf(stderr, "', %s:%u:%u\n", location.file_name, location.line, location.column); diff --git a/sus/assertions/panic.h b/sus/assertions/panic.h index 8a2eadf54..325fdc72b 100644 --- a/sus/assertions/panic.h +++ b/sus/assertions/panic.h @@ -66,18 +66,18 @@ void print_panic_message(std::string_view msg, /// possible and [`std::abort()`]( /// https://en.cppreference.com/w/cpp/utility/program/abort) otherwise. The /// behaviour of this function can be overridden by defining a -/// `SUS_PROVIDE_PANIC_HANDLER()` macro when compiling the library. The panic +/// `SUS_PROVIDE_PANIC_HANDLER()` macro when compiling. The panic /// message will be printed to stderr before aborting. This behaviour can be /// overridden by defining a `SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER()` macro -/// when compiling the library. Message handling can be suppressed entirely by +/// when compiling. Message handling can be suppressed entirely by /// defining a `SUS_PANIC_ELIDE_MESSAGE` macro. This can be advantageous for /// optimised builds, as it turns `panic` into just calling the panic handler. /// `SUS_PROVIDE_PANIC_HANDLER`, `SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER`, and /// `SUS_PANIC_ELIDE_MESSAGE` must all have a consistent definition when -/// building Subspace and any binaries that link against it (this means you can +/// building Subspace and any binaries that link against it. This means you can /// change them for different build configurations, but cannot change them for -/// different targets within the same build configuration). If used as a shared -/// library, they cannot be modified by the calling code. +/// different targets within the same build configuration. If used as a shared +/// library, the compilation of calling code must match how the Subspace library was built. /// /// The `SUS_PROVIDE_PRINT_PANIC_MESSAGE_HANDLER()` macro receives two arguments: /// * A message, which is a `const char*`, a `std::string_view` or a diff --git a/sus/assertions/unreachable.h b/sus/assertions/unreachable.h index 2ddba1fbf..0af660f36 100644 --- a/sus/assertions/unreachable.h +++ b/sus/assertions/unreachable.h @@ -40,7 +40,7 @@ namespace sus::assertions { /// [`sus::unreachable_unchecked`]($sus::unreachable_unchecked) to /// indicate to the compiler the code is not reachable. [[noreturn, gnu::always_inline, gnu::nodebug]] inline void unreachable(PanicLocation loc = PanicLocation::current()) { - [[clang::always_inline]] sus::panic("entered unreachable code", loc); + [[clang::always_inline]] ::sus::panic("entered unreachable code", loc); } /// Indicates to the compiler that the location will never be reached, allowing diff --git a/sus/collections/__private/slice_mut_methods.inc b/sus/collections/__private/slice_mut_methods.inc index efe48c599..92536d52a 100644 --- a/sus/collections/__private/slice_mut_methods.inc +++ b/sus/collections/__private/slice_mut_methods.inc @@ -597,7 +597,7 @@ constexpr RSplitNMut rsplitn_mut(usize n, index < len(), "partition_at_index index greater than length of slice"); // TODO: Requires Iterator::enumerate(), max_by(), min_by() - sus::unreachable(); + ::sus::unreachable(); } /// Reorder the slice with a key extraction function such that the element at diff --git a/sus/collections/iterators/drain.h b/sus/collections/iterators/drain.h index d781173ce..ad48b760f 100644 --- a/sus/collections/iterators/drain.h +++ b/sus/collections/iterators/drain.h @@ -73,7 +73,7 @@ struct [[nodiscard]] Drain final /// /// Calling this function will always panic. constexpr Drain& operator=(Drain&&) noexcept { - sus::panic("attempt to assign to Drain iterator"); + ::sus::panic("attempt to assign to Drain iterator"); } ~Drain() noexcept { diff --git a/sus/env/var.h b/sus/env/var.h index 5af23fb2e..e56a72e70 100644 --- a/sus/env/var.h +++ b/sus/env/var.h @@ -84,6 +84,6 @@ struct sus::error::ErrorImpl<::sus::env::VarError> { case ::sus::env::VarError::InvalidKeyEncoding: return "InvalidKeyEncoding"; } - sus::unreachable(); + ::sus::unreachable(); } }; diff --git a/sus/iter/generator.h b/sus/iter/generator.h index 28f53ed03..32aedfedc 100644 --- a/sus/iter/generator.h +++ b/sus/iter/generator.h @@ -105,7 +105,7 @@ class IterPromise { constexpr auto initial_suspend() noexcept { return std::suspend_always(); } constexpr auto final_suspend() noexcept { return std::suspend_always(); } - constexpr void unhandled_exception() noexcept { sus::unreachable(); } + constexpr void unhandled_exception() noexcept { ::sus::unreachable(); } constexpr Option take() & noexcept { return yielded_.take(); } diff --git a/sus/num/__private/intrinsics.h b/sus/num/__private/intrinsics.h index e731f20ef..73d07f53c 100644 --- a/sus/num/__private/intrinsics.h +++ b/sus/num/__private/intrinsics.h @@ -1768,7 +1768,7 @@ __sus_pure_const constexpr inline ::sus::num::FpCategory float_category( case norm: return ::sus::num::FpCategory::Normal; case subnorm: return ::sus::num::FpCategory::Subnormal; case zero: return ::sus::num::FpCategory::Zero; - default: sus::unreachable_unchecked(::sus::marker::unsafe_fn); + default: ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } } #else @@ -1790,7 +1790,7 @@ __sus_pure_const constexpr inline ::sus::num::FpCategory float_category( case FP_NORMAL: return ::sus::num::FpCategory::Normal; case FP_SUBNORMAL: return ::sus::num::FpCategory::Subnormal; case FP_ZERO: return ::sus::num::FpCategory::Zero; - default: sus::unreachable_unchecked(::sus::marker::unsafe_fn); + default: ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } } } diff --git a/sus/num/try_from_int_error_impl.h b/sus/num/try_from_int_error_impl.h index 70184adc8..f71ed99cf 100644 --- a/sus/num/try_from_int_error_impl.h +++ b/sus/num/try_from_int_error_impl.h @@ -53,7 +53,7 @@ struct sus::error::ErrorImpl { switch (e.kind()) { case sus::num::TryFromIntError::Kind::OutOfBounds: return "out of bounds"; } - sus::unreachable_unchecked(::sus::marker::unsafe_fn); + ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } }; diff --git a/sus/option/option.h b/sus/option/option.h index f2856360f..1ffd5c0bb 100644 --- a/sus/option/option.h +++ b/sus/option/option.h @@ -853,7 +853,7 @@ class Option final { // Result::unwrap_unchecked benefits from telling the compiler explicitly // that the other states are never set. Match that here until shown it's // actually not useful. - sus::unreachable_unchecked(::sus::marker::unsafe_fn); + ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } } constexpr inline T unwrap_unchecked( @@ -1724,7 +1724,7 @@ class Option final { r.as_value_unchecked(::sus::marker::unsafe_fn)); case None: return r.is_none(); } - sus::unreachable_unchecked(::sus::marker::unsafe_fn); + ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } template requires(::sus::cmp::Eq) @@ -1736,7 +1736,7 @@ class Option final { r.as_value_unchecked(::sus::marker::unsafe_fn)); case None: return r.is_none(); } - sus::unreachable_unchecked(::sus::marker::unsafe_fn); + ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } template @@ -1776,7 +1776,7 @@ class Option final { else return std::strong_ordering::equivalent; } - sus::unreachable_unchecked(::sus::marker::unsafe_fn); + ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } template requires(::sus::cmp::ExclusiveStrongOrd) @@ -1796,7 +1796,7 @@ class Option final { else return std::strong_ordering::equivalent; } - sus::unreachable_unchecked(::sus::marker::unsafe_fn); + ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } // sus::cmp::Ord> trait. @@ -1818,7 +1818,7 @@ class Option final { else return std::weak_ordering::equivalent; } - sus::unreachable_unchecked(::sus::marker::unsafe_fn); + ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } template requires(::sus::cmp::ExclusiveOrd) @@ -1838,7 +1838,7 @@ class Option final { else return std::weak_ordering::equivalent; } - sus::unreachable_unchecked(::sus::marker::unsafe_fn); + ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } // sus::cmp::PartialOrd> trait. @@ -1860,7 +1860,7 @@ class Option final { else return std::partial_ordering::equivalent; } - sus::unreachable_unchecked(::sus::marker::unsafe_fn); + ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } friend constexpr inline std::partial_ordering operator<=>( const Option& l, const Option& r) noexcept @@ -1880,7 +1880,7 @@ class Option final { else return std::partial_ordering::equivalent; } - sus::unreachable_unchecked(::sus::marker::unsafe_fn); + ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } template diff --git a/sus/result/__private/storage.h b/sus/result/__private/storage.h index 9f01a73b3..e8a558ef1 100644 --- a/sus/result/__private/storage.h +++ b/sus/result/__private/storage.h @@ -138,7 +138,7 @@ struct StorageVoid { switch (o.state) { case Ok: break; case Err: std::construct_at(&u.err, o.u.err); break; - case Moved: sus::panic("Result used after move"); + case Moved: ::sus::panic("Result used after move"); } // After construct_at since it may write into the field if it's in tail // padding. @@ -171,7 +171,7 @@ struct StorageVoid { switch (o.state) { case Ok: break; case Err: std::construct_at(&u.err, o.u.err); break; - case Moved: sus::unreachable_unchecked(::sus::marker::unsafe_fn); + case Moved: ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } } // After construct_at since it may write into the field if it's in tail @@ -189,7 +189,7 @@ struct StorageVoid { switch (o.state) { case Ok: break; case Err: std::construct_at(&u.err, ::sus::move(o.u.err)); break; - case Moved: sus::panic("Result used after move"); + case Moved: ::sus::panic("Result used after move"); } // After construct_at since it may write into the field if it's in tail // padding. @@ -224,7 +224,7 @@ struct StorageVoid { std::construct_at(&u.err, ::sus::move(o.u.err)); std::destroy_at(&o.u.err); break; - case Moved: sus::unreachable_unchecked(::sus::marker::unsafe_fn); + case Moved: ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } } // After construct_at since it may write into the field if it's in tail @@ -384,7 +384,7 @@ struct StorageNonVoid { switch (o.state) { case Ok: std::construct_at(&u.ok, o.u.ok); break; case Err: std::construct_at(&u.err, o.u.err); break; - case Moved: sus::panic("Result used after move"); + case Moved: ::sus::panic("Result used after move"); } // After construct_at since it may write into the field if it's in tail // padding. @@ -419,7 +419,7 @@ struct StorageNonVoid { switch (o.state) { case Ok: std::construct_at(&u.ok, o.u.ok); break; case Err: std::construct_at(&u.err, o.u.err); break; - case Moved: sus::unreachable_unchecked(::sus::marker::unsafe_fn); + case Moved: ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } } // After construct_at since it may write into the field if it's in tail @@ -441,7 +441,7 @@ struct StorageNonVoid { switch (o.state) { case Ok: std::construct_at(&u.ok, ::sus::move(o.u.ok)); break; case Err: std::construct_at(&u.err, ::sus::move(o.u.err)); break; - case Moved: sus::panic("Result used after move"); + case Moved: ::sus::panic("Result used after move"); } // After construct_at since it may write into the field if it's in tail // padding. @@ -482,7 +482,7 @@ struct StorageNonVoid { std::construct_at(&u.err, ::sus::move(o.u.err)); std::destroy_at(&o.u.err); break; - case Moved: sus::unreachable_unchecked(::sus::marker::unsafe_fn); + case Moved: ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } } // After construct_at since it may write into the field if it's in tail diff --git a/sus/result/result.h b/sus/result/result.h index a3ac8ed83..db52c163c 100644 --- a/sus/result/result.h +++ b/sus/result/result.h @@ -241,7 +241,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return Result(WITH_ERR, ::sus::clone(storage_.get_err())); } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } @@ -251,7 +251,7 @@ class [[nodiscard]] Result final { !(::sus::mem::CopyOrRefOrVoid && ::sus::mem::Copy)) { if (source.storage_.is_moved()) [[unlikely]] { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } else if (&source == this) [[unlikely]] { // Nothing to do. } else if (storage_.is_moved()) { @@ -429,7 +429,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return sus::fn::ReturnOnce::with_err(storage_.take_err()); } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } template <::sus::fn::FnOnce<::sus::fn::NonVoid()> AndFn> @@ -444,7 +444,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return sus::fn::ReturnOnce::with_err(storage_.take_err()); } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } /// Converts from `Result` to [`Option`]($sus::option::Option). @@ -463,7 +463,7 @@ class [[nodiscard]] Result final { storage_.drop_err(); return Option(); } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } @@ -478,7 +478,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return Option(storage_.take_err()); } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } @@ -493,12 +493,12 @@ class [[nodiscard]] Result final { return storage_.template get_ok(); } else if (storage_.is_err()) { if constexpr (fmt::is_formattable::value) { - sus::panic(fmt::to_string(storage_.get_err())); + ::sus::panic(fmt::to_string(storage_.get_err())); } else { - sus::panic("Result has error state"); + ::sus::panic("Result has error state"); } } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } constexpr const std::remove_reference_t& as_value() && = delete; @@ -514,12 +514,12 @@ class [[nodiscard]] Result final { return storage_.template get_ok_mut(); } else if (storage_.is_err()) { if constexpr (fmt::is_formattable::value) { - sus::panic(fmt::to_string(storage_.get_err())); + ::sus::panic(fmt::to_string(storage_.get_err())); } else { - sus::panic("Result has error state"); + ::sus::panic("Result has error state"); } } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } /// Returns a const reference to the contained `Err` value. @@ -531,14 +531,14 @@ class [[nodiscard]] Result final { return storage_.get_err(); } else if (storage_.is_ok()) { if constexpr (std::is_void_v) { - sus::panic("Result has ok state"); + ::sus::panic("Result has ok state"); } else if constexpr (!fmt::is_formattable::value) { - sus::panic("Result has ok state"); + ::sus::panic("Result has ok state"); } else { - sus::panic(fmt::to_string(storage_.template get_ok())); + ::sus::panic(fmt::to_string(storage_.template get_ok())); } } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } constexpr const E& as_err() && = delete; @@ -557,12 +557,12 @@ class [[nodiscard]] Result final { return storage_.template take_ok(); } else if (storage_.is_err()) { if constexpr (fmt::is_formattable::value) { - sus::panic(fmt::to_string(storage_.get_err())); + ::sus::panic(fmt::to_string(storage_.get_err())); } else { - sus::panic("Result has error state"); + ::sus::panic("Result has error state"); } } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } @@ -580,12 +580,12 @@ class [[nodiscard]] Result final { return storage_.template take_ok(); } else if (storage_.is_err()) { if constexpr (fmt::is_formattable::value) { - sus::panic(fmt::format("{}: {}", msg, storage_.get_err())); + ::sus::panic(fmt::format("{}: {}", msg, storage_.get_err())); } else { - sus::panic(msg); + ::sus::panic(msg); } } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } @@ -606,7 +606,7 @@ class [[nodiscard]] Result final { else return; } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } @@ -624,7 +624,7 @@ class [[nodiscard]] Result final { // construction, possibly because the `state_` gets clobbered below? // The signed code version at https://godbolt.org/z/Gax47shsb improves // greatly when the compiler is informed about the UB here. - sus::unreachable_unchecked(::sus::marker::unsafe_fn); + ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } return storage_.template take_ok(); } @@ -638,14 +638,14 @@ class [[nodiscard]] Result final { return storage_.take_err(); } else if (storage_.is_ok()) { if constexpr (std::is_void_v) { - sus::panic("Result has ok state"); + ::sus::panic("Result has ok state"); } else if constexpr (!fmt::is_formattable::value) { - sus::panic("Result has ok state"); + ::sus::panic("Result has ok state"); } else { - sus::panic(fmt::to_string(storage_.template get_ok())); + ::sus::panic(fmt::to_string(storage_.template get_ok())); } } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } @@ -660,7 +660,7 @@ class [[nodiscard]] Result final { if (!storage_.is_err()) { // Match the code in unwrap_unchecked, and tell the compiler that the // `state_` is an Err before clobbering it. - sus::unreachable_unchecked(::sus::marker::unsafe_fn); + ::sus::unreachable_unchecked(::sus::marker::unsafe_fn); } return storage_.take_err(); } @@ -683,7 +683,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return ::sus::fn::call_once(::sus::move(op), storage_.take_err()); } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } @@ -700,7 +700,7 @@ class [[nodiscard]] Result final { return ::sus::option::OptionIter&>( Option&>()); } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } constexpr ::sus::option::OptionIter< @@ -717,7 +717,7 @@ class [[nodiscard]] Result final { return ::sus::option::OptionIter&>( Option&>()); } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } @@ -730,7 +730,7 @@ class [[nodiscard]] Result final { } else if (storage_.is_err()) { return ::sus::option::OptionIter(Option()); } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } constexpr ::sus::option::OptionIter iter_mut() && noexcept @@ -744,7 +744,7 @@ class [[nodiscard]] Result final { storage_.drop_err(); return ::sus::option::OptionIter(Option()); } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } } @@ -758,7 +758,7 @@ class [[nodiscard]] Result final { storage_.drop_err(); return ::sus::option::OptionIter(Option()); } else { - sus::panic("Result used after move"); + ::sus::panic("Result used after move"); } }