https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127675

--- Comment #10 from GCC Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Jonathan Wakely <[email protected]>:

https://gcc.gnu.org/g:c1a660d44be3dc140b7ff24611e8ff7a4557d761

commit r17-4841-gc1a660d44be3dc140b7ff24611e8ff7a4557d761
Author: Jonathan Wakely <[email protected]>
Date:   Thu Oct 1 12:32:41 2026 +0100

    libstdc++: Fix debug mode for pb_ds ordered-vector trees [PR127675]

    As noted in the bugzilla PR, there are debug mode assertions in the
    ov_tree_map and ov_tree_set code which fail occasionally (but not
    consistently because the tests use the current time as a random seed).

    This change first allows the time-based random seed to be overridden
    using the same GLIBCXX_SEED_TEST_RNG environment variable already
    checked elsewhere in the testsuite. This allows failures to be triggered
    reliably.

    The bug in erase_if is an exception-safety bug where most state is
    rolled back cleanly if an exception occurs, but some modifications to
    the debug_base::m_keys list are not rolled back. This leaves the
    container in an inconsistent state where the values in m_keys do not
    match the contents of the container. This triggers debug assertions on
    the next operation when the container checks its invariants.

    The bug in erase(It) is much simpler and easier to trigger: the function
    calls alloc.allocate(m_size - 1) unconditionally, even if m_size == 1.
    It is unspecified what allocators return for zero-sized allocations, so
    this can return a non-null pointer.  That is then inconsistent with the
    debug assertions which require m_a_values == nullptr to be true when
    m_size == 0. The solution here is to simply avoid allocating anything
    when m_size == 1.

    There's a similar latent bug in the 4-param overload of
    copy_from_ordered_range, which would call allocate(0) if called with two
    empty ranges. Although its only caller today already checks for empty
    ranges, it would be more robust to make copy_from_ordered_range safe for
    this case. That avoids a potential future bug where the debug assertions
    fail again because copy_from_ordered_range allocates a non-null pointer
    for m_size == 0. The change made in this commit is to return early if
    len == 0, which is exactly what the 2-param overload of
    copy_from_ordered_range already does.

    Finally, the split and join members of the debug_base are refactored to
    use std::list::splice instead of inserting and erasing elements from the
    two lists. This means they perform no allocations and no copies, so
    cannot throw exceptions (unless the comparison function passed to split
    throws).  Although this doesn't fix any known bugs, it makes the callers
    of those functions more robust and makes it easier to ensure those
    callers are exception-safe.

    libstdc++-v3/ChangeLog:

            PR libstdc++/127675
            * include/ext/pb_ds/detail/debug_map_base.hpp (split, join): Use
            std::list::splice to move elements without reallocating.
            *
include/ext/pb_ds/detail/ov_tree_map_/constructors_destructor_fn_imps.hpp
            (copy_from_ordered_range(It, It, It, It)): Do not allocate new
            storage for an empty container.
            * include/ext/pb_ds/detail/ov_tree_map_/erase_fn_imps.hpp
            (erase_if): Do not modify this->debug_base::m_keys until all
            allocations and other potentially-throwing operations have
            completed successfully.
            (erase_imp): Do not allocate new storage for an empty container.
            * testsuite/ext/pb_ds/regression/127675.cc: New test.
            * testsuite/util/testsuite_rng.h (get_time_determined_seed): Use
            getenv("GLIBCXX_SEED_TEST_RNG") to allow the seed to be fixed.

    Reviewed-by: Tomasz KamiÅski <[email protected]>

Reply via email to