mirror of
https://github.com/boostorg/variant2.git
synced 2026-01-19 04:42:16 +00:00
Add test for GCC 12+ -Wmaybe-uninitialized false positive
Add variant_gcc_false_positive.cpp to reproduce GCC 12+ spurious -Wmaybe-uninitialized warnings when copying/moving variant containing non-trivially-copyable types like std::exception_ptr and std::string. Tests use boost::system::result which internally uses variant2, and include coroutine-based tests when C++20 coroutine support is available. The test uses -O3 optimization to trigger the aggressive inlining that exposes the false positive where GCC's dataflow analysis cannot prove the discriminator ensures only initialized alternatives are accessed. See https://github.com/boostorg/variant2/issues/33
This commit is contained in:
committed by
Peter Dimov
parent
4632862a4b
commit
382cf33824
@@ -6,6 +6,6 @@ include(BoostTestJamfile OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST)
|
||||
|
||||
if(HAVE_BOOST_TEST)
|
||||
|
||||
boost_test_jamfile(FILE Jamfile LINK_LIBRARIES Boost::variant2 Boost::core Boost::container_hash)
|
||||
boost_test_jamfile(FILE Jamfile LINK_LIBRARIES Boost::variant2 Boost::core Boost::container_hash Boost::system)
|
||||
|
||||
endif()
|
||||
|
||||
@@ -172,3 +172,10 @@ compile variant_default_construct_cx_5.cpp ;
|
||||
compile variant_value_construct_cx_2.cpp ;
|
||||
compile variant_value_construct_cx_3.cpp ;
|
||||
compile variant_value_construct_cx_4.cpp ;
|
||||
|
||||
# GCC 12+ false positive -Wmaybe-uninitialized with non-trivially-copyable types
|
||||
run variant_gcc_false_positive.cpp : : :
|
||||
<library>/boost/system//boost_system
|
||||
<toolset>gcc:<cxxflags>-O3
|
||||
<toolset>clang:<cxxflags>-O3
|
||||
;
|
||||
191
test/variant_gcc_false_positive.cpp
Normal file
191
test/variant_gcc_false_positive.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
|
||||
// Copyright 2025 Peter Dimov
|
||||
// Copyright 2025 Vinnie Falco
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
//
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
// GCC 12+ -Wmaybe-uninitialized false positive tests
|
||||
// https://github.com/boostorg/variant2/issues/33
|
||||
//
|
||||
// GCC 12+'s improved dataflow analysis sees code paths for all alternatives
|
||||
// in mp_with_index and warns that members may be uninitialized, even though
|
||||
// the variant's discriminator guarantees only initialized alternatives are
|
||||
// accessed.
|
||||
|
||||
#include <boost/system/result.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
// Check for C++17 std::optional support
|
||||
#if __cplusplus >= 201703L
|
||||
# include <optional>
|
||||
# define BOOST_VARIANT2_TEST_HAS_OPTIONAL 1
|
||||
#endif
|
||||
|
||||
// Check for C++20 coroutine support
|
||||
#if defined(__cpp_impl_coroutine) && __cpp_impl_coroutine >= 201902L
|
||||
# include <coroutine>
|
||||
# define BOOST_VARIANT2_TEST_HAS_CORO 1
|
||||
#endif
|
||||
|
||||
using result_void = boost::system::result<void, std::exception_ptr>;
|
||||
using result_string = boost::system::result<std::string, std::exception_ptr>;
|
||||
|
||||
void testGccUninitialized()
|
||||
{
|
||||
// Test 1: Simple copy construction
|
||||
{
|
||||
result_void r1;
|
||||
result_void r2(r1);
|
||||
(void)r2;
|
||||
}
|
||||
|
||||
// Test 2: Copy assignment
|
||||
{
|
||||
result_void r1;
|
||||
result_void r2;
|
||||
r2 = r1;
|
||||
(void)r2;
|
||||
}
|
||||
|
||||
#ifdef BOOST_VARIANT2_TEST_HAS_OPTIONAL
|
||||
// Test 3: std::optional assignment (matches spawn pattern)
|
||||
{
|
||||
std::optional<result_void> opt;
|
||||
opt = result_void{};
|
||||
(void)opt;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Test 4: Pass to function via copy
|
||||
{
|
||||
auto fn = [](result_void r) { (void)r; };
|
||||
fn(result_void{});
|
||||
}
|
||||
|
||||
#ifdef BOOST_VARIANT2_TEST_HAS_OPTIONAL
|
||||
// Test 5: Lambda capture + optional (closest to spawn)
|
||||
{
|
||||
auto fn = [](result_void r) {
|
||||
std::optional<result_void> opt;
|
||||
opt = r;
|
||||
return opt.has_value();
|
||||
};
|
||||
(void)fn(result_void{});
|
||||
}
|
||||
#endif
|
||||
|
||||
// Test 6: Non-void result with string (triggers string warning)
|
||||
{
|
||||
result_string r1;
|
||||
result_string r2(r1);
|
||||
(void)r2;
|
||||
}
|
||||
|
||||
// Test 7: Assign exception to result holding value
|
||||
{
|
||||
result_string r1{"hello"};
|
||||
r1 = std::make_exception_ptr(std::runtime_error("test"));
|
||||
(void)r1;
|
||||
}
|
||||
|
||||
#ifdef BOOST_VARIANT2_TEST_HAS_OPTIONAL
|
||||
// Test 8: Optional with string result
|
||||
{
|
||||
std::optional<result_string> opt;
|
||||
opt = result_string{};
|
||||
(void)opt;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BOOST_VARIANT2_TEST_HAS_CORO
|
||||
// Minimal fire-and-forget coroutine for testing
|
||||
struct fire_and_forget
|
||||
{
|
||||
struct promise_type
|
||||
{
|
||||
fire_and_forget get_return_object() { return {}; }
|
||||
std::suspend_never initial_suspend() noexcept { return {}; }
|
||||
std::suspend_never final_suspend() noexcept { return {}; }
|
||||
void return_void() {}
|
||||
void unhandled_exception() { std::terminate(); }
|
||||
};
|
||||
};
|
||||
|
||||
// Test 9: Coroutine returning result (mimics spawn)
|
||||
{
|
||||
auto coro = []() -> fire_and_forget {
|
||||
result_void r{};
|
||||
(void)r;
|
||||
co_return;
|
||||
};
|
||||
coro();
|
||||
}
|
||||
|
||||
// Test 10: Coroutine with handler call (closest to actual spawn)
|
||||
{
|
||||
std::optional<result_void> received;
|
||||
auto handler = [&](result_void r) {
|
||||
received = r;
|
||||
};
|
||||
auto coro = [&]() -> fire_and_forget {
|
||||
handler(result_void{});
|
||||
co_return;
|
||||
};
|
||||
coro();
|
||||
(void)received;
|
||||
}
|
||||
|
||||
// Test 11: Coroutine with try/catch like spawn
|
||||
{
|
||||
std::optional<result_void> received;
|
||||
auto handler = [&](result_void r) {
|
||||
received = r;
|
||||
};
|
||||
auto coro = [&]() -> fire_and_forget {
|
||||
try
|
||||
{
|
||||
handler(result_void{});
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
handler(result_void{std::current_exception()});
|
||||
}
|
||||
co_return;
|
||||
};
|
||||
coro();
|
||||
(void)received;
|
||||
}
|
||||
|
||||
// Test 12: Coroutine with string result
|
||||
{
|
||||
std::optional<result_string> received;
|
||||
auto handler = [&](result_string r) {
|
||||
received = r;
|
||||
};
|
||||
auto coro = [&]() -> fire_and_forget {
|
||||
try
|
||||
{
|
||||
handler(result_string{"test"});
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
handler(result_string{std::current_exception()});
|
||||
}
|
||||
co_return;
|
||||
};
|
||||
coro();
|
||||
(void)received;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
testGccUninitialized();
|
||||
return boost::report_errors();
|
||||
}
|
||||
Reference in New Issue
Block a user