Skip to content

[deque] fix: copy_construct_impl source pointer, emplace_decision::po…#1279

Merged
trcrsired merged 3 commits into
cppfastio:nextfrom
SekaiArendelle:deque-tests
Jun 14, 2026
Merged

[deque] fix: copy_construct_impl source pointer, emplace_decision::po…#1279
trcrsired merged 3 commits into
cppfastio:nextfrom
SekaiArendelle:deque-tests

Conversation

@SekaiArendelle

Copy link
Copy Markdown

…s type, null-pointer guard in deque(n,val)

  • copy_construct_impl for non-trivial types: use fromcontroller.front_block.curr_ptr instead of controller.front_block.curr_ptr for single-block case
  • emplace_decision::pos changed from ::std::size_t to iterator type
  • deque(size_type, const_reference) add early return for n==0 to avoid null-pointer arithmetic in set_newed_common

Add tests: access, algorithm, assign_range, capacity, compare, concepts, constructors/iter_iter, constructors/n_val, insert_initializer_list, insert_range_iterator, invalidation, move_only, self_reference, swap, type_traits

…s type, null-pointer guard in deque(n,val)

- copy_construct_impl for non-trivial types: use fromcontroller.front_block.curr_ptr
  instead of controller.front_block.curr_ptr for single-block case
- emplace_decision::pos changed from ::std::size_t to iterator type
- deque(size_type, const_reference) add early return for n==0 to avoid
  null-pointer arithmetic in set_newed_common

Add tests: access, algorithm, assign_range, capacity, compare, concepts,
constructors/iter_iter, constructors/n_val, insert_initializer_list,
insert_range_iterator, invalidation, move_only, self_reference, swap,
type_traits
@SekaiArendelle

Copy link
Copy Markdown
Author
// Bug 1: copy_construct_impl uses destination's front_block.curr_ptr
// instead of source's (fromcontroller.front_block.curr_ptr) for the
// single-block non-trivial copy path.
//
// Without fix: copy-constructing a deque of non-trivial elements
// that fit in one block reads from uninitialized destination memory.
//
// Fix: deque.h:2231  lastblockbegin = fromcontroller.front_block.curr_ptr

#include <fast_io.h>
#include <fast_io_dsal/deque.h>
#include <fast_io_dsal/string.h>

int main()
{
        ::fast_io::deque<::fast_io::string> dq;
        for (::std::size_t i{}; i != 50u; ++i)
        {
                dq.emplace_back(::fast_io::concat_fast_io("hello_", i));
        }

        ::fast_io::deque<::fast_io::string> copy = dq;

        for (::std::size_t i{}; i != 50u; ++i)
        {
                if (copy[i] != dq[i])
                {
                        ::fast_io::io::panicln("Bug 1 reproduced: copy[", i, "] != dq[", i, "]");
                }
        }
        ::fast_io::io::print("Bug 1 NOT reproduced (copy matches source)\n");
}

@SekaiArendelle

SekaiArendelle commented Jun 12, 2026

Copy link
Copy Markdown
Author
// Bug 2: deque(size_type n, const_reference val) constructor has no
// early return for n==0, and controller is not zero-initialized.
// This causes UB in set_newed_common via begin() on garbage controller.
//
// Without fix: constructing deque(0, val) invokes UB (uninitialized
// controller members).  Under constexpr evaluation this is a hard error.
// With ASan/UBSan this is caught at runtime.
//
// Fix: deque.h:2344  add ": controller{}" and "if (!n) return;"

#include <fast_io.h>
#include <fast_io_dsal/deque.h>

constexpr bool test()
{
        ::fast_io::deque<int> dq(0, 42);
        return dq.empty();
}
static_assert(test());

int main()
{
        ::fast_io::deque<int> dq(0, 42);
        if (!dq.empty())
        {
                ::fast_io::io::panic("Bug 2 reproduced: deque(0, val) is not empty\n");
        }
        ::fast_io::io::print("Bug 2 NOT reproduced (deque(0, val) is empty)\n");
}
> clang++ repro/bug2_deque_zero_n_val.cc -o build\test.exe -std=c++23 -I include\ -lntdll
repro/bug2_deque_zero_n_val.cc:19:15: error: static assertion expression is not an integral constant expression
   19 | static_assert(test());
      |               ^~~~~~
