-
-
Notifications
You must be signed in to change notification settings - Fork 3
-Wfree-nonheap-object false positive in std::variant destructor chain when variant contains std::vector<int> #20
Description
When compiling a project with GCC 15.2.0, I receive the following diagnostic:
[33/231] Building CXX object kernel/CMakeFiles/ultra.dir/gp/individual.cc.o
In file included from /opt/gcc-15.2.0/include/c++/15.2.0/x86_64-slackware-linux/bits/c++allocator.h:33,
from /opt/gcc-15.2.0/include/c++/15.2.0/bits/allocator.h:46,
from /opt/gcc-15.2.0/include/c++/15.2.0/bits/alloc_traits.h:39,
from /opt/gcc-15.2.0/include/c++/15.2.0/ext/alloc_traits.h:36,
from /opt/gcc-15.2.0/include/c++/15.2.0/bits/hashtable_policy.h:39,
from /opt/gcc-15.2.0/include/c++/15.2.0/bits/hashtable.h:37,
from /opt/gcc-15.2.0/include/c++/15.2.0/bits/unordered_map.h:33,
from /opt/gcc-15.2.0/include/c++/15.2.0/unordered_map:43,
from /opt/gcc-15.2.0/include/c++/15.2.0/functional:65,
from /home/../prj/ultra/src/kernel/gp/individual.cc:13:
In member function ‘void std::__new_allocator<_Tp>::deallocate(_Tp*, size_type) [with _Tp = int]’,
inlined from ‘constexpr void std::allocator< >::deallocate(_Tp*, std::size_t) [with _Tp = int]’ at /opt/gcc-15.2.0/include/c++/15.2.0/bits/allocator.h:215:35,
inlined from ‘static constexpr void std::allocator_traits<std::allocator<_Up> >::deallocate(allocator_type&, pointer, size_type) [with _Tp = int]’ at /opt/gcc-15.2.0/include/c++/15.2.0/bits/alloc_traits.h:649:23,
inlined from ‘constexpr void std::_Vector_base<_Tp, _Alloc>::_M_deallocate(pointer, std::size_t) [with _Tp = int; _Alloc = std::allocator]’ at /opt/gcc-15.2.0/include/c++/15.2.0/bits/stl_vector.h:396:19,
inlined from ‘constexpr void std::_Vector_base<_Tp, _Alloc>::_M_deallocate(pointer, std::size_t) [with _Tp = int; _Alloc = std::allocator]’ at /opt/gcc-15.2.0/include/c++/15.2.0/bits/stl_vector.h:392:7,
inlined from ‘constexpr std::_Vector_base<_Tp, _Alloc>::_Vector_base() [with _Tp = int; _Alloc = std::allocator]’ at /opt/gcc-15.2.0/include/c++/15.2.0/bits/stl_vector.h:375:15,_Copy_ctor_base()’ at /opt/gcc-15.2.0/include/c++/15.2.0/variant:576:12,
inlined from ‘constexpr std::vector<_Tp, _Alloc>::vector() [with _Tp = int; _Alloc = std::allocator]’ at /opt/gcc-15.2.0/include/c++/15.2.0/bits/stl_vector.h:805:7,_Variant_storage() [with _Types = {std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator >}]’ at /opt/gcc-15.2.0/include/c++/15.2.0/variant:501:17,
inlined from ‘constexpr void std::destroy_at(_Tp*) [with _Tp = vector]’ at /opt/gcc-15.2.0/include/c++/15.2.0/bits/stl_construct.h:88:18,
inlined from ‘constexpr void std::_Destroy(_Tp*) [with _Tp = vector]’ at /opt/gcc-15.2.0/include/c++/15.2.0/bits/stl_construct.h:164:22,
inlined from ‘std::__detail::__variant::_Variant_storage<false, std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >::_M_reset()::<lambda(auto:11&&)> mutable [with auto:11 = std::vector&]’ at /opt/gcc-15.2.0/include/c++/15.2.0/variant:493:19,
inlined from ‘constexpr _Res std::__invoke_impl(__invoke_other, _Fn&&, _Args&& ...) [with _Res = void; _Fn = __detail::__variant::_Variant_storage<false, monostate, int, double, __cxx11::basic_string<char, char_traits, allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, vector<int, allocator > >::_M_reset()::<lambda(auto:11&&)>; _Args = {vector<int, allocator >&}]’ at /opt/gcc-15.2.0/include/c++/15.2.0/bits/invoke.h:63:36,
inlined from ‘constexpr std::enable_if_t<((bool)is_invocable_r_v<_Res, _Callable, _Args ...>), _Res> std::__invoke_r(_Callable&&, _Args&& ...) [with _Res = void; _Callable = __detail::__variant::_Variant_storage<false, monostate, int, double, __cxx11::basic_string<char, char_traits, allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, vector<int, allocator > >::_M_reset()::<lambda(auto:11&&)>; _Args = {vector<int, allocator >&}]’ at /opt/gcc-15.2.0/include/c++/15.2.0/bits/invoke.h:113:28,
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 = void; _Visitor = std::__detail::__variant::_Variant_storage<false, std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >::_M_reset()::<lambda(auto:11&&)>&&; _Variants = {std::variant<std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >&}; long unsigned int ...__indices = {7}]’ at /opt/gcc-15.2.0/include/c++/15.2.0/variant:1059:40,
inlined from ‘constexpr decltype(auto) std::__do_visit(_Visitor&&, _Variants&& ...) [with _Result_type = void; _Visitor = __detail::__variant::_Variant_storage<false, monostate, int, double, __cxx11::basic_string<char, char_traits, allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, vector<int, allocator > >::_M_reset()::<lambda(auto:11&&)>; _Variants = {variant<monostate, int, double, __cxx11::basic_string<char, char_traits, allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, vector<int, allocator > >&}]’ at /opt/gcc-15.2.0/include/c++/15.2.0/variant:1898:5,
inlined from ‘constexpr void std::__detail::__variant::_Variant_storage<false, _Types ...>::_M_reset() [with _Types = {std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator >}]’ at /opt/gcc-15.2.0/include/c++/15.2.0/variant:491:23,
inlined from ‘constexpr std::__detail::__variant::_Variant_storage<false, _Types ...>::
inlined from ‘constexpr std::__detail::__variant::_Copy_ctor_base<false, std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >::
inlined from ‘constexpr std::__detail::__variant::_Move_ctor_base<false, std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >::_Move_ctor_base()’ at /opt/gcc-15.2.0/include/c++/15.2.0/variant:613:12,_Copy_assign_base()’ at /opt/gcc-15.2.0/include/c++/15.2.0/variant:651:12,
inlined from ‘constexpr std::__detail::__variant::_Copy_assign_base<false, std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >::
inlined from ‘constexpr std::__detail::__variant::_Move_assign_base<false, std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >::_Move_assign_base()’ at /opt/gcc-15.2.0/include/c++/15.2.0/variant:703:12,_Variant_base()’ at /opt/gcc-15.2.0/include/c++/15.2.0/variant:757:12,
inlined from ‘constexpr std::__detail::__variant::_Variant_base<std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >::
inlined from ‘constexpr std::variant<_Types>::~variant() [with _Types = {std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator >}]’ at /opt/gcc-15.2.0/include/c++/15.2.0/variant:1490:28,
inlined from ‘std::__detail::__variant::_Copy_assign_base<false, std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >::operator=(const std::__detail::__variant::_Copy_assign_base<false, std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >&)::<lambda(auto:16&&, auto:17)> mutable [with auto:16 = const std::__cxx11::basic_string&; auto:17 = std::integral_constant<long unsigned int, 3>]’ at /opt/gcc-15.2.0/include/c++/15.2.0/variant:679:16,
inlined from ‘constexpr _Res std::__invoke_impl(__invoke_other, _Fn&&, _Args&& ...) [with _Res = void; _Fn = __detail::__variant::_Copy_assign_base<false, monostate, int, double, __cxx11::basic_string<char, char_traits, allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, vector<int, allocator > >::operator=(const std::__detail::__variant::_Copy_assign_base<false, std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >&)::<lambda(auto:16&&, auto:17)>; _Args = {const __cxx11::basic_string<char, char_traits, allocator >&, integral_constant<long unsigned int, 3>}]’ at /opt/gcc-15.2.0/include/c++/15.2.0/bits/invoke.h:63:36,
inlined from ‘constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...) [with _Callable = __detail::__variant::_Copy_assign_base<false, monostate, int, double, __cxx11::basic_string<char, char_traits, allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, vector<int, allocator > >::operator=(const std::__detail::__variant::_Copy_assign_base<false, std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >&)::<lambda(auto:16&&, auto:17)>; _Args = {const __cxx11::basic_string<char, char_traits, allocator >&, integral_constant<long unsigned int, 3>}]’ at /opt/gcc-15.2.0/include/c++/15.2.0/bits/invoke.h:98: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::_Copy_assign_base<false, std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >::operator=(const std::__detail::__variant::_Copy_assign_base<false, std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >&)::<lambda(auto:16&&, auto:17)>&&; _Variants = {const std::variant<std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >&}; long unsigned int ...__indices = {3}]’ at /opt/gcc-15.2.0/include/c++/15.2.0/variant:1044:17,
inlined from ‘constexpr decltype(auto) std::__do_visit(_Visitor&&, _Variants&& ...) [with _Result_type = __detail::__variant::__variant_idx_cookie; _Visitor = __detail::__variant::_Copy_assign_base<false, monostate, int, double, __cxx11::basic_string<char, char_traits, allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, vector<int, allocator > >::operator=(const std::__detail::__variant::_Copy_assign_base<false, std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >&)::<lambda(auto:16&&, auto:17)>; _Variants = {const variant<monostate, int, double, __cxx11::basic_string<char, char_traits, allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, vector<int, allocator > >&}]’ at /opt/gcc-15.2.0/include/c++/15.2.0/variant:1894:5:
/opt/gcc-15.2.0/include/c++/15.2.0/bits/new_allocator.h:172:66: warning: ‘void operator delete(void*, std::size_t)’ called on unallocated object ‘’ [-Wfree-nonheap-object]
172 | _GLIBCXX_OPERATOR_DELETE(_GLIBCXX_SIZED_DEALLOC(__p, __n));
| ^
In file included from /home/../prj/ultra/src/kernel/value.h:18,
from /home/../prj/ultra/src/kernel/symbol.h:19,
from /home/../prj/ultra/src/kernel/terminal.h:16,
from /home/../prj/ultra/src/kernel/symbol_set.h:16,
from /home/../prj/ultra/src/kernel/individual.h:19,
from /home/../prj/ultra/src/kernel/gp/individual.h:18,
from /home/../prj/ultra/src/kernel/gp/individual.cc:15:
/opt/gcc-15.2.0/include/c++/15.2.0/variant: In function ‘constexpr decltype(auto) std::__do_visit(_Visitor&&, _Variants&& ...) [with _Result_type = __detail::__variant::__variant_idx_cookie; _Visitor = __detail::__variant::_Copy_assign_base<false, monostate, int, double, __cxx11::basic_string<char, char_traits, allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, vector<int, allocator > >::operator=(const std::__detail::__variant::_Copy_assign_base<false, std::monostate, int, double, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, std::vector<int, std::allocator > >&)::<lambda(auto:16&&, auto:17)>; _Variants = {const variant<monostate, int, double, __cxx11::basic_string<char, char_traits, allocator >, const ultra::nullary*, ultra::param_address, const ultra::src::variable*, vector<int, allocator > >&}]’:
/opt/gcc-15.2.0/include/c++/15.2.0/variant:679:30: note: declared here
679 | __self = _Variant(in_place_index<__j>, __rhs_mem);
The warning is triggered inside libstdc++, specifically during destruction or assignment of a std::variant that includes a std::vector<int> alternative.
The full inlining trace points to:
std::vector<int>::~vector();- called from
std::__detail::__variant::_Variant_storage::_M_reset(); - during destruction and copy-assignment of
std::variant<...>.
This happens even though the code never frees memory manually, and Clang as well as GCC ≤ 13 compile the same sources without any warnings.
The problematic variant structure looks like:
using value_t = std::variant<
std::monostate,
int,
double,
std::string,
const nullary*,
param_address,
const src::variable*,
std::vector<int>
>;The warning appears whenever value_t is destroyed or assigned (e.g. in constructors/assignment operators of surrounding classes).
The backtrace shows the warning originates entirely from inlined libstdc++ destructors, not from user code.
The problem occurs only when the variant contains a std::vector<T> alternative; removing it makes the warning disappear.
It appears to be a misanalysis in the inliner or the data-flow engine for constexpr destructor paths in the variant implementation.
This looks very similar to regressions reported starting with GCC 14, where certain std::variant destructor paths produce false positives.
I believe this is a GCC bug, not a library or user issue.
For the moment, the only reliable workaround is to locally disable the warning around the inclusion of the file using the variant:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfree-nonheap-object"
#include "value.h"
#pragma GCC diagnostic popSuppressing the warning globally is undesirable.