Skip to content

Add gcc15,clang21,clang22 workflows to ci#135

Closed
20162026 wants to merge 1 commit intobemanproject:mainfrom
20162026:fix/more-ci-compilers
Closed

Add gcc15,clang21,clang22 workflows to ci#135
20162026 wants to merge 1 commit intobemanproject:mainfrom
20162026:fix/more-ci-compilers

Conversation

@20162026
Copy link
Copy Markdown
Collaborator

@20162026 20162026 commented Apr 28, 2026

testing inplace_vector with newer compiler version

also running Asan and Tsan for all buils feels wasteful and hella slow, furthermore I dont really see a point of running Tsan for inplace_vector at all...

@20162026
Copy link
Copy Markdown
Collaborator Author

the only warning, I have noticed occures when compiling with gcc15

27/29] "/usr/sbin/ctest" --instrument --command-type compile --target-name beman.inplace_vector.tests.modifiers --config Release --build-dir "/__w/inplace_vector/inplace_vector/build" --output tests/beman/inplace_vector/CMakeFiles/beman.inplace_vector.tests.modifiers.dir/Release/modifiers.test.cpp.o --source /__w/inplace_vector/inplace_vector/tests/beman/inplace_vector/modifiers.test.cpp --language CXX --  /usr/sbin/g++ -DCMAKE_INTDIR=\"Release\" -I/__w/inplace_vector/inplace_vector/include -I/__w/inplace_vector/inplace_vector/build/include -isystem /__w/inplace_vector/inplace_vector/build/_deps/googletest-src/googletest/include -isystem /__w/inplace_vector/inplace_vector/build/_deps/googletest-src/googletest -O3  -O3 -DNDEBUG -std=gnu++23 -MD -MT tests/beman/inplace_vector/CMakeFiles/beman.inplace_vector.tests.modifiers.dir/Release/modifiers.test.cpp.o -MF tests/beman/inplace_vector/CMakeFiles/beman.inplace_vector.tests.modifiers.dir/Release/modifiers.test.cpp.o.d -o tests/beman/inplace_vector/CMakeFiles/be
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/15/include/g++-v15/bits/ranges_algobase.h:41,
                 from /usr/lib/gcc/x86_64-pc-linux-gnu/15/include/g++-v15/numeric:95,
                 from /__w/inplace_vector/inplace_vector/tests/beman/inplace_vector/modifiers.test.cpp:1:
In function 'constexpr _OutIter std::__copy_move_a2(_InIter, _Sent, _OutIter) [with bool _IsMove = true; _InIter = NonTriviallyDefaultConstructible*; _Sent = NonTriviallyDefaultConstructible*; _OutIter = NonTriviallyDefaultConstructible*]',
    inlined from 'constexpr _OI std::__copy_move_a1(_II, _II, _OI) [with bool _IsMove = true; _II = NonTriviallyDefaultConstructible*; _OI = NonTriviallyDefaultConstructible*]' at /usr/lib/gcc/x86_64-pc-linux-gnu/15/include/g++-v15/bits/stl_algobase.h:492:42,
    inlined from 'constexpr _OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = true; _II = NonTriviallyDefaultConstructible*; _OI = NonTriviallyDefaultConstructible*]' at /usr/lib/gcc/x86_64-pc-linux-gnu/15/include/g++-v15/bits/stl_algobase.h:500:31,
    inlined from 'constexpr _OI std::move(_II, _II, _OI) [with _II = NonTriviallyDefaultConstructible*; _OI = NonTriviallyDefaultConstructible*]' at /usr/lib/gcc/x86_64-pc-linux-gnu/15/include/g++-v15/bits/stl_algobase.h:674:38,
    inlined from 'constexpr T* beman::inplace_vector::details::inplace_vector_base<T, N>::erase(const_iterator, const_iterator) requires  movable<T> [with T = NonTriviallyDefaultConstructible; long unsigned int N = 1]' at /__w/inplace_vector/inplace_vector/include/beman/inplace_vector/inplace_vector.hpp:382:21,
    inlined from 'void {anonymous}::Modifiers_EraseRange_Test<gtest_TypeParam_>::TestBody() [with gtest_TypeParam_ = TestParam<NonTriviallyDefaultConstructible, 1>]' at /__w/inplace_vector/inplace_vector/tests/beman/inplace_vector/modifiers.test.cpp:878:21:
/usr/lib/gcc/x86_64-pc-linux-gnu/15/include/g++-v15/bits/stl_algobase.h:426:32: warning: 'void* __builtin_memmove(void*, const void*, long unsigned int)' reading between 5 and 1016 bytes from a region of size 4 [-Wstringop-overread]
  426 |               __builtin_memmove(_GLIBCXX_TO_ADDR(__result),
      |               ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
  427 |                                 _GLIBCXX_TO_ADDR(__first),
      |                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~
  428 |                                 __n * sizeof(*__first));
      |                                 ~~~~~~~~~~~~~~~~~~~~~~~
/__w/inplace_vector/inplace_vector/tests/beman/inplace_vector/modifiers.test.cpp: In member function 'void {anonymous}::Modifiers_EraseRange_Test<gtest_TypeParam_>::TestBody() [with gtest_TypeParam_ = TestParam<NonTriviallyDefaultConstructible, 1>]':
/__w/inplace_vector/inplace_vector/tests/beman/inplace_vector/modifiers.test.cpp:869:6: note: at offset 4 into source object 'device' of size 8
  869 |   IV device(reference);
      |      ^~~~~~
