All of lore.kernel.org
 help / color / mirror / Atom feed
From: Markus Armbruster <armbru@redhat.com>
To: Paolo Bonzini <pbonzini@redhat.com>
Cc: qemu-devel@nongnu.org
Subject: Re: [PATCH 1/3] scripts/qapi: enum with conditional first item must be optional
Date: Sat, 25 Jul 2026 08:15:10 +0200	[thread overview]
Message-ID: <87a4rf61e9.fsf@pond.sub.org> (raw)
In-Reply-To: <20260724130044.1369843-2-pbonzini@redhat.com> (Paolo Bonzini's message of "Fri, 24 Jul 2026 15:00:41 +0200")

Paolo Bonzini <pbonzini@redhat.com> writes:

> Prevent an all-zero struct from having different meanings with
> different configurations or builds of QEMU.

This is a bit terse.  What *exactly* is broken?  Spelling this out
matters, because it can expose holes in the argument, if any.  Let me
try.

The numeric encoding of enum values is irrelevant except for zero,
because we actually use numeric zero via zero initialization.  If the
enum's first value is conditional, the meaning of zero depends on build
configuration.  We don't want such a default at the external interface.
It could conceivably lead to purely internal bugs, too.

However, I'm not sure your solution fixes this problem completely.

Consider type Arg with a mandatory member @mand and an optional enum
member @opt, both of enum type ENUM_TYPE.

QMP input gets converted to native C like this:

    if (!visit_type_Arg(v, NULL, &arg, errp)) {
        return;
    }

For members present in the input, there is no zero initialization
problem.

@mand is always present, so arg.mand is whatever the user said.

If @opt is present, arg.has_opt is true and arg.opt is whatever the user
said.

If @opt is absent, then arg.has_opt and arg.opt are both zero.

Code providing an explicit default then is still fine:

    if (!arg.has_opt) {
        arg.opt = ENUM_TYPE_MUMBLE;
    }

But we often use arg.opt without checking arg.has_opt for brevity.  This
is an implicit default to zero, whatever zero may mean.

If the the optional enum's first member is conditional, this default
depends on build configuration.

Stupidest solution that could possibly work: an enum's first member
cannot be conditional.

Thoughts?

> This needs some changes to doc-good.json, which used unwittingly
> such an enum.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  scripts/qapi/schema.py                        |  8 ++++++++
>  tests/qapi-schema/doc-good.json               | 16 ++++++++--------
>  tests/qapi-schema/doc-good.out                | 12 ++++++------
>  tests/qapi-schema/doc-good.txt                |  8 ++++----
>  tests/qapi-schema/enum-if-first-required.err  |  2 ++
>  tests/qapi-schema/enum-if-first-required.json |  6 ++++++
>  tests/qapi-schema/enum-if-first-required.out  |  0
>  tests/qapi-schema/meson.build                 |  1 +
>  8 files changed, 35 insertions(+), 18 deletions(-)
>  create mode 100644 tests/qapi-schema/enum-if-first-required.err
>  create mode 100644 tests/qapi-schema/enum-if-first-required.json
>  create mode 100644 tests/qapi-schema/enum-if-first-required.out

Thanks for the negative test.

docs/devel/qapi-code-gen.rst section "Enumeration types" could perhaps
use an update.

It's less than clear even before the patch:

    The generated C enumeration constants have values 0, 1, ..., N-1 (in
    QAPI schema order), where N is the number of values.  There is an
    additional enumeration constant PREFIX__MAX with value N.

    Do not use string or an integer type when an enumeration type can do
    the job satisfactorily.

    The optional 'if' member specifies a conditional.  See `Configuring the
    schema`_ below for more on this.

The first paragraph can lead readers to assume we generate something
like

    typedef enum Example {
        EXAMPLE_FOO = 0,
    #if defined(COND)
        EXAMPLE_BAR = 1,
    #endif
        EXAMPLE__MAX = 2,
    } Example;

We don't, because we'd have to deal with a "hole" in the value range
when COND is not defined.

Instead, we do
        
    typedef enum Example {
        EXAMPLE_FOO,
    #if defined(COND)
        EXAMPLE_BAR,
    #endif
        EXAMPLE__MAX,
    } Example;



  reply	other threads:[~2026-07-25  6:15 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-24 13:00 [PATCH resend 0/3] scripts/qapi: non-Rust-specific preparatory patches Paolo Bonzini
2026-07-24 13:00 ` [PATCH 1/3] scripts/qapi: enum with conditional first item must be optional Paolo Bonzini
2026-07-25  6:15   ` Markus Armbruster [this message]
2026-07-24 13:00 ` [PATCH 2/3] scripts/qapi: reject empty enums Paolo Bonzini
2026-07-24 13:00 ` [PATCH 3/3] scripts/qapi: pull c_name and lstrip from camel_to_upper to caller Paolo Bonzini

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=87a4rf61e9.fsf@pond.sub.org \
    --to=armbru@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.