include/fast_io_dsal/impl/deque.h:1944:58: note: read of uninitialized object is not allowed in a constant expression
 1944 |         ::std::size_t controllerdiff{static_cast<::std::size_t>(a.controller_ptr - b.controller_ptr)};
      |                                                                 ^~~~~~~~~~~~~~~~
include/fast_io_dsal/impl/deque.h:1959:11: note: in call to 'deque_itercontent_diff_impl<fast_io::containers::details::deque_control_block<int>>(dq.controller.back_block, dq.controller.front_block, 1024)'
 1959 |                 deqsz = ::fast_io::containers::details::deque_itercontent_diff_impl(controller.back_block, controller.front_block, block_size);
      |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/fast_io_dsal/impl/deque.h:1993:2: note: in call to 'deque_reserve_back_impl<fast_io::generic_allocator_adapter<fast_io::nt_rtlallocateheap_allocator>, false,
      fast_io::containers::details::deque_controller<int>>(dq.controller, 0, 4, 4, 1024)'
 1993 |         ::fast_io::containers::details::deque_reserve_back_impl<allocator, divsz>(controller, newbackcap, align, sz, block_size);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/fast_io_dsal/impl/deque.h:3605:4: note: in call to 'deque_reserve_back_common<fast_io::generic_allocator_adapter<fast_io::nt_rtlallocateheap_allocator>, 4ULL, 4ULL, 1024ULL, false,
      fast_io::containers::details::deque_controller<int>>(dq.controller, 0)'
 3605 |                         ::fast_io::containers::details::deque_reserve_back_common<allocator, alignof(value_type), sizeof(value_type), block_size, false>(this->controller, backcap);
      |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/fast_io_dsal/impl/deque.h:2348:4: note: in call to 'this->reserve_back(0)'
 2348 |                         this->reserve_back(n);
      |                         ^~~~~~~~~~~~~~~~~~~~~
repro/bug2_deque_zero_n_val.cc:16:24: note: in call to 'deque(0, 42)'
   16 |         ::fast_io::deque<int> dq(0, 42);
      |                               ^~~~~~~~~
repro/bug2_deque_zero_n_val.cc:19:15: note: in call to 'test()'
   19 | static_assert(test());
      |               ^~~~~~
1 error generated.

@SekaiArendelle

Copy link
Copy Markdown
Author
// Bug 3: emplace_decision::pos is ::std::size_t instead of iterator.
// The structured binding in emplace() for the non-nothrow path
// (auto [retit, decision] = emplace_decision_common<false>(iter))
// expects retit to be an iterator, but gets size_t instead.
//
// deque_iterator has no conversion to size_t, so the aggregate
// initialization emplace_decision{retit, ...} fails to compile
// for non-nothrow-constructible types.
//
// Fix: deque.h:3308  ::std::size_t pos → iterator pos

#include <fast_io.h>
#include <fast_io_dsal/deque.h>

struct NonNoThrow
{
        int value;
        NonNoThrow(int v) : value(v) {}
};

int main()
{
        ::fast_io::deque<NonNoThrow> dq;
        dq.push_back(NonNoThrow(1));
        dq.push_back(NonNoThrow(2));

        auto it = dq.emplace(dq.begin(), 42);

        if (it->value != 42 || dq.size() != 3u)
        {
                ::fast_io::io::panic("Bug 3 reproduced: emplace result is wrong\n");
        }
        ::fast_io::io::print("Bug 3 NOT reproduced (emplace works)\n");
}
> clang++ repro/bug3_emplace_non_nothrow.cc -o build\test.exe -std=c++23 -I include\ -lntdll
In file included from repro/bug3_emplace_non_nothrow.cc:13:
In file included from include/fast_io_dsal/deque.h:28:
include/fast_io_dsal/impl/deque.h:3330:30: error: no viable conversion from 'iterator' (aka '::fast_io::containers::details::deque_iterator<NonNoThrow, false>') to '::std::size_t' (aka 'unsigned long long')
 3330 |                                         return emplace_decision{retit, 1};
      |                                                                 ^~~~~
