https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107919
Bug ID: 107919 Summary: Possibly false-positive "maybe-uninitialized" with GCC 12 on complex variant-variant-tuple-unique_ptr types Product: gcc Version: 12.1.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: freddie_chopin at op dot pl Target Milestone: --- Following code gives no warning with GCC 11.3 and earlier versions: -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- #include <memory> #include <variant> using Event = std::variant<std::variant<std::tuple<std::unique_ptr<int>>>, int, char>; void do_something(void* storage) { Event event {}; auto& swappedValue = *reinterpret_cast<Event*>(storage); std::swap(event, swappedValue); } -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- When compiled with GCC 12.1 or newer it reports a (possibly false-positive) maybe-uninitialized warning that is so long, that it is hard to comprehend (; -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- $ g++ -std=c++17 -c -O2 warn.cpp -Wall -Wextra In file included from /usr/include/c++/12.2.0/bits/unique_ptr.h:36, from /usr/include/c++/12.2.0/memory:76, from warn.cpp:1: In constructor ‘constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(std::_Tuple_impl<_Idx, _Head, _Tail ...>&&) [with long unsigned int _Idx = 0; _Head = int*; _Tail = {std::default_delete<int>}]’, inlined from ‘constexpr std::tuple<_T1, _T2>::tuple(std::tuple<_T1, _T2>&&) [with _T1 = int*; _T2 = std::default_delete<int>]’ at /usr/include/c++/12.2.0/tuple:1090:17, inlined from ‘std::__uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl(std::__uniq_ptr_impl<_Tp, _Dp>&&) [with _Tp = int; _Dp = std::default_delete<int>]’ at /usr/include/c++/12.2.0/bits/unique_ptr.h:177:9, inlined from ‘std::__uniq_ptr_data<_Tp, _Dp, <anonymous>, <anonymous> >::__uniq_ptr_data(std::__uniq_ptr_data<_Tp, _Dp, <anonymous>, <anonymous> >&&) [with _Tp = int; _Dp = std::default_delete<int>; bool <anonymous> = true; bool <anonymous> = true]’ at /usr/include/c++/12.2.0/bits/unique_ptr.h:234:7, inlined from ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = int; _Dp = std::default_delete<int>]’ at /usr/include/c++/12.2.0/bits/unique_ptr.h:358:7, inlined from ‘std::_Head_base<_Idx, _Head, false>::_Head_base(std::_Head_base<_Idx, _Head, false>&&) [with long unsigned int _Idx = 0; _Head = std::unique_ptr<int>]’ at /usr/include/c++/12.2.0/tuple:196:17, inlined from ‘constexpr std::_Tuple_impl<_Idx, _Head>::_Tuple_impl(std::_Tuple_impl<_Idx, _Head>&&) [with long unsigned int _Idx = 0; _Head = std::unique_ptr<int>]’ at /usr/include/c++/12.2.0/tuple:456:41, inlined from ‘constexpr std::tuple< <template-parameter-1-1> >::tuple(std::tuple< <template-parameter-1-1> >&&) [with _Elements = {std::unique_ptr<int, std::default_delete<int> >}]’ at /usr/include/c++/12.2.0/tuple:756:17, inlined from ‘constexpr std::__detail::__variant::_Uninitialized<_Type, false>::_Uninitialized(std::in_place_index_t<0>, _Args&& ...) [with _Args = {std::tuple<std::unique_ptr<int, std::default_delete<int> > >}; _Type = std::tuple<std::unique_ptr<int, std::default_delete<int> > >]’ at /usr/include/c++/12.2.0/variant:283:4, inlined from ‘constexpr std::__detail::__variant::_Variadic_union<_First, _Rest ...>::_Variadic_union(std::in_place_index_t<0>, _Args&& ...) [with _Args = {std::tuple<std::unique_ptr<int, std::default_delete<int> > >}; _First = std::tuple<std::unique_ptr<int, std::default_delete<int> > >; _Rest = {}]’ at /usr/include/c++/12.2.0/variant:385:4, inlined from ‘void std::_Construct(_Tp*, _Args&& ...) [with _Tp = __detail::__variant::_Variadic_union<tuple<unique_ptr<int, default_delete<int> > > >; _Args = {const in_place_index_t<0>&, tuple<unique_ptr<int, default_delete<int> > >}]’ at /usr/include/c++/12.2.0/bits/stl_construct.h:119:7, inlined from ‘std::__detail::__variant::_Move_ctor_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::_Move_ctor_base(std::__detail::__variant::_Move_ctor_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >&&)::<lambda(auto:4&&, auto:5)> mutable [with auto:4 = std::tuple<std::unique_ptr<int, std::default_delete<int> > >; auto:5 = std::integral_constant<long unsigned int, 0>]’ at /usr/include/c++/12.2.0/variant:605:23, inlined from ‘constexpr _Res std::__invoke_impl(__invoke_other, _Fn&&, _Args&& ...) [with _Res = void; _Fn = __detail::__variant::_Move_ctor_base<false, tuple<unique_ptr<int, default_delete<int> > > >::_Move_ctor_base(std::__detail::__variant::_Move_ctor_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >&&)::<lambda(auto:4&&, auto:5)>; _Args = {tuple<unique_ptr<int, default_delete<int> > >, integral_constant<long unsigned int, 0>}]’ at /usr/include/c++/12.2.0/bits/invoke.h:61:36, inlined from ‘constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...) [with _Callable = __detail::__variant::_Move_ctor_base<false, tuple<unique_ptr<int, default_delete<int> > > >::_Move_ctor_base(std::__detail::__variant::_Move_ctor_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >&&)::<lambda(auto:4&&, auto:5)>; _Args = {tuple<unique_ptr<int, default_delete<int> > >, integral_constant<long unsigned int, 0>}]’ at /usr/include/c++/12.2.0/bits/invoke.h:96:40, inlined from ‘static constexpr decltype(auto) std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<_Result_type (*)(_Visitor, _Variants ...)>, std::integer_sequence<long unsigned int, __indices ...> >::__visit_invoke(_Visitor&&, _Variants ...) [with _Result_type = std::__detail::__variant::__variant_idx_cookie; _Visitor = std::__detail::__variant::_Move_ctor_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::_Move_ctor_base(std::__detail::__variant::_Move_ctor_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >&&)::<lambda(auto:4&&, auto:5)>&&; _Variants = {std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >&&}; long unsigned int ...__indices = {0}]’ at /usr/include/c++/12.2.0/variant:1020:17, inlined from ‘constexpr decltype(auto) std::__do_visit(_Visitor&&, _Variants&& ...) [with _Result_type = __detail::__variant::__variant_idx_cookie; _Visitor = __detail::__variant::_Move_ctor_base<false, tuple<unique_ptr<int, default_delete<int> > > >::_Move_ctor_base(std::__detail::__variant::_Move_ctor_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >&&)::<lambda(auto:4&&, auto:5)>; _Variants = {variant<tuple<unique_ptr<int, default_delete<int> > > >}]’ at /usr/include/c++/12.2.0/variant:1783:5, inlined from ‘constexpr decltype(auto) std::__do_visit(_Visitor&&, _Variants&& ...) [with _Result_type = __detail::__variant::__variant_idx_cookie; _Visitor = __detail::__variant::_Move_ctor_base<false, tuple<unique_ptr<int, default_delete<int> > > >::_Move_ctor_base(std::__detail::__variant::_Move_ctor_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >&&)::<lambda(auto:4&&, auto:5)>; _Variants = {variant<tuple<unique_ptr<int, default_delete<int> > > >}]’ at /usr/include/c++/12.2.0/variant:1729:5, inlined from ‘constexpr void std::__detail::__variant::__raw_idx_visit(_Visitor&&, _Variants&& ...) [with _Visitor = _Move_ctor_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::_Move_ctor_base(std::__detail::__variant::_Move_ctor_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >&&)::<lambda(auto:4&&, auto:5)>; _Variants = {std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >}]’ at /usr/include/c++/12.2.0/variant:184:44, inlined from ‘std::__detail::__variant::_Move_ctor_base<<anonymous>, _Types>::_Move_ctor_base(std::__detail::__variant::_Move_ctor_base<<anonymous>, _Types>&&) [with bool <anonymous> = false; _Types = {std::tuple<std::unique_ptr<int, std::default_delete<int> > >}]’ at /usr/include/c++/12.2.0/variant:600:28, inlined from ‘std::__detail::__variant::_Copy_assign_base<<anonymous>, _Types>::_Copy_assign_base(std::__detail::__variant::_Copy_assign_base<<anonymous>, _Types>&&) [with bool <anonymous> = false; _Types = {std::tuple<std::unique_ptr<int, std::default_delete<int> > >}]’ at /usr/include/c++/12.2.0/variant:665:7, inlined from ‘std::__detail::__variant::_Move_assign_base<<anonymous>, _Types>::_Move_assign_base(std::__detail::__variant::_Move_assign_base<<anonymous>, _Types>&&) [with bool <anonymous> = false; _Types = {std::tuple<std::unique_ptr<int, std::default_delete<int> > >}]’ at /usr/include/c++/12.2.0/variant:719:7, inlined from ‘std::__detail::__variant::_Variant_base<_Types>::_Variant_base(std::__detail::__variant::_Variant_base<_Types>&&) [with _Types = {std::tuple<std::unique_ptr<int, std::default_delete<int> > >}]’ at /usr/include/c++/12.2.0/variant:750:7, inlined from ‘std::variant<_Types>::variant(std::variant<_Types>&&) [with _Types = {std::tuple<std::unique_ptr<int, std::default_delete<int> > >}]’ at /usr/include/c++/12.2.0/variant:1404:7, inlined from ‘void std::_Construct(_Tp*, _Args&& ...) [with _Tp = variant<tuple<unique_ptr<int, default_delete<int> > > >; _Args = {variant<tuple<unique_ptr<int, default_delete<int> > > >}]’ at /usr/include/c++/12.2.0/bits/stl_construct.h:119:7, inlined from ‘void std::__detail::__variant::__emplace(_Variant_storage<_Triv, _Types ...>&, _Args&& ...) [with long unsigned int _Np = 0; bool _Triv = false; _Types = {std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >, int, char}; _Args = {std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >}]’ at /usr/include/c++/12.2.0/variant:541:22, inlined from ‘std::variant<std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >, int, char>::swap(std::variant<std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >, int, char>&)::<lambda(auto:22&&, auto:23)> mutable [with auto:22 = std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >&; auto:23 = std::integral_constant<long unsigned int, 0>]’ at /usr/include/c++/12.2.0/variant:1650:32, inlined from ‘constexpr _Res std::__invoke_impl(__invoke_other, _Fn&&, _Args&& ...) [with _Res = void; _Fn = variant<variant<tuple<unique_ptr<int, default_delete<int> > > >, int, char>::swap(std::variant<std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >, int, char>&)::<lambda(auto:22&&, auto:23)>; _Args = {variant<tuple<unique_ptr<int, default_delete<int> > > >&, integral_constant<long unsigned int, 0>}]’ at /usr/include/c++/12.2.0/bits/invoke.h:61:36, inlined from ‘constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...) [with _Callable = variant<variant<tuple<unique_ptr<int, default_delete<int> > > >, int, char>::swap(std::variant<std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >, int, char>&)::<lambda(auto:22&&, auto:23)>; _Args = {variant<tuple<unique_ptr<int, default_delete<int> > > >&, integral_constant<long unsigned int, 0>}]’ at /usr/include/c++/12.2.0/bits/invoke.h:96:40, inlined from ‘static constexpr decltype(auto) std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<_Result_type (*)(_Visitor, _Variants ...)>, std::integer_sequence<long unsigned int, __indices ...> >::__visit_invoke(_Visitor&&, _Variants ...) [with _Result_type = std::__detail::__variant::__variant_idx_cookie; _Visitor = std::variant<std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >, int, char>::swap(std::variant<std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >, int, char>&)::<lambda(auto:22&&, auto:23)>&&; _Variants = {std::variant<std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >, int, char>&}; long unsigned int ...__indices = {0}]’ at /usr/include/c++/12.2.0/variant:1020:17, inlined from ‘constexpr decltype(auto) std::__do_visit(_Visitor&&, _Variants&& ...) [with _Result_type = __detail::__variant::__variant_idx_cookie; _Visitor = variant<variant<tuple<unique_ptr<int, default_delete<int> > > >, int, char>::swap(std::variant<std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >, int, char>&)::<lambda(auto:22&&, auto:23)>; _Variants = {variant<variant<tuple<unique_ptr<int, default_delete<int> > > >, int, char>&}]’ at /usr/include/c++/12.2.0/variant:1783:5, inlined from ‘constexpr decltype(auto) std::__do_visit(_Visitor&&, _Variants&& ...) [with _Result_type = __detail::__variant::__variant_idx_cookie; _Visitor = variant<variant<tuple<unique_ptr<int, default_delete<int> > > >, int, char>::swap(std::variant<std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >, int, char>&)::<lambda(auto:22&&, auto:23)>; _Variants = {variant<variant<tuple<unique_ptr<int, default_delete<int> > > >, int, char>&}]’ at /usr/include/c++/12.2.0/variant:1729:5, inlined from ‘constexpr void std::__detail::__variant::__raw_idx_visit(_Visitor&&, _Variants&& ...) [with _Visitor = std::variant<std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >, int, char>::swap(std::variant<std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >, int, char>&)::<lambda(auto:22&&, auto:23)>; _Variants = {std::variant<std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >, int, char>&}]’ at /usr/include/c++/12.2.0/variant:184:44, inlined from ‘void std::variant<_Types>::swap(std::variant<_Types>&) [with _Types = {std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >, int, char}]’ at /usr/include/c++/12.2.0/variant:1623:28, inlined from ‘std::enable_if_t<((is_move_constructible_v<_Types> && ...) && (is_swappable_v<_Types> && ...))> std::swap(variant<_Types ...>&, variant<_Types ...>&) [with _Types = {variant<tuple<unique_ptr<int, default_delete<int> > > >, int, char}]’ at /usr/include/c++/12.2.0/variant:1297:17, inlined from ‘void do_something(void*)’ at warn.cpp:10:11: /usr/include/c++/12.2.0/tuple:301:7: warning: ‘*(int**)((char*)&__tmp + offsetof(std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >,std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::<unnamed>.std::__detail::__variant::_Variant_base<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::<unnamed>.std::__detail::__variant::_Move_assign_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::<unnamed>.std::__detail::__variant::_Copy_assign_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::<unnamed>.std::__detail::__variant::_Move_ctor_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::<unnamed>.std::__detail::__variant::_Copy_ctor_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::<unnamed>.std::__detail::__variant::_Variant_storage<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::_M_u))’ may be used uninitialized [-Wmaybe-uninitialized] 301 | _Tuple_impl(_Tuple_impl&&) = default; | ^~~~~~~~~~~ In file included from warn.cpp:2: /usr/include/c++/12.2.0/variant: In function ‘void do_something(void*)’: /usr/include/c++/12.2.0/variant:1636:26: note: ‘*(int**)((char*)&__tmp + offsetof(std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >,std::variant<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::<unnamed>.std::__detail::__variant::_Variant_base<std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::<unnamed>.std::__detail::__variant::_Move_assign_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::<unnamed>.std::__detail::__variant::_Copy_assign_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::<unnamed>.std::__detail::__variant::_Move_ctor_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::<unnamed>.std::__detail::__variant::_Copy_ctor_base<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::<unnamed>.std::__detail::__variant::_Variant_storage<false, std::tuple<std::unique_ptr<int, std::default_delete<int> > > >::_M_u))’ was declared here 1636 | auto __tmp(std::move(__rhs_mem)); | ^~~~~ -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- Above warning is with GCC 12.2 on PC (linux), but I also get a similar (or identical, hard to see) warning for 12.1 (tested for arm-none-eabi target, as I have multiple versions of the compiler at hand). It seems as if any change in the test-case above will make the warning go away, even changes which should not make much difference - for example reducing the number of types in the "outer" variant from 3 to 2 or just putting the "complex type" inside a struct or renaming `Event` to sth else and creating a struct called `Event` which inherits the "complex type". But things like avoiding the tuple, avoiding unique_ptr, avoiding variant-in-variant - each one makes the warning disappear. Also using a "real" object instead of casted `void*` storage "solves" the problem, however this particular code is part of a low-level embedded RTOS and I would really prefer to keep using `void*` in this case. But as I mentioned, even with casted `void*` but slightly simpler/different `Event` type, the warning is gone. Note - having optional-in-variant also produces mostly identical warning. I'm not sure whether this is intended behavior and my code is indeed wrong or maybe this is a regression.