Skip to content

fix: add -Wmaybe-uninitialized pragma to union storage constructors#145

Merged
akrzemi1 merged 1 commit intoboostorg:developfrom
alandefreitas:fix/maybe-uninitialized-pragma
Feb 25, 2026
Merged

fix: add -Wmaybe-uninitialized pragma to union storage constructors#145
akrzemi1 merged 1 commit intoboostorg:developfrom
alandefreitas:fix/maybe-uninitialized-pragma

Conversation

@alandefreitas
Copy link
Member

GCC 7+ at -O3 produces false-positive -Wmaybe-uninitialized warnings when inlining through the constexpr_union_storage_t and fallback_union_storage_t constructors. Same class of false positive as boostorg/variant2#55 — union with a dummy and value member, constructor initializes value.

Adds scoped pragmas around both forwarding constructors in union_optional.hpp.

Refs boostorg/url#981

GCC 7+ produces false-positive -Wmaybe-uninitialized warnings at -O3
when deeply inlining through union constructors that initialize one
member while another (dummy_) exists. This is the same class of false
positive addressed in boostorg/variant2#55.

The pragmas are placed around the forwarding constructors in both
constexpr_union_storage_t and fallback_union_storage_t.
@alandefreitas alandefreitas force-pushed the fix/maybe-uninitialized-pragma branch from f6bcfaa to 88e2378 Compare February 24, 2026 17:11
@akrzemi1 akrzemi1 merged commit ff710d1 into boostorg:develop Feb 25, 2026
22 of 49 checks passed
@alandefreitas
Copy link
Member Author

Complementing the PR for future reference:

In most cases, Boost.URL builds cleanly with GCC 15 at -O3 and no errors. The variant2 fix (merged upstream) plus a local pragma in Boost.URL are enough. The optional false positive only surfaces with ASan instrumentation in CI on x86_64 Linux. Even GCC 15 + ASan on ARM (macOS) doesn't trigger it, because ASan changes inlining thresholds and code layout differently per target.

CI run without the Boost.Optional patch (showing the errors): https://github.com/boostorg/url/actions/runs/22404543043/job/64860913382?pr=981

CI run with the patch (all green): https://github.com/boostorg/url/actions/runs/22371586620

Why this is a false positive

The warning is about variant2's internal storage member st_ being "potentially uninitialized". The root cause is variant2's nested union patterns confusing GCC, but the warning surfaces at optional because that's the next link in the forwarding chain after inlining.