In function 'constexpr _OutIter std::__copy_move_a2(_InIter, _Sent, _OutIter) [with bool _IsMove = true; _InIter = Trivial*; _Sent = Trivial*; _OutIter = Trivial*]',
    inlined from 'constexpr _OI std::__copy_move_a1(_II, _II, _OI) [with bool _IsMove = true; _II = Trivial*; _OI = Trivial*]' at /usr/lib/gcc/x86_64-pc-linux-gnu/15/include/g++-v15/bits/stl_algobase.h:492:42,
    inlined from 'constexpr _OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = true; _II = Trivial*; _OI = Trivial*]' at /usr/lib/gcc/x86_64-pc-linux-gnu/15/include/g++-v15/bits/stl_algobase.h:500:31,
    inlined from 'constexpr _OI std::move(_II, _II, _OI) [with _II = Trivial*; _OI = Trivial*]' at /usr/lib/gcc/x86_64-pc-linux-gnu/15/include/g++-v15/bits/stl_algobase.h:674:38,
    inlined from 'constexpr T* beman::inplace_vector::details::inplace_vector_base<T, N>::erase(const_iterator, const_iterator) requires  movable<T> [with T = Trivial; long unsigned int N = 1]' at /__w/inplace_vector/inplace_vector/include/beman/inplace_vector/inplace_vector.hpp:382:21,
    inlined from 'void {anonymous}::Modifiers_EraseRange_Test<gtest_TypeParam_>::TestBody() [with gtest_TypeParam_ = TestParam<Trivial, 1>]' at /__w/inplace_vector/inplace_vector/tests/beman/inplace_vector/modifiers.test.cpp:878:21:
/usr/lib/gcc/x86_64-pc-linux-gnu/15/include/g++-v15/bits/stl_algobase.h:426:32: warning: 'void* __builtin_memmove(void*, const void*, long unsigned int)' reading between 5 and 1016 bytes from a region of size 4 [-Wstringop-overread]
  426 |               __builtin_memmove(_GLIBCXX_TO_ADDR(__result),
      |               ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
  427 |                                 _GLIBCXX_TO_ADDR(__first),
      |                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~
  428 |                                 __n * sizeof(*__first));
      |                                 ~~~~~~~~~~~~~~~~~~~~~~~
/__w/inplace_vector/inplace_vector/tests/beman/inplace_vector/modifiers.test.cpp: In member function 'void {anonymous}::Modifiers_EraseRange_Test<gtest_TypeParam_>::TestBody() [with gtest_TypeParam_ = TestParam<Trivial, 1>]':
/__w/inplace_vector/inplace_vector/tests/beman/inplace_vector/modifiers.test.cpp:869:6: note: at offset 4 into source object 'device' of size 8
  869 |   IV device(reference);
      |      ^~~~~~

@20162026
Copy link
Copy Markdown
Collaborator Author

20162026 commented Apr 28, 2026

according to opus 4.7 previously mentioned gcc15 warning is false positive,


That's GCC 15's -Wstringop-overread firing on erase(first, last) at include/beman/inplace_vector/inplace_vector.hpp:382. With N=1, the storage is one element wide (4 or 8 bytes), but GCC can't prove last - first <= size() so it assumes the std::move source range could be up to 1016 bytes — hence "reading between 5 and 1016 bytes from a region of size 4."

Ranked, with the tradeoff that drives each choice:

1. [[assume]] in the header

[[assume(static_cast<size_type>(last - first) <= size() - static_cast<size_type>(first - begin()))]];
  • For: Encodes the standard's existing precondition in the API contract. Fixes the warning for all callers. Lets the optimizer use the bound. Spec-clean — only formalizes UB the standard already grants.
  • Against: Buggy precondition arithmetic could be silently optimized away. Couples to compiler analysis quality.
  • Pick when: You see this as a library contract issue.

2. Localized #pragma in the header

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-overread"
unsafe_destroy(std::move(...), end());
#pragma GCC diagnostic pop
  • For: Zero codegen change, zero new UB, no optimizer coupling. Honest "known false positive" acknowledgment. Fixes for all callers.
  • Against: GCC-specific (guard with #if defined(__GNUC__) && !defined(__clang__)). Disables the diagnostic at this site forever.
  • Pick when: You'd rather not encode assumptions.

3. Localized #pragma in the test

  • For: Header stays clean. Suppression co-located with the only known trigger.
  • Against: Whack-a-mole — every future N=1 erase caller will see it again.
  • Pick when: You're sure this is a one-off and don't want to touch the header.

4. Hand-rolled move loop

for (iterator src = f + (last-first), dst = f; src != end(); ++src, ++dst)
  *dst = std::move(*src);
  • For: No pragma, no [[assume]], no UB strengthening, no optimizer coupling.
  • Against: Loses std::move's memmove specialization for trivial T (usually re-vectorized, not guaranteed).
  • Pick when: You want zero compiler-specific knobs and accept potential minor perf cost.

Recommendation

Default to #1 ([[assume]] in header) — only fix that documents the contract, helps the optimizer, aligns with the standard, and works for every caller.

Fall back to #2 (header pragma) to avoid assumption hints. Use #3 (test pragma) only if the library shouldn't change. Reach for #4 (loop) if opposed to compiler-specific knobs.

@ednolan
Copy link
Copy Markdown
Member

ednolan commented May 1, 2026

Superseded by #138.

@ednolan ednolan closed this May 1, 2026
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