Following example will fail to compile. Error message is included below but most important part is here: error: accessing 'std::__cxx11::basic_string<char>::<unnamed union>::_M_allocated_capacity' member instead of initialized 'std::__cxx11::basic_string<char>::<unnamed union>::_M_local_buf' member in constant expression Example: #include <string> #include <functional> #include <algorithm> #include <vector> using namespace std; using namespace std::literals; constexpr auto foo() { const auto vec = vector{ "a"s, "b"s, "c"s }; const auto concat = ranges::fold_left(vec, ""s, plus{}); return concat.size(); } int main() { constexpr auto _ = foo(); } Error message: Using built-in specs. COLLECT_GCC=g++ COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/13.2.1/lto-wrapper Target: x86_64-pc-linux-gnu Configured with: /build/gcc/src/gcc/configure --enable-languages=ada,c,c++,d,fortran,go,lto,objc,obj-c++ --enable-bootstrap --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --with-build-config=bootstrap-lto --with-linker-hash-style=gnu --with-system-zlib --enable-__cxa_atexit --enable-cet=auto --enable-checking=release --enable-clocale=gnu --enable-default-pie --enable-default-ssp --enable-gnu-indirect-function --enable-gnu-unique-object --enable-libstdcxx-backtrace --enable-link-serialization=1 --enable-linker-build-id --enable-lto --enable-multilib --enable-plugin --enable-shared --enable-threads=posix --disable-libssp --disable-libstdcxx-pch --disable-werror Thread model: posix Supported LTO compression algorithms: zlib zstd gcc version 13.2.1 20230801 (GCC) COLLECT_GCC_OPTIONS='-v' '-save-temps' '-std=c++23' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'a-' /usr/lib/gcc/x86_64-pc-linux-gnu/13.2.1/cc1plus -E -quiet -v -D_GNU_SOURCE prog.cpp -mtune=generic -march=x86-64 -std=c++23 -fpch-preprocess -o a-prog.ii ignoring nonexistent directory "/usr/lib/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../x86_64-pc-linux-gnu/include" #include "..." search starts here: #include <...> search starts here: /usr/lib/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../include/c++/13.2.1 /usr/lib/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../include/c++/13.2.1/x86_64-pc-linux-gnu /usr/lib/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../include/c++/13.2.1/backward /usr/lib/gcc/x86_64-pc-linux-gnu/13.2.1/include /usr/local/include /usr/lib/gcc/x86_64-pc-linux-gnu/13.2.1/include-fixed /usr/include End of search list. COLLECT_GCC_OPTIONS='-v' '-save-temps' '-std=c++23' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'a-' /usr/lib/gcc/x86_64-pc-linux-gnu/13.2.1/cc1plus -fpreprocessed a-prog.ii -quiet -dumpdir a- -dumpbase prog.cpp -dumpbase-ext .cpp -mtune=generic -march=x86-64 -std=c++23 -version -o a-prog.s GNU C++23 (GCC) version 13.2.1 20230801 (x86_64-pc-linux-gnu) compiled by GNU C version 13.2.1 20230801, GMP version 6.3.0, MPFR version 4.2.0-p12, MPC version 1.3.1, isl version isl-0.26-GMP warning: MPFR header version 4.2.0-p12 differs from library version 4.2.1. GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 Compiler executable checksum: 5a490a353c29b926850bca65a518c219 prog.cpp: In function ‘int main()’: prog.cpp:16:27: in ‘constexpr’ expansion of ‘foo()’ prog.cpp:16:28: in ‘constexpr’ expansion of ‘std::ranges::__fold_left_fn::operator()(_Range&&, _Tp, _Fp) const [with _Range = const std::vector<std::__cxx11::basic_string<char>, std::allocator<std::__cxx11::basic_string<char> > >&; _Tp = std::__cxx11::basic_string<char>; _Fp = std::plus<void>](vec, std::literals::string_literals::operator""s(const char*, std::size_t)(0), (std::plus<void>(), std::plus<void>()))’ prog.cpp:16:28: in ‘constexpr’ expansion of ‘std::__cxx11::basic_string<char>((* & std::move<__cxx11::basic_string<char>&>(__init)))’ prog.cpp:16:28: error: accessing ‘std::__cxx11::basic_string<char>::<unnamed union>::_M_allocated_capacity’ member instead of initialized ‘std::__cxx11::basic_string<char>::<unnamed union>::_M_local_buf’ member in constant expression 16 | constexpr auto _ = foo(); | ^
I think the libstdc++ code is fine here, suggesting a compiler bug. Slightly reduced: #include <string> #include <vector> using namespace std; using namespace std::literals; struct Fold { template<typename _Iter, typename _Sent, typename _Tp, typename _Fp> constexpr auto operator()(_Iter, _Sent, _Tp __init, _Fp) const { return std::move(__init); } template<typename _Range, typename _Tp, typename _Fp> constexpr auto operator()(_Range&& __r, _Tp __init, _Fp __f) const { return (*this)(__r.begin(), __r.end(), std::move(__init), std::move(__f)); } } fold; constexpr auto foo() { const auto vec = vector{ "a"s, "b"s, "c"s }; return fold(vec, ""s, plus{}); } constexpr auto bar() { return foo().size(); } int main() { constexpr auto _ = bar(); }
Further reduced: #include <string> using namespace std::literals; template<typename T> constexpr auto fold2(T init) { return std::move(init); } template<typename T> constexpr auto fold(T init) { return fold2(std::move(init)); } constexpr auto foo() { return fold(""s); } constexpr auto bar() { return foo().size(); } int main() { constexpr auto i = bar(); return i; }
Further reduced: #include <string> using namespace std::literals; consteval void bar() { auto _ = [](auto s) { return s; }(""s); } int main() { bar(); return 0; }
Or: #include <string> consteval void bar() { auto _ = [](std::string s) { return s; }({}); } int main() { bar(); } Or: #include <string> constexpr auto foo(std::string init) { return init; } constexpr auto bar() { return foo("").size(); } constexpr auto i = bar(); Clang compiles both of these without problems (it can't compile anything using ""s in a constant expression, maybe due to https://github.com/llvm/llvm-project/issues/68527) So I am pretty sure this is a g++ front end bug.
I have been trying to figure out where exactly the bug is and these are my findings. > Or: > > #include <string> > > consteval void bar() { > auto _ = [](std::string s) { return s; }({}); > } > > int main() { > bar(); > } > > Or: > > #include <string> > constexpr auto foo(std::string init) { return init; } > constexpr auto bar() { return foo("").size(); } > constexpr auto i = bar(); > >Clang compiles both of these without problems (it can't compile anything using ""s in a constant expression, maybe due to https://github.com/llvm/llvm-project/issues/68527) > > So I am pretty sure this is a g++ front end bug. If you use libstdc++ on clang these will not compile but with different errors. Then with following example I try to showcase the bug without std::string. Try it out: https://godbolt.org/z/rvoeMEaxc This is bare minimum of libstdc++ basic_string to reproduce this bug: --- struct S { union { char a[1]; }; char* ptr; constexpr S() : ptr{a} { a[0] = {}; } constexpr S(S&&) = delete; constexpr S(const S&) = delete; constexpr S operator=(S&&) = delete; constexpr S operator=(const S&) = delete; constexpr ~S() = default; } --- Then to reproduce the bug instance of this class has to be function parameter and the function has to be constant evaluated. This can happens in std::basic_string move constructor bits/basic_string.h:682 and following tester functions tries to emulate what happens in it. --- // Should always be false constexpr bool test(const S& s){ return s.ptr != s.a; } consteval void tester1(S param = {}) { S notparam = {}; if (test(notparam)){ throw std::logic_error("compiletime notparam!"); } if (test(param)) { // gcc ends up here so fails to compile // in std::basic_string move constructor // compilation would fail due to accessing // inactive union member // clang and msvc never end up here throw std::logic_error("compiletime param"); } } int main() { tester(); ) --- Notice that here only the parameter version fails. In non-constant evaluated context (see godbolt link) all of the test evaluate false as they should.
(In reply to Miro Palmu from comment #5) > If you use libstdc++ on clang these will not compile but with different > errors. The examples in comment 4 do compile using libstdc++ on clang, if you use libstdc++ headers from after sept 29 (for trunk) or oct 21 (for gcc-13).
(In reply to Jonathan Wakely from comment #6) > The examples in comment 4 do compile using libstdc++ on clang, if you use > libstdc++ headers from after sept 29 (for trunk) or oct 21 (for gcc-13). I was testing this on compiler explorer on clang 17.0.1 and it used gcc-13.2.0 libstdc++. Also tried it locally with clang 16.0.6 with gcc-13.2.1 libstdc++ Output: $ cat prog.cpp #include <string> #include <utility> int main() { [](std::string s = {}) consteval { std::string ss{ std::move(s) }; }(); } $ clang prog.cpp -std=c++2b -stdlib=libstdc++ prog.cpp:4:5: error: call to consteval function 'main()::(anonymous class)::operator()' is not a constant expression [](std::string s = {}) consteval { ^ /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../include/c++/13.2.1/bits/stl_construct.h:97:14: note: construction of subobject of member '_M_local_buf' of union with no active member is not allowed in a constant expression { return ::new((void*)__location) _Tp(std::forward<_Args>(__args)...); } ^ /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../include/c++/13.2.1/bits/char_traits.h:272:6: note: in call to 'construct_at(&ss.._M_local_buf[0], s.._M_local_buf[0])' std::construct_at(__s1 + __i, __s2[__i]); ^ /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../include/c++/13.2.1/bits/char_traits.h:443:11: note: in call to 'copy(&ss.._M_local_buf[0], &s.._M_local_buf[0], 1)' return __gnu_cxx::char_traits<char_type>::copy(__s1, __s2, __n); ^ /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../include/c++/13.2.1/bits/basic_string.h:672:6: note: in call to 'copy(&ss.._M_local_buf[0], &s.._M_local_buf[0], 1)' traits_type::copy(_M_local_buf, __str._M_local_buf, ^ prog.cpp:5:21: note: in call to 'basic_string(s)' std::string ss{ std::move(s) }; ^ prog.cpp:4:5: note: in call to '&[](std::string s) { std::string ss{std::move(s)}; }->operator()({{{{}}, &s.._M_local_buf[0]}, 0, {._M_local_buf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...}}})' [](std::string s = {}) consteval { ^ 1 error generated.
(In reply to Miro Palmu from comment #7) > (In reply to Jonathan Wakely from comment #6) > > The examples in comment 4 do compile using libstdc++ on clang, if you use > > libstdc++ headers from after sept 29 (for trunk) or oct 21 (for gcc-13). > > I was testing this on compiler explorer on clang 17.0.1 and it used > gcc-13.2.0 libstdc++. Which is expected to fail, because 13.2.0 was released before Oct 21. > Also tried it locally with clang 16.0.6 with > gcc-13.2.1 libstdc++ Which gcc-13.2.1 though? That's a snapshot that could date from any time in the past four months. If I use gcc version 13.2.1 20231025 then clang compiles it. Anyway, the original GCC error is the same as PR 112642 which was apparently reduced to PR 111284, which does seem relevant.
(In reply to Jonathan Wakely from comment #8) > > Also tried it locally with clang 16.0.6 with > > gcc-13.2.1 libstdc++ > > Which gcc-13.2.1 though? That's a snapshot that could date from any time in > the past four months. If I use gcc version 13.2.1 20231025 then clang > compiles it. Mine is 13.2.1 20230801 so way before Oct 21. (I did not know there were different snapshots of the releases, I'm just a user trying to help :) ) > Anyway, the original GCC error is the same as PR 112642 You probably mean PR 110158
(In reply to Miro Palmu from comment #9) > Mine is 13.2.1 20230801 so way before Oct 21. (I did not know there were > different snapshots of the releases, I'm just a user trying to help :) ) 13.2.1 (and any x.y.1 version) is not a release, it's a snapshot made from a branch between releases. See https://gcc.gnu.org/develop.html#num_scheme or more details. Releases end with a .0 number. > > Anyway, the original GCC error is the same as PR 112642 > > You probably mean PR 110158 Oops! I meant PR 111258