Skip to content

Commit 382cf33

Browse files
vinniefalcopdimov
authored andcommitted
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 #33
1 parent 4632862 commit 382cf33

3 files changed

Lines changed: 199 additions & 1 deletion

File tree

test/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ include(BoostTestJamfile OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST)
66

77
if(HAVE_BOOST_TEST)
88

9-
boost_test_jamfile(FILE Jamfile LINK_LIBRARIES Boost::variant2 Boost::core Boost::container_hash)
9+
boost_test_jamfile(FILE Jamfile LINK_LIBRARIES Boost::variant2 Boost::core Boost::container_hash Boost::system)
1010

1111
endif()

test/Jamfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,10 @@ compile variant_default_construct_cx_5.cpp ;
172172
compile variant_value_construct_cx_2.cpp ;
173173
compile variant_value_construct_cx_3.cpp ;
174174
compile variant_value_construct_cx_4.cpp ;
175+
176+
# GCC 12+ false positive -Wmaybe-uninitialized with non-trivially-copyable types
177+
run variant_gcc_false_positive.cpp : : :
178+
<library>/boost/system//boost_system
179+
<toolset>gcc:<cxxflags>-O3
180+
<toolset>clang:<cxxflags>-O3
181+
;
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
2+
// Copyright 2025 Peter Dimov
3+
// Copyright 2025 Vinnie Falco
4+
//
5+
// Distributed under the Boost Software License, Version 1.0.
6+
//
7+
// See accompanying file LICENSE_1_0.txt or copy at
8+
// http://www.boost.org/LICENSE_1_0.txt
9+
10+
// GCC 12+ -Wmaybe-uninitialized false positive tests
11+
// https://github.com/boostorg/variant2/issues/33
12+
//
13+
// GCC 12+'s improved dataflow analysis sees code paths for all alternatives
14+
// in mp_with_index and warns that members may be uninitialized, even though
15+
// the variant's discriminator guarantees only initialized alternatives are
16+
// accessed.
17+
18+
#include <boost/system/result.hpp>
19+
#include <boost/core/lightweight_test.hpp>
20+
#include <exception>
21+
#include <string>
22+
23+
// Check for C++17 std::optional support
24+
#if __cplusplus >= 201703L
25+
# include <optional>
26+
# define BOOST_VARIANT2_TEST_HAS_OPTIONAL 1
27+
#endif
28+
29+
// Check for C++20 coroutine support
30+
#if defined(__cpp_impl_coroutine) && __cpp_impl_coroutine >= 201902L
31+
# include <coroutine>
32+
# define BOOST_VARIANT2_TEST_HAS_CORO 1
33+
#endif
34+
35+
using result_void = boost::system::result<void, std::exception_ptr>;
36+
using result_string = boost::system::result<std::string, std::exception_ptr>;
37+
38+
void testGccUninitialized()
39+
{
40+
// Test 1: Simple copy construction
41+
{
42+
result_void r1;
43+
result_void r2(r1);
44+
(void)r2;
45+
}
46+
47+
// Test 2: Copy assignment
48+
{
49+
result_void r1;
50+
result_void r2;
51+
r2 = r1;
52+
(void)r2;
53+
}
54+
55+
#ifdef BOOST_VARIANT2_TEST_HAS_OPTIONAL
56+
// Test 3: std::optional assignment (matches spawn pattern)
57+
{
58+
std::optional<result_void> opt;
59+
opt = result_void{};
60+
(void)opt;
61+
}
62+
#endif
63+
64+
// Test 4: Pass to function via copy
65+
{
66+
auto fn = [](result_void r) { (void)r; };
67+
fn(result_void{});
68+
}
69+
70+
#ifdef BOOST_VARIANT2_TEST_HAS_OPTIONAL
71+
// Test 5: Lambda capture + optional (closest to spawn)
72+
{
73+
auto fn = [](result_void r) {
74+
std::optional<result_void> opt;
75+
opt = r;
76+
return opt.has_value();
77+
};
78+
(void)fn(result_void{});
79+
}
80+
#endif
81+
82+
// Test 6: Non-void result with string (triggers string warning)
83+
{
84+
result_string r1;
85+
result_string r2(r1);
86+
(void)r2;
87+
}
88+
89+
// Test 7: Assign exception to result holding value
90+
{
91+
result_string r1{"hello"};
92+
r1 = std::make_exception_ptr(std::runtime_error("test"));
93+
(void)r1;
94+
}
95+
96+
#ifdef BOOST_VARIANT2_TEST_HAS_OPTIONAL
97+
// Test 8: Optional with string result
98+
{
99+
std::optional<result_string> opt;
100+
opt = result_string{};
101+
(void)opt;
102+
}
103+
#endif
104+
105+
#ifdef BOOST_VARIANT2_TEST_HAS_CORO
106+
// Minimal fire-and-forget coroutine for testing
107+
struct fire_and_forget
108+
{
109+
struct promise_type
110+
{
111+
fire_and_forget get_return_object() { return {}; }
112+
std::suspend_never initial_suspend() noexcept { return {}; }
113+
std::suspend_never final_suspend() noexcept { return {}; }
114+
void return_void() {}
115+
void unhandled_exception() { std::terminate(); }
116+
};
117+
};
118+
119+
// Test 9: Coroutine returning result (mimics spawn)
120+
{
121+
auto coro = []() -> fire_and_forget {
122+
result_void r{};
123+
(void)r;
124+
co_return;
125+
};
126+
coro();
127+
}
128+
129+
// Test 10: Coroutine with handler call (closest to actual spawn)
130+
{
131+
std::optional<result_void> received;
132+
auto handler = [&](result_void r) {
133+
received = r;
134+
};
135+
auto coro = [&]() -> fire_and_forget {
136+
handler(result_void{});
137+
co_return;
138+
};
139+
coro();
140+
(void)received;
141+
}
142+
143+
// Test 11: Coroutine with try/catch like spawn
144+
{
145+
std::optional<result_void> received;
146+
auto handler = [&](result_void r) {
147+
received = r;
148+
};
149+
auto coro = [&]() -> fire_and_forget {
150+
try
151+
{
152+
handler(result_void{});
153+
}
154+
catch (...)
155+
{
156+
handler(result_void{std::current_exception()});
157+
}
158+
co_return;
159+
};
160+
coro();
161+
(void)received;
162+
}
163+
164+
// Test 12: Coroutine with string result
165+
{
166+
std::optional<result_string> received;
167+
auto handler = [&](result_string r) {
168+
received = r;
169+
};
170+
auto coro = [&]() -> fire_and_forget {
171+
try
172+
{
173+
handler(result_string{"test"});
174+
}
175+
catch (...)
176+
{
177+
handler(result_string{std::current_exception()});
178+
}
179+
co_return;
180+
};
181+
coro();
182+
(void)received;
183+
}
184+
#endif
185+
}
186+
187+
int main()
188+
{
189+
testGccUninitialized();
190+
return boost::report_errors();
191+
}

0 commit comments

Comments
 (0)