fix: Don't mark Any copy operations noexcept#5495
Merged
Merged
Conversation
Contributor
860f7d9 to
7dfcd6c
Compare
paulgessinger
commented
May 22, 2026
The value constructor, copy constructor and copy assignment were marked noexcept(kAnyNoexcept) (i.e. noexcept in normal builds), but for types larger than the small buffer they allocate on the heap and run the stored type's copy/constructor. A std::bad_alloc or a throwing copy therefore called std::terminate. std::any has the same shape and deliberately leaves its copy operations potentially-throwing; only moves (a pointer steal for heap values) are noexcept. Remove noexcept from the value constructor, copy constructor and copy assignment so these exceptions propagate. Keep the move constructor and move assignment noexcept so std::vector<Any> still moves rather than copies on reallocation. Relaxing the copy-assignment noexcept exposes a pre-existing exception-safety hole: when the new value's copy throws, the target was left with its handler set but no value, so destruction would operate on uninitialized storage (assert/zombie). Reset the handler to empty on throw so a failed copy assignment leaves the target empty. Add a regression test and static_asserts pinning the exception specifications. https://claude.ai/code/session_013by5hPDzWJCe9gjXNDZjLs
AnyTests.cpp is compiled into two targets: the normal build and an AnyDebug build that defines _ACTS_ANY_ENABLE_TRACK_ALLOCATIONS (which sets kAnyNoexcept = false so the tracking hooks may throw). The new static_asserts assumed the move operations are always noexcept, which is only true in the normal build, so they failed to compile the AnyDebug target. Tie the move assertions to detail::kAnyNoexcept. Also wrap the AnyCopyExceptionPropagates body in an inner scope so the held value is destroyed before CHECK_ANY_ALLOCATIONS(), matching the other test cases (otherwise its still-live allocation trips the leak check in the tracking build). https://claude.ai/code/session_013by5hPDzWJCe9gjXNDZjLs
clang-tidy's readability-named-parameter flagged the unnamed parameter in HeapCopyThrows's throwing copy constructor. Add a /*unused*/ comment to match the project convention, and apply clang-format (wraps the long static_assert line). https://claude.ai/code/session_013by5hPDzWJCe9gjXNDZjLs
Add an explicit !std::is_base_of_v<AnyBaseAll, std::decay_t<T>> guard to the forwarding-reference constructor. This was already enforced indirectly (isStorable returns false for AnyBaseAll-derived types), but stating it at the constructor makes the exclusion of recursive/self construction explicit and robust against future changes to isStorable. https://claude.ai/code/session_013by5hPDzWJCe9gjXNDZjLs
7dfcd6c to
c669468
Compare
andiwand
approved these changes
May 22, 2026
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Let's try not making the copy operations in Any noexcept. They mask actual paths that can throw (the contained object's constructor). For vector optimization, I think the moves being noexcept should be sufficient.
This also fixes an exception bug that was masked by noexcept before, where an exception on copy would produce an Any pointing at invalid memory.