[deque] fix: copy_construct_impl source pointer, emplace_decision::po…#1279
Merged
Conversation
…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
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");
} |
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. |
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. |
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.
…s type, null-pointer guard in deque(n,val)
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