From: Andrew Burgess <aburgess@redhat.com>
To: Tom de Vries <tdevries@suse.de>, gdb-patches@sourceware.org
Subject: Re: [PATCH] [gdb/build] Fix Wmaybe-uninitialized false positive for ui_out_emit_type
Date: Wed, 28 Oct 2026 17:16:18 +0000 [thread overview]
Message-ID: <87se1p7nkt.fsf@redhat.com> (raw)
In-Reply-To: <20261026163904.156660-1-tdevries@suse.de>
Tom de Vries <tdevries@suse.de> writes:
> With an aarch64-linux -O2 build with gcc 16.2.1, I run into:
> ...
> In file included from gdb/exceptions.h:23,
> from gdb/symtab.c:21:
> In destructor ‘ui_out_emit_type<Type>::~ui_out_emit_type() [with ui_out_type Type = ui_out_type_tuple]’,
> inlined from ‘constexpr void std::_Optional_payload_base<_Tp>::_M_destroy() [with _Tp = ui_out_emit_type<ui_out_type_tuple>]’ at /usr/include/c++/16/optional:325:35,
> inlined from ‘constexpr void std::_Optional_payload_base<_Tp>::_M_reset() [with _Tp = ui_out_emit_type<ui_out_type_tuple>]’ at /usr/include/c++/16/optional:361:14,
> inlined from ‘constexpr void std::_Optional_payload_base<_Tp>::_M_reset() [with _Tp = ui_out_emit_type<ui_out_type_tuple>]’ at /usr/include/c++/16/optional:358:7,
> inlined from ‘std::_Optional_payload<_Tp, false, _Copy, _Move>::~_Optional_payload() [with _Tp = ui_out_emit_type<ui_out_type_tuple>; bool _Copy = false; bool _Move = false]’ at /usr/include/c++/16/optional:484:65,
> inlined from ‘std::_Optional_base<ui_out_emit_type<ui_out_type_tuple>, false, false>::~_Optional_base()’ at /usr/include/c++/16/optional:508:12,
> inlined from ‘std::optional<ui_out_emit_type<ui_out_type_tuple> >::~optional()’ at /usr/include/c++/16/optional:812:11,
> inlined from ‘void info_sources_worker(ui_out*, bool, const info_sources_filter&)’ at gdb/symtab.c:4544:1:
> gdb/ui-out.h:428:18: warning: ‘*(ui_out_emit_type<ui_out_type_tuple>*)((char*)&output_tuple + offsetof(std::optional<ui_out_emit_type<ui_out_type_tuple> >,std::optional<ui_out_emit_type<ui_out_type_tuple> >::<unnamed>.std::_Optional_base<ui_out_emit_type<ui_out_type_tuple>, false, false>::_M_payload.std::_Optional_payload<ui_out_emit_type<ui_out_type_tuple>, false, false, false>::<unnamed>.std::_Optional_payload<ui_out_emit_type<ui_out_type_tuple>, true, false, false>::<unnamed>.std::_Optional_payload_base<ui_out_emit_type<ui_out_type_tuple> >::_M_payload)).ui_out_emit_type<ui_out_type_tuple>::m_uiout’ may be used uninitialized [-Wmaybe-uninitialized]
> 428 | m_uiout->end (Type);
> | ~~~~~~~~~~~~~^~~~~~
> gdb/symtab.c: In function ‘void info_sources_worker(ui_out*, bool, const info_sources_filter&)’:
> gdb/symtab.c:4477:36: note: ‘*(ui_out_emit_type<ui_out_type_tuple>*)((char*)&output_tuple + offsetof(std::optional<ui_out_emit_type<ui_out_type_tuple> >,std::optional<ui_out_emit_type<ui_out_type_tuple> >::<unnamed>.std::_Optional_base<ui_out_emit_type<ui_out_type_tuple>, false, false>::_M_payload.std::_Optional_payload<ui_out_emit_type<ui_out_type_tuple>, false, false, false>::<unnamed>.std::_Optional_payload<ui_out_emit_type<ui_out_type_tuple>, true, false, false>::<unnamed>.std::_Optional_payload_base<ui_out_emit_type<ui_out_type_tuple> >::_M_payload)).ui_out_emit_type<ui_out_type_tuple>::m_uiout’ was declared here
> 4477 | std::optional<ui_out_emit_tuple> output_tuple;
> | ^~~~~~~~~~~~
> ...
>
> The code pattern triggering this is:
> ...
> std::optional<ui_out_emit_tuple> output_tuple;
> for (objfile &objfile : current_program_space->objfiles ())
> {
> if (group_by_objfile)
> output_tuple.emplace (uiout, nullptr);
> ...
> if (group_by_objfile)
> output_tuple.reset ();
> }
> ...
>
> This is a false positive.
>
> We can get rid of the warning by making changes to the function:
> - making the reset unconditional, or
> - moving the output_tuple declaration into the loop.
>
> But instead, consider that the class ui_out_emit_type wraps a pointer.
>
> An std::optional<void *> has three possible states: !has_value, nullptr or
> non-nullptr. If we don't use the nullptr state, using std::optional<void *>
> is overkill, and we can use nullptr instead of !has_value.
>
> Rewrite ui_out_emit_type to provide some of the std::optional interface:
> - default constructor
> - reset
> - emplace
> - has_value
> with protected access, and derive an opt_ui_out_emit_type which inherits those
> with public access.
>
> As a side effect, we get rid of the warning.
>
> Tested on aarch64-linux.
>
> Tested on x86_64-linux with gcc 7.5.0, where all the Wmaybe-uninitialized
> reported in PR31150 are now fixed.
>
> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31150
> ---
> gdb/breakpoint.c | 11 +++----
> gdb/disasm.c | 8 ++---
> gdb/mi/mi-cmd-stack.c | 2 +-
> gdb/mi/mi-main.c | 2 +-
> gdb/python/py-framefilter.c | 6 ++--
> gdb/record-btrace.c | 8 ++---
> gdb/symtab.c | 4 +--
> gdb/thread.c | 2 +-
> gdb/ui-out.h | 63 ++++++++++++++++++++++++++++++++++---
> 9 files changed, 80 insertions(+), 26 deletions(-)
>
> diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
> index 0f5360c4ce5..9995c6ab21b 100644
> --- a/gdb/breakpoint.c
> +++ b/gdb/breakpoint.c
> @@ -7150,8 +7150,8 @@ print_one_breakpoint_location (struct breakpoint *b,
> (uiout->test_flags (fix_breakpoint_script_output)
> || fix_breakpoint_script_output_globally);
>
> - std::optional<ui_out_emit_tuple> tuple_emitter;
> - std::optional<ui_out_emit_list> list_emitter;
> + opt_ui_out_emit_tuple tuple_emitter;
> + opt_ui_out_emit_list list_emitter;
>
> if (use_fixed_output)
> list_emitter.emplace (uiout, "script");
> @@ -7224,8 +7224,7 @@ print_one_breakpoint (breakpoint *b, const bp_location **last_loc, int allflag)
> = (uiout->test_flags (fix_multi_location_breakpoint_output)
> || fix_multi_location_breakpoint_output_globally);
>
> - std::optional<ui_out_emit_tuple> bkpt_tuple_emitter (std::in_place, uiout,
> - "bkpt");
> + opt_ui_out_emit_tuple bkpt_tuple_emitter (uiout, "bkpt");
> bool printed = print_one_breakpoint_location (b, NULL, 0, last_loc,
> allflag, false);
>
> @@ -7260,7 +7259,7 @@ print_one_breakpoint (breakpoint *b, const bp_location **last_loc, int allflag)
> || !b->first_loc ().enabled
> || b->first_loc ().disabled_by_cond))))
> {
> - std::optional<ui_out_emit_list> locations_list;
> + opt_ui_out_emit_list locations_list;
>
> /* For MI version <= 2, keep the behavior where GDB outputs an invalid
> MI record. For later versions, place breakpoint locations in a
> @@ -10278,7 +10277,7 @@ watchpoint::print_it (const bpstat *bs) const
>
> string_file stb;
>
> - std::optional<ui_out_emit_tuple> tuple_emitter;
> + opt_ui_out_emit_tuple tuple_emitter;
> switch (this->type)
> {
> case bp_watchpoint:
> diff --git a/gdb/disasm.c b/gdb/disasm.c
> index 01361fe4323..67f56faaecd 100644
> --- a/gdb/disasm.c
> +++ b/gdb/disasm.c
> @@ -610,8 +610,8 @@ do_mixed_source_and_assembly_deprecated
>
> ui_out_emit_list asm_insns_list (uiout, "asm_insns");
>
> - std::optional<ui_out_emit_tuple> outer_tuple_emitter;
> - std::optional<ui_out_emit_list> inner_list_emitter;
> + opt_ui_out_emit_tuple outer_tuple_emitter;
> + opt_ui_out_emit_list inner_list_emitter;
>
> for (i = 0; i < newlines; i++)
> {
> @@ -767,8 +767,8 @@ do_mixed_source_and_assembly (struct gdbarch *gdbarch,
>
> ui_out_emit_list asm_insns_emitter (uiout, "asm_insns");
>
> - std::optional<ui_out_emit_tuple> tuple_emitter;
> - std::optional<ui_out_emit_list> list_emitter;
> + opt_ui_out_emit_tuple tuple_emitter;
> + opt_ui_out_emit_list list_emitter;
>
> last_symtab = NULL;
> last_line = 0;
> diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
> index a3b577032ce..335f7e1fbef 100644
> --- a/gdb/mi/mi-cmd-stack.c
> +++ b/gdb/mi/mi-cmd-stack.c
> @@ -533,7 +533,7 @@ list_arg_or_local (const struct frame_arg *arg, enum what_to_list what,
> == lang_vars_shadowing::HIDE && shadowed && already_printed)
> return;
>
> - std::optional<ui_out_emit_tuple> tuple_emitter;
> + opt_ui_out_emit_tuple tuple_emitter;
> if (values != PRINT_NO_VALUES || what == all)
> tuple_emitter.emplace (uiout, nullptr);
>
> diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
> index d39b8eeab87..dcf76db39a3 100644
> --- a/gdb/mi/mi-main.c
> +++ b/gdb/mi/mi-main.c
> @@ -2455,7 +2455,7 @@ print_variable_or_computed (const char *expression, enum print_values values)
> else
> val = expr->evaluate ();
>
> - std::optional<ui_out_emit_tuple> tuple_emitter;
> + opt_ui_out_emit_tuple tuple_emitter;
> if (values != PRINT_NO_VALUES)
> tuple_emitter.emplace (uiout, nullptr);
> uiout->field_string ("name", expression);
> diff --git a/gdb/python/py-framefilter.c b/gdb/python/py-framefilter.c
> index b67431cf8c9..9899470cc0f 100644
> --- a/gdb/python/py-framefilter.c
> +++ b/gdb/python/py-framefilter.c
> @@ -321,7 +321,7 @@ py_print_single_arg (struct ui_out *out,
> else
> val = fv;
>
> - std::optional<ui_out_emit_tuple> maybe_tuple;
> + opt_ui_out_emit_tuple maybe_tuple;
>
> /* MI has varying rules for tuples, but generally if there is only
> one element in each item in the list, do not start a tuple. The
> @@ -557,7 +557,7 @@ enumerate_locals (PyObject *iter,
> struct symbol *sym;
> const struct block *sym_block;
> int local_indent = 8 + (8 * indent);
> - std::optional<ui_out_emit_tuple> tuple;
> + opt_ui_out_emit_tuple tuple;
>
> gdbpy_ref<> item (PyIter_Next (iter));
> if (item == NULL)
> @@ -814,7 +814,7 @@ py_print_frame (PyObject *filter, frame_filter_flags flags,
> return EXT_LANG_BT_OK;
> }
>
> - std::optional<ui_out_emit_tuple> tuple;
> + opt_ui_out_emit_tuple tuple;
>
> /* -stack-list-locals does not require a
> wrapping frame attribute. */
> diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
> index dae243ee2db..5c1bb606130 100644
> --- a/gdb/record-btrace.c
> +++ b/gdb/record-btrace.c
> @@ -763,8 +763,8 @@ btrace_find_line_range (CORE_ADDR pc)
>
> static void
> btrace_print_lines (struct btrace_line_range lines, struct ui_out *uiout,
> - std::optional<ui_out_emit_tuple> *src_and_asm_tuple,
> - std::optional<ui_out_emit_list> *asm_list,
> + opt_ui_out_emit_tuple *src_and_asm_tuple,
> + opt_ui_out_emit_list *asm_list,
> gdb_disassembly_flags flags)
> {
> print_source_lines_flags psl_flags;
> @@ -803,8 +803,8 @@ btrace_insn_history (struct ui_out *uiout,
>
> ui_out_emit_list list_emitter (uiout, "asm_insns");
>
> - std::optional<ui_out_emit_tuple> src_and_asm_tuple;
> - std::optional<ui_out_emit_list> asm_list;
> + opt_ui_out_emit_tuple src_and_asm_tuple;
> + opt_ui_out_emit_list asm_list;
>
> gdb_pretty_print_disassembler disasm (gdbarch, uiout);
>
> diff --git a/gdb/symtab.c b/gdb/symtab.c
> index b082a488b88..18fdc66d1f7 100644
> --- a/gdb/symtab.c
> +++ b/gdb/symtab.c
> @@ -4474,8 +4474,8 @@ info_sources_worker (struct ui_out *uiout,
> output_source_filename_data data (uiout, filter);
>
> ui_out_emit_list results_emitter (uiout, "files");
> - std::optional<ui_out_emit_tuple> output_tuple;
> - std::optional<ui_out_emit_list> sources_list;
> + opt_ui_out_emit_tuple output_tuple;
> + opt_ui_out_emit_list sources_list;
>
> gdb_assert (group_by_objfile || uiout->is_mi_like_p ());
>
> diff --git a/gdb/thread.c b/gdb/thread.c
> index e14254619e3..5fa2bb6915c 100644
> --- a/gdb/thread.c
> +++ b/gdb/thread.c
> @@ -1296,7 +1296,7 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads,
> /* For backward compatibility, we make a list for MI. A table is
> preferable for the CLI, though, because it shows table
> headers. */
> - std::optional<ui_out_emit_list> list_emitter;
> + opt_ui_out_emit_list list_emitter;
> std::optional<ui_out_emit_table> table_emitter;
>
> /* We'll be switching threads temporarily below. */
> diff --git a/gdb/ui-out.h b/gdb/ui-out.h
> index 26d4a6b7d0e..8717d93539d 100644
> --- a/gdb/ui-out.h
> +++ b/gdb/ui-out.h
> @@ -418,25 +418,80 @@ class ui_out_emit_type
> public:
>
> ui_out_emit_type (struct ui_out *uiout, const char *id = nullptr)
> - : m_uiout (uiout)
> {
> - uiout->begin (Type, id);
> + emplace (uiout, id);
> }
>
> ~ui_out_emit_type ()
> {
> - m_uiout->end (Type);
> + reset ();
> }
>
> DISABLE_COPY_AND_ASSIGN (ui_out_emit_type);
>
> +protected:
> +
> + ui_out_emit_type () = default;
> +
> + /* Support the std::optional interface, see opt_ui_out_emit_type. */
> + ui_out_emit_type &emplace (struct ui_out *uiout, const char *id = nullptr)
> + {
> + reset ();
> + uiout->begin (Type, id);
> + m_uiout = uiout;
> + return *this;
> + }
> +
> + /* Support the std::optional interface, see opt_ui_out_emit_type. */
> + void reset () noexcept
> + {
> + if (!has_value ())
> + return;
> +
> + m_uiout->end (Type);
> + m_uiout = nullptr;
> + }
> +
> + /* Support the std::optional interface, see opt_ui_out_emit_type. */
> + bool has_value () const noexcept
> + {
> + return m_uiout != nullptr;
> + }
> +
> private:
> + struct ui_out *m_uiout = nullptr;
> +};
>
> - struct ui_out *m_uiout;
> +/* Stand-in for std::optional<ui_out_emit_type<Type>>.
> + Support (as needed) some of the std::optional interface, allowing it to be
> + used as if it were std::optional<ui_out_emit_type<Type>>. There are 2
> + reasons why we don't want to use the std::optional form:
> + - Wmaybe-uninitialized false positives, and
> + - it's overkill. With std::optional, we can have three states:
> + - !has_value ()
> + - has_value () && m_uiout == nullptr
> + - has_value () && m_uiout != nullptr
> + and we don't use the middle one. */
> +template<ui_out_type Type>
> +class opt_ui_out_emit_type : ui_out_emit_type<Type>
> +{
> + using base = ui_out_emit_type<Type>;
> +
> +public:
> + opt_ui_out_emit_type () = default;
> +
> + using base::base;
> + using base::emplace;
> + using base::reset;
> + using base::has_value;
> +
> + DISABLE_COPY_AND_ASSIGN (opt_ui_out_emit_type);
> };
>
> using ui_out_emit_tuple = ui_out_emit_type<ui_out_type_tuple>;
> +using opt_ui_out_emit_tuple = opt_ui_out_emit_type<ui_out_type_tuple>;
> using ui_out_emit_list = ui_out_emit_type<ui_out_type_list>;
> +using opt_ui_out_emit_list = opt_ui_out_emit_type<ui_out_type_list>;
>
Did you consider adding something like this:
namespace std
{
template<>
class
[[deprecated ("std::optional<ui_out_emit_tuple> is disabled; opt_ui_out_emit_tuple instead")]]
optional<ui_out_emit_tuple>
{
public:
/* Delete all constructors to prevent object creation. */
optional() = delete;
optional(const optional&) = delete;
optional(optional&&) = delete;
template <typename... Args>
optional(Args&&...) = delete;
};
}
This should be valid(ish) since C++14. I say "ish" because I think
overriding things in std:: without providing an API compatible
replacement is I think itself bad. But given we want this to not
compile I suspect this would be good enough.
You'd want to add something similar for ui_out_emit_list too.
The goal here is to enforce the use of your new std::optional
replacements.
thoughts?
Thanks,
Andrew
prev parent reply other threads:[~2026-10-28 17:16 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-26 16:39 Tom de Vries
2026-10-28 17:16 ` Andrew Burgess [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87se1p7nkt.fsf@redhat.com \
--to=aburgess@redhat.com \
--cc=gdb-patches@sourceware.org \
--cc=tdevries@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox