fmtlib / fmt

A modern formatting library
https://fmt.dev
Other
20.74k stars 2.49k forks source link

warning for vector<float> but not vector<int> #3481

Closed prlw1 closed 1 year ago

prlw1 commented 1 year ago

Using today's g++ (GCC) 14.0.0 20230610 (experimental) on today's fmt checkout 8fe893c0ac54c83a08c2492a7ac790c1a2bdd577, the following simple code

#include <vector>

#include <fmt/core.h>
#include <fmt/ranges.h>

int main()
{
        std::vector<int> x{1,2,3,4};
        fmt::print("x={}\n", x);
}

compiles and does what you expect.

If I change vector<int> to vector<float> (optionally changing 1 to 1.0f etc.), the code fails to compile.

The head and tail of the error message spew is:

In file included from /usr/local/gcc/include/c++/14.0.0/vector:62,
                 from fmtbug.cc:1:
In static member function ‘static constexpr _Up* std::__copy_move<_IsMove, true, std::random_access_iterator_tag>::__copy_m(_Tp*, _Tp*, _Up*) [with _Tp = unsigned int; _Up = unsigned int; bool _IsMove = false]’,
    inlined from ‘constexpr _OI std::__copy_move_a2(_II, _II, _OI) [with bool _IsMove = false; _II = unsigned int*; _OI = unsigned int*]’ at /usr/local/gcc/include/c++/14.0.0/bits/stl_algobase.h:506:30,
...
    inlined from ‘constexpr OutputIt fmt::v10::detail::write_float(OutputIt, T, fmt::v10::format_specs<Char>, locale_ref) [with Char = char; OutputIt = fmt::v10::appender; T = float]’ at /usr/src/local/fmt/include/fmt/format.h:3580:25:
/usr/local/gcc/include/c++/14.0.0/bits/new_allocator.h:147:55: note: destination object of size 4 allocated by ‘operator new’
  147 |         return static_cast<_Tp*>(_GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp)));
      |                                                       ^
vitaut commented 1 year ago

GCC 14 hasn't been released yet but I wasn't able to repro the issue on the GCC trunk in godbolt: https://godbolt.org/z/MYKTr7cKx. Could you provide a link to the godbolt repro?

prlw1 commented 1 year ago

Very interesting: add -O2 to the flags, and godbolt reproduces it. I was using -std=c++23, but the same effect happens with -std=c++20 as you had.

vitaut commented 1 year ago

OK, it does repro (https://godbolt.org/z/cqjrM3s7e) and it's actually a warning:

/opt/compiler-explorer/gcc-trunk-20230612/include/c++/14.0.0/bits/stl_algobase.h:437:30: warning: 'void* __builtin_memmove(void*, const void*, long unsigned int)' writing between 5 and 9223372036854775807 bytes into a region of size 4 overflows the destination [-Wstringop-overflow=]
  437 |             __builtin_memmove(__result, __first, sizeof(_Tp) * _Num);
      |             ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In member function '_Tp* std::__new_allocator<_Tp>::allocate(size_type, const void*) [with _Tp = unsigned int]',
    inlined from 'constexpr _Tp* std::allocator< <template-parameter-1-1> >::allocate(std::size_t) [with _Tp = unsigned int]' at /opt/compiler-explorer/gcc-trunk-20230612/include/c++/14.0.0/bits/allocator.h:198:40,
    inlined from 'static constexpr _Tp* std::allocator_traits<std::allocator<_Up> >::allocate(allocator_type&, size_type) [with _Tp = unsigned int]' at /opt/compiler-explorer/gcc-trunk-20230612/include/c++/14.0.0/bits/alloc_traits.h:482:28,
    inlined from 'constexpr void fmt::v10::basic_memory_buffer<T, SIZE, Allocator>::grow(size_t) [with T = unsigned int; long unsigned int SIZE = 32; Allocator = std::allocator<unsigned int>]' at /opt/compiler-explorer/libs/fmt/trunk/include/fmt/format.h:943:51,
    inlined from 'constexpr void fmt::v10::detail::buffer<T>::try_reserve(size_t) [with T = unsigned int]' at /opt/compiler-explorer/libs/fmt/trunk/include/fmt/core.h:858:39,
    inlined from 'constexpr void fmt::v10::detail::buffer<T>::try_reserve(size_t) [with T = unsigned int]' at /opt/compiler-explorer/libs/fmt/trunk/include/fmt/core.h:857:24,
    inlined from 'constexpr void fmt::v10::detail::buffer<T>::try_resize(size_t) [with T = unsigned int]' at /opt/compiler-explorer/libs/fmt/trunk/include/fmt/core.h:849:16,
    inlined from 'constexpr void fmt::v10::basic_memory_buffer<T, SIZE, Allocator>::resize(size_t) [with T = unsigned int; long unsigned int SIZE = 32; Allocator = std::allocator<unsigned int>]' at /opt/compiler-explorer/libs/fmt/trunk/include/fmt/format.h:1016:63,
    inlined from 'constexpr void fmt::v10::detail::bigint::assign(UInt) [with UInt = long unsigned int; typename std::enable_if<(std::is_same<UInt, long unsigned int>::value || std::is_same<UInt, __int128 unsigned>::value), int>::type <anonymous> = 0]' at /opt/compiler-explorer/libs/fmt/trunk/include/fmt/format.h:2833:19,
    inlined from 'constexpr void fmt::v10::detail::bigint::operator=(Int) [with Int = long long unsigned int]' at /opt/compiler-explorer/libs/fmt/trunk/include/fmt/format.h:2854:11,
    inlined from 'constexpr void fmt::v10::detail::format_dragon(basic_fp<__int128 unsigned>, unsigned int, int, buffer<char>&, int&)' at /opt/compiler-explorer/libs/fmt/trunk/include/fmt/format.h:3056:19,
    inlined from 'constexpr int fmt::v10::detail::format_float(Float, int, float_specs, buffer<char>&) [with Float = double]' at /opt/compiler-explorer/libs/fmt/trunk/include/fmt/format.h:3524:18:
/opt/compiler-explorer/gcc-trunk-20230612/include/c++/14.0.0/bits/new_allocator.h:147:55: note: destination object of size 4 allocated by 'operator new'
  147 |         return static_cast<_Tp*>(_GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp)));
      |                                                       ^
prlw1 commented 1 year ago

Oh dear: my system build adds -O2 -fPIE -g -Werror to my own CXXFLAGS, which explains why I saw it as an error.

So the surprise is that int & float behave differently, but I suppose this can be closed as NOTABUG.

vitaut commented 1 year ago

This is a false positive suppressed in https://github.com/fmtlib/fmt/commit/977d887a4ea9ae82182d6c26b2114a0fb0cade72. I recommend reporting to GCC.