Here's the chain:

  1. variant_rule.hpp:75 constructs a variant2::variant<size_t, optional<variant<string_view, size_t>>> with {in_place_index_t<I>{}, *rv}. The *rv dereferences a system::result<T> that was just checked with if(rv) at line 71, so the value is guaranteed initialized.

  2. This variant construction goes through variant2's variant_storage_impl union constructor, which initializes first_ or rest_ depending on the index. Variant2's pragma covers this part (boostorg/variant2#57, merged).

  3. One of the variant's alternatives is optional<variant<string_view, size_t>>. When constructing it, optional wraps the inner variant via constexpr_union_storage_t(Args&&...) at union_optional.hpp:78, which does value_(forward_<Args>(args)...).

  4. GCC's optimizer inlines the entire chain (parse_variant -> variant construction -> variant2 storage -> optional storage -> union constructor). After inlining, GCC sees a union (dummy_ + value_) where value_ is initialized from a forwarded argument originating from *rv. GCC's data flow analysis loses track of the if(rv) guard across all these layers. It conflates the "uninitialized dummy_ member" path with the "initialized value_ member" path and concludes st_ (at offset +8) might be uninitialized.

Whose "fault" is it?

The root cause is variant2's nested union storage being opaque to GCC's optimizer. But GCC reports -Wmaybe-uninitialized at the point of use, not at the point of origin. After inlining, the "use" happens at optional's forwarding constructor. A pragma in variant2 only suppresses warnings emitted at variant2's source locations. It can't suppress a warning GCC decides to emit at optional's source location. So the pragma has to go where GCC reports it.

This is the same pattern we hit in Boost.URL itself: variant2 was the "cause", but GCC blamed our tuple_rule.hpp, so the pragma had to go there. When variant2 wraps through optional, GCC blames optional.

Why __GNUC__ >= 7?

All false positives we've encountered over time affect every GCC version since 7. For this specific optional case, we only test ASan with GCC 15 in Boost.URL CI, so we don't have proof for older versions. But the pattern is identical to the variant2 false positives (union forwarding constructor), which are confirmed on GCC 7-15, so >= 7 is a reasonable lower bound.

Errors

GCC 15 ASan errors without the patch (click to expand)
  [  0%] Built target boost_optional
  [  0%] Built target boost_assert
  [  0%] Built target boost_unordered
  [  2%] Building CXX object libs/url/test/limits/CMakeFiles/boost_url_small_limits.dir/__/__/src/decode_view.cpp.o
  [  2%] Building CXX object libs/url/extra/test_suite/CMakeFiles/boost_url_test_suite.dir/test_suite.cpp.o
  [  2%] Building C object libs/container/CMakeFiles/boost_container.dir/src/alloc_lib.c.o
  [  3%] Building CXX object libs/filesystem/CMakeFiles/boost_filesystem.dir/src/codecvt_error_category.cpp.o
  [  4%] Building CXX object libs/filesystem/CMakeFiles/boost_filesystem.dir/src/exception.cpp.o
  [  4%] Building CXX object libs/url/test/limits/CMakeFiles/boost_url_small_limits.dir/__/__/src/detail/decode.cpp.o
  [  5%] Building CXX object libs/container/CMakeFiles/boost_container.dir/src/dlmalloc.cpp.o
  [  6%] Building CXX object libs/container/CMakeFiles/boost_container.dir/src/global_resource.cpp.o
  [  6%] Linking CXX static library ../../../../stage/lib/libboost_url_test_suite.a
  [  6%] Built target boost_url_test_suite
  [  6%] Building CXX object libs/filesystem/CMakeFiles/boost_filesystem.dir/src/operations.cpp.o
  [  6%] Building CXX object libs/container/CMakeFiles/boost_container.dir/src/monotonic_buffer_resource.cpp.o
  [  7%] Building CXX object libs/url/test/limits/CMakeFiles/boost_url_small_limits.dir/__/__/src/detail/except.cpp.o
  [  8%] Building CXX object libs/container/CMakeFiles/boost_container.dir/src/pool_resource.cpp.o
  [  8%] Building CXX object libs/container/CMakeFiles/boost_container.dir/src/synchronized_pool_resource.cpp.o
  [  9%] Building CXX object libs/container/CMakeFiles/boost_container.dir/src/unsynchronized_pool_resource.cpp.o
  [ 10%] Linking CXX static library ../../stage/lib/libboost_container.a
  [ 10%] Built target boost_container
  [ 11%] Building CXX object libs/filesystem/CMakeFiles/boost_filesystem.dir/src/directory.cpp.o
  [ 13%] Building CXX object libs/url/test/limits/CMakeFiles/boost_url_small_limits.dir/__/__/src/detail/format_args.cpp.o
  [ 13%] Building CXX object libs/url/test/limits/CMakeFiles/boost_url_small_limits.dir/__/__/src/detail/normalize.cpp.o
  [ 14%] Building CXX object libs/url/test/limits/CMakeFiles/boost_url_small_limits.dir/__/__/src/detail/pattern.cpp.o
  In file included from /__w/url/boost-root/libs/optional/include/boost/optional/optional.hpp:52,
                   from /__w/url/boost-root/libs/optional/include/boost/optional.hpp:15,
                   from /__w/url/boost-root/libs/url/include/boost/url/optional.hpp:14,
                   from /__w/url/boost-root/libs/url/include/boost/url/grammar/optional_rule.hpp:15,
                   from /__w/url/boost-root/libs/url/src/detail/format_args.cpp:16:
  /__w/url/boost-root/libs/optional/include/boost/optional/detail/union_optional.hpp: In member function 'const char* boost::urls::detail::integer_formatter_impl::parse(boost::urls::detail::format_parse_context&)':
  /__w/url/boost-root/libs/optional/include/boost/optional/detail/union_optional.hpp:78:61: error: '*(const boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int>*)((char*)&<unnamed> + offsetof(boost::variant2::variant<long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >,boost::variant2::variant<long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_ma_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_mc_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_ca_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_cc_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_base_impl<true, true, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::st_) + 8)' may be used uninitialized [-Werror=maybe-uninitialized]
     78 |     constexpr constexpr_union_storage_t( Args&&... args ) : value_(forward_<Args>(args)...) {}
        |                                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  In file included from /__w/url/boost-root/libs/url/include/boost/url/grammar/variant_rule.hpp:121,
                   from /__w/url/boost-root/libs/url/include/boost/url/detail/replacement_field_rule.hpp:15,
                   from /__w/url/boost-root/libs/url/src/detail/format_args.cpp:14:
  /__w/url/boost-root/libs/url/include/boost/url/grammar/impl/variant_rule.hpp:75:53: note: '<anonymous>' declared here
     75 |                 variant2::in_place_index_t<I>{}, *rv};
        |                                                     ^
  /__w/url/boost-root/libs/optional/include/boost/optional/detail/union_optional.hpp: In member function 'const char* boost::urls::detail::formatter<boost::core::basic_string_view<char> >::parse(boost::urls::detail::format_parse_context&)':
  /__w/url/boost-root/libs/optional/include/boost/optional/detail/union_optional.hpp:78:61: error: '*(const boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int>*)((char*)&<unnamed> + offsetof(boost::variant2::variant<long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >,boost::variant2::variant<long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_ma_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_mc_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_ca_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_cc_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_base_impl<true, true, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::st_) + 8)' may be used uninitialized [-Werror=maybe-uninitialized]
     78 |     constexpr constexpr_union_storage_t( Args&&... args ) : value_(forward_<Args>(args)...) {}
        |                                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  /__w/url/boost-root/libs/url/include/boost/url/grammar/impl/variant_rule.hpp:75:53: note: '<anonymous>' declared here
     75 |                 variant2::in_place_index_t<I>{}, *rv};
        |                                                     ^
  cc1plus: all warnings being treated as errors
  gmake[3]: *** [libs/url/test/limits/CMakeFiles/boost_url_small_limits.dir/build.make:121: libs/url/test/limits/CMakeFiles/boost_url_small_limits.dir/__/__/src/detail/format_args.cpp.o] Error 1
  gmake[3]: *** Waiting for unfinished jobs....
  [ 15%] Building CXX object libs/url/CMakeFiles/boost_url.dir/src/decode_view.cpp.o
  [ 15%] Building CXX object libs/filesystem/CMakeFiles/boost_filesystem.dir/src/path.cpp.o
  [ 16%] Building CXX object libs/url/extra/test_suite/CMakeFiles/boost_url_test_suite_with_main.dir/test_main.cpp.o
  [ 16%] Linking CXX static library ../../../../stage/lib/libboost_url_test_suite_with_main.a
  [ 16%] Built target boost_url_test_suite_with_main
  [ 16%] Building CXX object libs/url/CMakeFiles/boost_url.dir/src/detail/decode.cpp.o
  [ 17%] Building CXX object libs/filesystem/CMakeFiles/boost_filesystem.dir/src/path_traits.cpp.o
  [ 18%] Building CXX object libs/url/CMakeFiles/boost_url.dir/src/detail/except.cpp.o
  [ 18%] Building CXX object libs/filesystem/CMakeFiles/boost_filesystem.dir/src/portability.cpp.o
  [ 19%] Building CXX object libs/filesystem/CMakeFiles/boost_filesystem.dir/src/unique_path.cpp.o
  [ 19%] Building CXX object libs/url/CMakeFiles/boost_url.dir/src/detail/format_args.cpp.o
  [ 20%] Building CXX object libs/url/CMakeFiles/boost_url.dir/src/detail/normalize.cpp.o
  [ 21%] Building CXX object libs/filesystem/CMakeFiles/boost_filesystem.dir/src/utf8_codecvt_facet.cpp.o
  [ 21%] Linking CXX static library ../../stage/lib/libboost_filesystem.a
  [ 21%] Built target boost_filesystem
  [ 22%] Building CXX object libs/url/CMakeFiles/boost_url.dir/src/detail/pattern.cpp.o
  In file included from /__w/url/boost-root/libs/optional/include/boost/optional/optional.hpp:52,
                   from /__w/url/boost-root/libs/optional/include/boost/optional.hpp:15,
                   from /__w/url/boost-root/libs/url/include/boost/url/optional.hpp:14,
                   from /__w/url/boost-root/libs/url/include/boost/url/grammar/optional_rule.hpp:15,
                   from /__w/url/boost-root/libs/url/src/detail/format_args.cpp:16:
  /__w/url/boost-root/libs/optional/include/boost/optional/detail/union_optional.hpp: In member function 'const char* boost::urls::detail::integer_formatter_impl::parse(boost::urls::detail::format_parse_context&)':
  /__w/url/boost-root/libs/optional/include/boost/optional/detail/union_optional.hpp:78:61: error: '*(const boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int>*)((char*)&<unnamed> + offsetof(boost::variant2::variant<long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >,boost::variant2::variant<long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_ma_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_mc_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_ca_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_cc_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_base_impl<true, true, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::st_) + 8)' may be used uninitialized [-Werror=maybe-uninitialized]
     78 |     constexpr constexpr_union_storage_t( Args&&... args ) : value_(forward_<Args>(args)...) {}
        |                                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  In file included from /__w/url/boost-root/libs/url/include/boost/url/grammar/variant_rule.hpp:121,
                   from /__w/url/boost-root/libs/url/include/boost/url/detail/replacement_field_rule.hpp:15,
                   from /__w/url/boost-root/libs/url/src/detail/format_args.cpp:14:
  /__w/url/boost-root/libs/url/include/boost/url/grammar/impl/variant_rule.hpp:75:53: note: '<anonymous>' declared here
     75 |                 variant2::in_place_index_t<I>{}, *rv};
        |                                                     ^
  /__w/url/boost-root/libs/optional/include/boost/optional/detail/union_optional.hpp: In member function 'const char* boost::urls::detail::formatter<boost::core::basic_string_view<char> >::parse(boost::urls::detail::format_parse_context&)':
  /__w/url/boost-root/libs/optional/include/boost/optional/detail/union_optional.hpp:78:61: error: '*(const boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int>*)((char*)&<unnamed> + offsetof(boost::variant2::variant<long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >,boost::variant2::variant<long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_ma_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_mc_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_ca_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_cc_base_impl<true, false, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::<unnamed>.boost::variant2::detail::variant_base_impl<true, true, long unsigned int, boost::optional<boost::variant2::variant<boost::core::basic_string_view<char>, long unsigned int> > >::st_) + 8)' may be used uninitialized [-Werror=maybe-uninitialized]
     78 |     constexpr constexpr_union_storage_t( Args&&... args ) : value_(forward_<Args>(args)...) {}
        |                                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  /__w/url/boost-root/libs/url/include/boost/url/grammar/impl/variant_rule.hpp:75:53: note: '<anonymous>' declared here
     75 |                 variant2::in_place_index_t<I>{}, *rv};
        |                                                     ^
  cc1plus: all warnings being treated as errors
  gmake[3]: *** [libs/url/CMakeFiles/boost_url.dir/build.make:121: libs/url/CMakeFiles/boost_url.dir/src/detail/format_args.cpp.o] Error 1
  gmake[3]: *** Waiting for unfinished jobs....
  gmake[2]: *** [CMakeFiles/Makefile2:2982: libs/url/test/limits/CMakeFiles/boost_url_small_limits.dir/all] Error 2
  gmake[2]: *** Waiting for unfinished jobs....
  gmake[2]: *** [CMakeFiles/Makefile2:2809: libs/url/CMakeFiles/boost_url.dir/all] Error 2
  gmake[1]: *** [CMakeFiles/Makefile2:2360: CMakeFiles/tests.dir/rule] Error 2
  gmake: *** [Makefile:564: tests] Error 2

Source code at the warning locations (click to expand)

union_optional.hpp:78 -- the warning location:

template <class T>
union constexpr_union_storage_t
{
    static_assert(::std::is_trivially_destructible<T>::value, "!!");

    unsigned char dummy_;
    T value_;

    constexpr constexpr_union_storage_t( trivial_init_t ) noexcept : dummy_() {};

    template <class... Args>
    constexpr constexpr_union_storage_t( Args&&... args ) : value_(forward_<Args>(args)...) {}
    //                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    //                                                      GCC warns here
};

variant_rule.hpp:71-75 -- the "declared here" note:

    if( rv )                              // guard: rv is checked here
        return variant2::variant<
            typename R0::value_type,
            typename Rn::value_type...>{
                variant2::in_place_index_t<I>{}, *rv};  // *rv is safe, but GCC loses track

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants