Skip to content

Fix exceptions from allocator-taking VLA move construction - #207

Merged
thirtytwobits merged 2 commits into
mainfrom
codex/fix-vla-allocator-move-noexcept
Oct 5, 2026
Merged

thirtytwobits merged 2 commits into
mainfrom
codex/fix-vla-allocator-move-noexcept

Conversation

@thirtytwobits

Copy link
Copy Markdown
Member

An allocator-taking VLA move can allocate new storage when the allocators differ. Its unconditional noexcept currently turns an allocation failure into std::terminate, preventing the caller from catching std::bad_alloc.

Allow exceptions to propagate through the allocating base constructor and make both the generic and packed-bool public constructors' exception specifications match the existing allocator dispatch. Ordinary moves and the existing unconditional storage-transfer paths retain noexcept.

Add regression tests for allocation failure, preservation and subsequent reuse of the source, equal-allocator moves without allocation, and the exception specifications for all allocator-trait combinations. Capacity bookkeeping and partial-construction cleanup remain tracked separately in #205 and #37.

Closes #204.

Validation:

  • The new compile-time regression checks fail on the original implementation for both int and bool.
  • The original issue reproducer now catches std::bad_alloc and exits successfully.
  • All seven VLA suites pass in C++14 under GCC 13.4 and Clang 22.1.8, in Debug and exceptions-disabled ReleaseEP configurations (existing compiler/platform skips retained).
  • Both affected test translation units pass C++17, C++20, and C++23 compilation under GCC and Clang, with exceptions enabled and disabled.
  • Formatting checks for the changed regions and git diff --check pass.

@thirtytwobits
thirtytwobits force-pushed the codex/fix-vla-allocator-move-noexcept branch from 69da911 to ff3de7f Compare October 1, 2026 17:05
@thirtytwobits
thirtytwobits requested a balanced review from Copilot October 1, 2026 17:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

POCMA-only allocators can still transfer storage between unequal allocators, causing deallocation through the wrong allocator.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Updates allocator-aware VLA move construction so allocation failures can propagate.

Changes:

  • Removes unconditional noexcept from allocating moves.
  • Adds allocation-failure and allocator-trait regression tests.
  • Preserves no-throw ordinary and storage-transfer moves.
File Description
include/​cetl/​variable_length_array.hpp Adjusts move-constructor exception specifications.
cetlvast/​suites/​unittest/​test_variable_length_array_copy_and_move.cpp Tests allocator failure and source preservation.
cetlvast/​suites/​unittest/​test_variable_length_array_compiles.cpp Adds compile-time exception-specification checks.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread include/cetl/variable_length_array.hpp Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

POCMA is incorrectly used to permit storage transfer and noexcept construction with potentially unequal stateful allocators.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
Resolved since last review (1)

Comment thread cetlvast/suites/unittest/test_variable_length_array_compiles.cpp Outdated
Comment thread include/cetl/variable_length_array.hpp
propagate_on_container_move_assignment applies to assignment and says
nothing about whether the allocator passed to a constructor equals the
source's. The base move constructor used it to select the overload that
adopts the source's storage, so a POCMA allocator that is not always equal
took the source's buffer even when the supplied allocator was a different
instance. The destination later deallocated that buffer through the wrong
allocator, and the public constructors were marked noexcept even though
they must allocate in that case.

- Select the storage-adopting base constructor only when is_always_equal.
  Otherwise use the runtime equality check, which allocates when unequal.
- Base the public allocator-extended move constructors' noexcept on
  is_always_equal only, for both VariableLengthArray and the bool
  specialization.
- Expect a POCMA allocator that is not always equal to be potentially
  throwing in the compile test.
- Run the allocation-failure tests with both POCMA settings. With the
  previous dispatch the POCMA cases don't throw and deallocate through
  the wrong allocator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thirtytwobits
thirtytwobits merged commit c14f871 into main Oct 5, 2026
24 checks passed
@thirtytwobits
thirtytwobits deleted the codex/fix-vla-allocator-move-noexcept branch October 5, 2026 21:13
thirtytwobits added a commit that referenced this pull request Oct 5, 2026
Moving a VLA with an unequal allocator allocates only the source's
occupied storage but records the source's full capacity. Spare capacity
is therefore reported as usable memory even though it was never
allocated, permitting out-of-bounds appends and incorrect deallocation
counts. An empty source with reserved capacity can produce a destination
with null storage and nonzero capacity.

Record the allocated element count as the destination capacity in the
unequal-allocator path. Equal-allocator moves continue transferring the
original allocation and capacity. The shared fix covers both generic VLA
elements and packed bool storage.

Add six regression tests covering nonempty and empty sources with spare
capacity, growth beyond the destination allocation, allocator ownership
and deallocation counts, source reuse, and equal-allocator capacity
transfer for both `int` and `bool`.

Closes #205.

Stacked on #207. This PR targets
`codex/fix-vla-allocator-move-noexcept`, so its diff contains only the
capacity fix and its tests. Merge #207 first, then retarget this PR to
`main`.

Validation:

- Four regression cases fail before the fix; all six pass after it.
- All eight VLA suites pass in C++14 under GCC 13.4 and Clang 22.1.8, in
Debug and exceptions-disabled ReleaseEP configurations, with existing
compiler/platform skips retained.
- The new test suite passes C++17, C++20, and C++23 compilation under
GCC and Clang, with exceptions enabled and disabled.
- The six focused tests pass with Clang AddressSanitizer and
UndefinedBehaviorSanitizer.
- The original reproducer now reports `size=3 capacity=3 allocated=3`,
with no deallocation-count mismatch.
- Formatting and `git diff --check` pass.

The head commit includes the documented `#verification` trigger so the
verification workflow also runs while this PR targets another `codex/`
branch.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

VLA allocator-taking move constructor terminates on allocation failure due to unconditional noexcept

3 participants