diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index fa558af..3c123ce 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -1,6 +1,6 @@ { "lockFileVersion": 6, - "moduleFileHash": "f4948b1db3c8426ce61c4e3b27033e236b6dbb6d042cab34daf9aa917ae9f808", + "moduleFileHash": "03f9fa01086ec7d850ba7c8b54fac207fbd6a6a4ec53bd30f19077dcf4673d5a", "flags": { "cmdRegistries": [ "https://bcr.bazel.build/" @@ -1409,7 +1409,7 @@ }, "@@ewdk_cc_toolchain~//:ewdk_extension.bzl%toolchains": { "general": { - "bzlTransitiveDigest": "o8jWc/DKL6u/xdGVR7ZVOsdvrJWHczkHwS5vnpdr1b8=", + "bzlTransitiveDigest": "9JDci1UBSQRcNgV8CaGaG7lk1+ZmfO4FF//420jL10w=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, diff --git a/radiant/Locks.h b/radiant/Locks.h index e2dd3b1..7edadd3 100644 --- a/radiant/Locks.h +++ b/radiant/Locks.h @@ -48,11 +48,15 @@ class RAD_NODISCARD LockExclusive final LockExclusive(LockType& lock) noexcept(noexcept(lock.LockExclusive())) : m_lock(lock) { + RAD_S_ASSERT_NOTHROW(noexcept(lock.LockExclusive())); + m_lock.LockExclusive(); } ~LockExclusive() { + RAD_S_ASSERT_NOTHROW_DTOR(noexcept(m_lock.Unlock())); + m_lock.Unlock(); } @@ -76,11 +80,15 @@ class RAD_NODISCARD LockShared final LockShared(LockType& lock) noexcept(noexcept(lock.LockShared())) : m_lock(lock) { + RAD_S_ASSERT_NOTHROW(noexcept(lock.LockShared())); + m_lock.LockShared(); } ~LockShared() { + RAD_S_ASSERT_NOTHROW_DTOR(noexcept(m_lock.Unlock())); + m_lock.Unlock(); } @@ -116,6 +124,7 @@ class RAD_NODISCARD RelockableExclusive final : m_lock(lock), m_acquired(true) { + RAD_S_ASSERT_NOTHROW(noexcept(lock.LockExclusive())); m_lock.LockExclusive(); } @@ -128,6 +137,8 @@ class RAD_NODISCARD RelockableExclusive final ~RelockableExclusive() { + RAD_S_ASSERT_NOTHROW_DTOR(noexcept(m_lock.Unlock())); + if (m_acquired) { m_lock.Unlock(); @@ -138,6 +149,8 @@ class RAD_NODISCARD RelockableExclusive final /// @warning It is not allowed to call Unlock() if the lock is not acquired. void Unlock() noexcept(noexcept(DeclVal().Unlock())) { + RAD_S_ASSERT_NOTHROW(noexcept(DeclVal().Unlock())); + RAD_ASSERT(m_acquired); m_acquired = false; m_lock.Unlock(); @@ -149,6 +162,8 @@ class RAD_NODISCARD RelockableExclusive final /// acquired. void Lock() noexcept(noexcept(DeclVal().LockExclusive())) { + RAD_S_ASSERT_NOTHROW(noexcept(DeclVal().LockExclusive())); + RAD_ASSERT(!m_acquired); m_lock.LockExclusive(); m_acquired = true; @@ -179,6 +194,8 @@ class RAD_NODISCARD RelockableShared final : m_lock(lock), m_acquired(true) { + RAD_S_ASSERT_NOTHROW(noexcept(lock.LockShared())); + m_lock.LockShared(); } @@ -192,6 +209,8 @@ class RAD_NODISCARD RelockableShared final /// @brief Defer acquiring the lock to a manual call to Lock() ~RelockableShared() { + RAD_S_ASSERT_NOTHROW_DTOR(noexcept(m_lock.Unlock())); + if (m_acquired) { m_lock.Unlock(); @@ -202,6 +221,8 @@ class RAD_NODISCARD RelockableShared final /// @warning It is not allowed to call Unlock() if the lock is not acquired. void Unlock() noexcept(noexcept(DeclVal().Unlock())) { + RAD_S_ASSERT_NOTHROW(noexcept(DeclVal().Unlock())); + RAD_ASSERT(m_acquired); m_acquired = false; m_lock.Unlock(); @@ -213,6 +234,8 @@ class RAD_NODISCARD RelockableShared final /// acquired. void Lock() noexcept(noexcept(DeclVal().LockShared())) { + RAD_S_ASSERT_NOTHROW(noexcept(DeclVal().Unlock())); + RAD_ASSERT(!m_acquired); m_lock.LockShared(); m_acquired = true; diff --git a/radiant/Result.h b/radiant/Result.h index eb77d29..87d0066 100644 --- a/radiant/Result.h +++ b/radiant/Result.h @@ -162,8 +162,7 @@ struct ResultStorage ~ResultStorage() noexcept { - RAD_S_ASSERTMSG(IsNoThrowDtor && IsNoThrowDtor, - "Destructors should not throw!"); + RAD_S_ASSERT_NOTHROW_DTOR(IsNoThrowDtor && IsNoThrowDtor); Destruct(); } @@ -327,6 +326,9 @@ class RAD_NODISCARD Result final : private detail::ResultStorage using typename StorageType::OkType; using typename StorageType::ErrType; + RAD_S_ASSERT_NOTHROW_MOVE((IsNoThrowMoveCtor && IsNoThrowMoveAssign && + IsNoThrowMoveCtor && IsNoThrowMoveAssign)); + constexpr Result() noexcept : StorageType(ResultEmptyTag) { diff --git a/radiant/ScopeExit.h b/radiant/ScopeExit.h index 61717c4..71650b3 100644 --- a/radiant/ScopeExit.h +++ b/radiant/ScopeExit.h @@ -30,8 +30,8 @@ class ScopeExit ~ScopeExit() { - RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); - RAD_S_ASSERTMSG(noexcept(m_fn()), "Destructors should not throw!"); + RAD_S_ASSERT_NOTHROW_DTOR(IsNoThrowDtor); + RAD_S_ASSERT_NOTHROW_DTOR(noexcept(m_fn())); if (m_call) { @@ -76,8 +76,8 @@ class ScopeGuard ~ScopeGuard() { - RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); - RAD_S_ASSERTMSG(noexcept(m_fn()), "Destructors should not throw!"); + RAD_S_ASSERT_NOTHROW_DTOR(IsNoThrowDtor); + RAD_S_ASSERT_NOTHROW_DTOR(noexcept(m_fn())); m_fn(); } diff --git a/radiant/TotallyRad.h b/radiant/TotallyRad.h index eca79ed..8bbf05b 100644 --- a/radiant/TotallyRad.h +++ b/radiant/TotallyRad.h @@ -229,16 +229,54 @@ inline bool DoAssert(const char* Assertion, const char* File, int Line) #define RAD_S_ASSERT(x) static_assert(x, #x) #define RAD_S_ASSERTMSG(x, m) static_assert(x, m) +// +// Enables broad assertions that objects do not throw exceptions. +// #ifndef RAD_ENABLE_NOTHROW_ASSERTIONS #define RAD_ENABLE_NOTHROW_ASSERTIONS 1 #endif - #if RAD_ENABLE_NOTHROW_ASSERTIONS #define RAD_S_ASSERT_NOTHROW(x) RAD_S_ASSERT(x) #define RAD_S_ASSERT_NOTHROWMSG(x, m) RAD_S_ASSERTMSG(x, m) #else -#define RAD_S_ASSERT_NOTHROW(x) ((void)0) -#define RAD_S_ASSERT_NOTHROWMSG(x, m) ((void)0) +#define RAD_S_ASSERT_NOTHROW(x) RAD_S_ASSERT(true) +#define RAD_S_ASSERT_NOTHROWMSG(x, m) RAD_S_ASSERT(true) +#endif + +// +// Enables assertions that destructors do not throw exceptions. +// +// Core Guideline: A destructor must not fail. If a destructor tries to exit +// with an exception, it’s a bad design error and the program had better +// terminate. +// +#ifndef RAD_ENABLE_NOTHROW_DTOR_ASSERTIONS +#define RAD_ENABLE_NOTHROW_DTOR_ASSERTIONS 1 +#endif +#if RAD_ENABLE_NOTHROW_DTOR_ASSERTIONS +#define RAD_S_ASSERT_NOTHROW_DTOR(x) \ + RAD_S_ASSERTMSG(x, "destructors should not throw") +#define RAD_S_ASSERT_NOTHROW_DTOR_T(x) \ + RAD_S_ASSERTMSG(IsNoThrowDtor, "destructors should not throw") +#else +#define RAD_S_ASSERT_NOTHROW_DTOR(x) RAD_S_ASSERT(true) +#endif + +// +// Enables assertions the move operations do not throw exceptions. +// +// Core Guideline: A throwing move violates most people’s reasonable +// assumptions. A non-throwing move will be used more efficiently by +// standard-library and language facilities. +// +#ifndef RAD_ENABLE_NOTHROW_MOVE_ASSERTIONS +#define RAD_ENABLE_NOTHROW_MOVE_ASSERTIONS 1 +#endif +#if RAD_ENABLE_NOTHROW_MOVE_ASSERTIONS +#define RAD_S_ASSERT_NOTHROW_MOVE(x) \ + RAD_S_ASSERTMSG(x, "move operations should not throw") +#else +#define RAD_S_ASSERT_NOTHROW_MOVE(x) RAD_S_ASSERT(true) #endif #define RAD_NOT_COPYABLE(x) \ diff --git a/radiant/TypeWrapper.h b/radiant/TypeWrapper.h index 107a874..b9ed7d1 100644 --- a/radiant/TypeWrapper.h +++ b/radiant/TypeWrapper.h @@ -33,6 +33,8 @@ class TypeWrapper using Type = T; + RAD_S_ASSERT_NOTHROW_MOVE((IsNoThrowMoveCtor && IsNoThrowMoveAssign)); + template , int> = 0> constexpr TypeWrapper() noexcept(IsNoThrowDefaultCtor) : m_value() diff --git a/radiant/Vector.h b/radiant/Vector.h index b4542a1..220b279 100644 --- a/radiant/Vector.h +++ b/radiant/Vector.h @@ -44,6 +44,10 @@ class Vector final static constexpr uint16_t InlineCount = TInlineCount; using AllocatorType = TAllocator; + RAD_S_ASSERT_NOTHROW_MOVE((IsNoThrowMoveCtor && IsNoThrowMoveAssign && + IsNoThrowMoveCtor && + IsNoThrowMoveAssign)); + // // TODO - rad::Vector does not yet support types that might throw exceptions // when manipulating it. It is the intention of the radiant authors for the @@ -60,14 +64,13 @@ class Vector final // itself should be in a valid state when this happens. // RAD_S_ASSERTMSG((IsNoThrowDtor && IsNoThrowDefaultCtor && - IsNoThrowCopyCtor && IsNoThrowMoveCtor && - IsNoThrowCopyAssign && IsNoThrowMoveAssign), + IsNoThrowCopyCtor && IsNoThrowCopyAssign), "rad::Vector does not yet support types that might throw."); ~Vector() { - RAD_S_ASSERTMSG(IsNoThrowDtor && IsNoThrowDtor, - "Destructors should not throw!"); + RAD_S_ASSERT_NOTHROW_DTOR(IsNoThrowDtor && + IsNoThrowDtor); Clear(); Storage().Free(Allocator()); diff --git a/radiant/detail/VectorOperations.h b/radiant/detail/VectorOperations.h index fcc1db5..eeb2529 100644 --- a/radiant/detail/VectorOperations.h +++ b/radiant/detail/VectorOperations.h @@ -45,7 +45,7 @@ struct VectorManipulation template , int> = 0> inline void Dtor(T* item) noexcept { - RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); + RAD_S_ASSERT_NOTHROW_DTOR(IsNoThrowDtor); item->~T(); } @@ -60,7 +60,7 @@ struct VectorManipulation template , int> = 0> inline void DtorRange(T* start, T* end) noexcept { - RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); + RAD_S_ASSERT_NOTHROW_DTOR(IsNoThrowDtor); for (T* p = start; p != end; p++) { @@ -121,7 +121,7 @@ struct VectorManipulation uint32_t count, const T& value) noexcept(IsNoThrowCopyCtor) { - RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); + RAD_S_ASSERT_NOTHROW_DTOR(IsNoThrowDtor); for (uint32_t i = 0; i < count; i++) { @@ -142,7 +142,7 @@ struct VectorManipulation inline void CopyCtorDtorDestRange(T* dest, T* src, uint32_t count) noexcept( IsNoThrowCopyCtor) { - RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); + RAD_S_ASSERT_NOTHROW_DTOR(IsNoThrowDtor); for (uint32_t i = 0; i < count; i++) { @@ -165,7 +165,7 @@ struct VectorManipulation T* src, uint32_t count) noexcept(IsNoThrowMoveCtor) { - RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); + RAD_S_ASSERT_NOTHROW_DTOR(IsNoThrowDtor); for (uint32_t i = 0; i < count; i++) { @@ -197,7 +197,7 @@ struct VectorManipulation inline void MoveCtorDtorSrcRange(T* dest, T* src, uint32_t count) noexcept( IsNoThrowMoveCtor) { - RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); + RAD_S_ASSERT_NOTHROW_DTOR(IsNoThrowDtor); for (uint32_t i = 0; i < count; i++) { diff --git a/test/test_Result.cpp b/test/test_Result.cpp index 54be86d..0de5070 100644 --- a/test/test_Result.cpp +++ b/test/test_Result.cpp @@ -15,7 +15,9 @@ // // Disable nothrow assertions for unit testing // -#define RAD_ENABLE_NOTHROW_ASSERTIONS 0 +#define RAD_ENABLE_NOTHROW_ASSERTIONS 0 +#define RAD_ENABLE_NOTHROW_DTOR_ASSERTIONS 0 +#define RAD_ENABLE_NOTHROW_MOVE_ASSERTIONS 0 #include "gtest/gtest.h" diff --git a/test/test_TypeWrapper.cpp b/test/test_TypeWrapper.cpp index f50becd..6822ddf 100644 --- a/test/test_TypeWrapper.cpp +++ b/test/test_TypeWrapper.cpp @@ -15,7 +15,9 @@ // // Disable nothrow assertions for unit testing // -#define RAD_ENABLE_NOTHROW_ASSERTIONS 0 +#define RAD_ENABLE_NOTHROW_ASSERTIONS 0 +#define RAD_ENABLE_NOTHROW_DTOR_ASSERTIONS 0 +#define RAD_ENABLE_NOTHROW_MOVE_ASSERTIONS 0 #include "gtest/gtest.h" diff --git a/test/test_Utility.cpp b/test/test_Utility.cpp index 89e1760..77a8876 100644 --- a/test/test_Utility.cpp +++ b/test/test_Utility.cpp @@ -15,7 +15,9 @@ // // Disable nothrow assertions for unit testing // -#define RAD_ENABLE_NOTHROW_ASSERTIONS 0 +#define RAD_ENABLE_NOTHROW_ASSERTIONS 0 +#define RAD_ENABLE_NOTHROW_DTOR_ASSERTIONS 0 +#define RAD_ENABLE_NOTHROW_MOVE_ASSERTIONS 0 #include "gtest/gtest.h" diff --git a/test/test_Vector.cpp b/test/test_Vector.cpp index de57504..f3314cf 100644 --- a/test/test_Vector.cpp +++ b/test/test_Vector.cpp @@ -12,6 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +// +// Disable nothrow assertions for unit testing +// +#define RAD_ENABLE_NOTHROW_ASSERTIONS 0 +#define RAD_ENABLE_NOTHROW_DTOR_ASSERTIONS 0 +#define RAD_ENABLE_NOTHROW_MOVE_ASSERTIONS 0 + #include "gtest/gtest.h" #include "test/TestAlloc.h"