include/fast_io_dsal/impl/deque.h:3490:35: note: in instantiation of function template specialization 'fast_io::containers::deque<NonNoThrow,
      fast_io::generic_allocator_adapter<fast_io::nt_rtlallocateheap_allocator>>::emplace_decision_common<false>' requested here
 3490 |                         auto [retit, decision] = this->emplace_decision_common<false>(iter);
      |                                                        ^
repro/bug3_emplace_non_nothrow.cc:27:15: note: in instantiation of function template specialization 'fast_io::containers::deque<NonNoThrow,
      fast_io::generic_allocator_adapter<fast_io::nt_rtlallocateheap_allocator>>::emplace<int>' requested here
   27 |         auto it = dq.emplace(dq.begin(), 42);
      |                      ^
include/fast_io_dsal/impl/deque.h:338:19: note: candidate function
  338 |         inline constexpr operator deque_iterator<T, true>() const noexcept
      |                          ^
include/fast_io_dsal/impl/deque.h:3358:28: error: no viable conversion from 'iterator' (aka '::fast_io::containers::details::deque_iterator<NonNoThrow, false>') to '::std::size_t' (aka 'unsigned long long')
 3358 |                         return emplace_decision{retit, 0};
      |                                                 ^~~~~
include/fast_io_dsal/impl/deque.h:338:19: note: candidate function
  338 |         inline constexpr operator deque_iterator<T, true>() const noexcept
      |                          ^
include/fast_io_dsal/impl/deque.h:3491:30: error: cannot initialize a member subobject of type 'NonNoThrow *' with an lvalue of type '::std::size_t' (aka 'unsigned long long')
 3491 |                         emplace_guard guard{this, retit, decision};
      |                                                   ^~~~~
repro/bug3_emplace_non_nothrow.cc:27:15: note: in instantiation of function template specialization 'fast_io::containers::deque<NonNoThrow,
      fast_io::generic_allocator_adapter<fast_io::nt_rtlallocateheap_allocator>>::emplace<int>' requested here
   27 |         auto it = dq.emplace(dq.begin(), 42);
      |                      ^
In file included from repro/bug3_emplace_non_nothrow.cc:13:
In file included from include/fast_io_dsal/deque.h:28:
include/fast_io_dsal/impl/deque.h:3491:37: error: cannot initialize a member subobject of type 'NonNoThrow *' with an lvalue of type '::std::int_fast8_t' (aka 'signed char')
 3491 |                         emplace_guard guard{this, retit, decision};
      |                                                          ^~~~~~~~
include/fast_io_dsal/impl/deque.h:3492:29: error: member reference base type '::std::size_t' (aka 'unsigned long long') is not a structure or union
 3492 |                         ::std::construct_at(retit.itercontent.curr_ptr, ::std::forward<Args>(args)...);
      |                                             ~~~~~^~~~~~~~~~~~
include/fast_io_dsal/impl/deque.h:3494:11: error: no viable conversion from returned value of type '::std::size_t' (aka 'unsigned long long') to function return type 'iterator' (aka
      '::fast_io::containers::details::deque_iterator<NonNoThrow, false>')
 3494 |                         return retit;
      |                                ^~~~~
include/fast_io_dsal/impl/deque.h:237:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from '::std::size_t' (aka 'unsigned long long') to
      'const deque_iterator<NonNoThrow, false> &' for 1st argument
  237 | struct deque_iterator
      |        ^~~~~~~~~~~~~~
include/fast_io_dsal/impl/deque.h:237:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from '::std::size_t' (aka 'unsigned long long') to
      'deque_iterator<NonNoThrow, false> &&' for 1st argument
  237 | struct deque_iterator
      |        ^~~~~~~~~~~~~~
6 errors generated.

@trcrsired trcrsired merged commit 7668a55 into cppfastio:next Jun 14, 2026
2 checks passed
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.

2 participants