public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug libstdc++/106547] New: std::variant::emplace goes into an infinite recursion with certain weird trivially copyable types
@ 2022-08-06 18:27 the.invisible.phantom at gmail dot com
  2022-08-06 22:10 ` [Bug libstdc++/106547] " redi at gcc dot gnu.org
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: the.invisible.phantom at gmail dot com @ 2022-08-06 18:27 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106547

            Bug ID: 106547
           Summary: std::variant::emplace goes into an infinite recursion
                    with certain weird trivially copyable types
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: the.invisible.phantom at gmail dot com
  Target Milestone: ---

Created attachment 53421
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=53421&action=edit
Preprocessed source file

Consider the following source code (preprocessed version attached):

#include <concepts>
#include <variant>

struct weird {
  weird() {}
  weird(std::same_as<weird> auto&&) {}
  weird(const weird&) = default;
  weird& operator=(const weird&) = default;
};

int main() {
  std::variant<std::monostate, weird> data;
  data.emplace<weird>();
}

When executing, the emplace call goes into an infinite recursion.

The emplace function has a number of different implementations, selected using
`if constexpr`. The first `if constexpr` branch has the condition
`is_nothrow_constructible_v<type, _Args...>`; since the constructor that we're
using is not noexcept, that branch is not taken. The second branch has the
condition `is_scalar_v<type>`, which does not apply, either.

The third branch has the condition
`__detail::__variant::_Never_valueless_alt<type>() && _Traits::_S_move_assign`.
The second part simply requires that all alternatives be move-assignable, which
is true here. The first part checks two things: that the type's size not exceed
256 bytes, which is true, and that the type be trivially copyable. The `weird`
type has a trivial destructor, a trivial copy constructor, a trivial copy
assignment operator, no move constructor, and no move assignment operator;
therefore, it is trivially copyable and the third branch is chosen.

The implementation in the third branch constructs a temporary variant on the
stack and then move-assigns it to `*this`. The move assignment ultimately calls
emplace again, this time constructing the alternative from `weird&&`. The text
in the comment appears to expect that the first `if constexpr` branch --
corresponding to the noexcept constructor -- will be chosen.

However, construction from `weird&&` actually calls `weird`'s second
constructor, which is not noexcept. (It does not prevent `weird` from being
trivially copyable, since it is a template and therefore is not a move
constructor.) Therefore, this actually chooses the third branch once again,
which, once again, constructs a temporary variant on the stack and move-assigns
it to `*this`. This causes an infinite recursion.

To sum up, the key problem here is that the `weird` type is trivially copyable,
but its move construction is not noexcept, because it invokes a constructor
template that is not a move constructor. (A real-world example of such a weird
type is gsl::not_null from Microsoft's GSL implementation
<https://github.com/Microsoft/GSL>).

Full invocation of GCC is below. (I also reproduced it on godbolt.org using the
"gcc (trunk)" compiler and checked the source code in the git repository to
make sure the offending lines are still there.)

$ g++ -v -save-temps -std=c++20 test.cpp -o test
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu
11.2.0-19ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-11/README.Bugs
--enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,m2 --prefix=/usr
--with-gcc-major-version-only --program-suffix=-11
--program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id
--libexecdir=/usr/lib --without-included-gettext --enable-threads=posix
--libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu
--enable-libstdcxx-debug --enable-libstdcxx-time=yes
--with-default-libstdcxx-abi=new --enable-gnu-unique-object
--disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib
--enable-libphobos-checking=release --with-target-system-zlib=auto
--enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet
--with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32
--enable-multilib --with-tune=generic
--enable-offload-targets=nvptx-none=/build/gcc-11-gBFGDP/gcc-11-11.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-11-gBFGDP/gcc-11-11.2.0/debian/tmp-gcn/usr
--without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu
--host=x86_64-linux-gnu --target=x86_64-linux-gnu
--with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.2.0 (Ubuntu 11.2.0-19ubuntu1)
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-std=c++20' '-o' 'test'
'-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/11/cc1plus -E -quiet -v -imultiarch
x86_64-linux-gnu -D_GNU_SOURCE test.cpp -mtune=generic -march=x86-64 -std=c++20
-fpch-preprocess -fasynchronous-unwind-tables -fstack-protector-strong -Wformat
-Wformat-security -fstack-clash-protection -fcf-protection -o test.ii
ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/11"
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/11/include-fixed"
ignoring nonexistent directory
"/usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/include/c++/11
 /usr/include/x86_64-linux-gnu/c++/11
 /usr/include/c++/11/backward
 /usr/lib/gcc/x86_64-linux-gnu/11/include
 /usr/local/include
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-std=c++20' '-o' 'test'
'-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/11/cc1plus -fpreprocessed test.ii -quiet
-dumpbase test.cpp -dumpbase-ext .cpp -mtune=generic -march=x86-64 -std=c++20
-version -fasynchronous-unwind-tables -fstack-protector-strong -Wformat
-Wformat-security -fstack-clash-protection -fcf-protection -o test.s
GNU C++20 (Ubuntu 11.2.0-19ubuntu1) version 11.2.0 (x86_64-linux-gnu)
        compiled by GNU C version 11.2.0, GMP version 6.2.1, MPFR version
4.1.0, MPC version 1.2.1, isl version isl-0.24-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
GNU C++20 (Ubuntu 11.2.0-19ubuntu1) version 11.2.0 (x86_64-linux-gnu)
        compiled by GNU C version 11.2.0, GMP version 6.2.1, MPFR version
4.1.0, MPC version 1.2.1, isl version isl-0.24-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 619370d92bf6efc3023abfa7394da0c1
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-std=c++20' '-o' 'test'
'-shared-libgcc' '-mtune=generic' '-march=x86-64'
 as -v --64 -o test.o test.s
GNU assembler version 2.38 (x86_64-linux-gnu) using BFD version (GNU Binutils
for Ubuntu) 2.38
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/11/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-std=c++20' '-o' 'test'
'-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'test.'
 /usr/lib/gcc/x86_64-linux-gnu/11/collect2 -plugin
/usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so
-plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper
-plugin-opt=-fresolution=test.res -plugin-opt=-pass-through=-lgcc_s
-plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc
-plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --build-id
--eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker
/lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o test
/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/11/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/11
-L/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu
-L/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib -L/lib/x86_64-linux-gnu
-L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib
-L/usr/lib/gcc/x86_64-linux-gnu/11/../../.. test.o -lstdc++ -lm -lgcc_s -lgcc
-lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/11/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-std=c++20' '-o' 'test'
'-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'test.'

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2023-09-11 13:53 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-06 18:27 [Bug libstdc++/106547] New: std::variant::emplace goes into an infinite recursion with certain weird trivially copyable types the.invisible.phantom at gmail dot com
2022-08-06 22:10 ` [Bug libstdc++/106547] " redi at gcc dot gnu.org
2023-09-11 12:38 ` de34 at live dot cn
2023-09-11 13:27 ` the.invisible.phantom at gmail dot com
2023-09-11 13:53 ` de34 at live dot cn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).