From: Zhao Liu <zhao1.liu@intel.com>
To: Paolo Bonzini <pbonzini@redhat.com>
Cc: qemu-devel@nongnu.org, qemu-rust@nongnu.org, armbru@redhat.com
Subject: Re: [PATCH v3 15/19] scripts/qapi: generate high-level Rust bindings
Date: Thu, 4 Jun 2026 18:11:44 +0800 [thread overview]
Message-ID: <aiFPYDvtWdAXD1kk@intel.com> (raw)
In-Reply-To: <20260526175618.227743-16-pbonzini@redhat.com>
On Tue, May 26, 2026 at 07:56:14PM +0200, Paolo Bonzini wrote:
> Date: Tue, 26 May 2026 19:56:14 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH v3 15/19] scripts/qapi: generate high-level Rust bindings
> X-Mailer: git-send-email 2.54.0
>
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> Generate high-level native Rust declarations for the QAPI types.
>
> - char* is mapped to String, scalars to there corresponding Rust types
>
> - enums use #[repr(u32)] and can be transmuted to their C counterparts
>
> - has_foo/foo members are mapped to Option<T>
>
> - lists are represented as Vec<T>
>
> - structures map fields 1:1 to Rust
>
> - alternate are represented as Rust enum, each variant being a 1-element
> tuple
>
> - unions are represented in a similar way as in C: a struct S with a "u"
> member (since S may have extra 'base' fields). The discriminant
> isn't a member of S, since Rust enum already include it, but it can be
> recovered with "mystruct.u.into()"
>
> Anything that includes a recursive struct puts it in a Box. Lists are
> not considered recursive, because Vec breaks the recursion (it's possible
> to construct an object containing an empty Vec of its own type).
>
> Given the experimental nature of Rust, and the incompleteness of the
> backend (it lacks commands and events), QAPIRsBackend is not modular
> and is not built together with the C and trace-event files. It can
> be used by specifying "-B qapi.backend.QAPIRsBackend" on the qapi-gen
> command line.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20210907121943.3498701-21-marcandre.lureau@redhat.com
> [Paolo: rewrite conversion of leaf types]
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> meson.build | 4 +-
> scripts/qapi/backend.py | 25 +++
> scripts/qapi/common.py | 49 ++++++
> scripts/qapi/rs.py | 50 ++++++
> scripts/qapi/rs_types.py | 372 +++++++++++++++++++++++++++++++++++++++
> scripts/qapi/schema.py | 59 +++++--
> 6 files changed, 540 insertions(+), 19 deletions(-)
> create mode 100644 scripts/qapi/rs.py
> create mode 100644 scripts/qapi/rs_types.py
Revisiting the v2 discussion, I think maybe it's still possible to use
visit_module to identify predefined cases (in schema builtn module)
instead of is_predefined?
(I guess that's what Markus wanted in v2, hopefully... and apologies if
this is a bit too verbose. I'm learning QAPI code so I want to explain
my undersatnding in as much details as possible (to ensure "IIUC") :). )
> +class QAPISchemaGenRsTypeVisitor(QAPISchemaRsVisitor):
> + _schema: Optional[QAPISchema]
> +
> + def __init__(self, prefix: str) -> None:
> + super().__init__(prefix, 'qapi-types',
> + 'Schema-defined QAPI types', __doc__)
> +
> + def visit_begin(self, schema: QAPISchema) -> None:
> + self._schema = schema
> + objects_seen.add(schema.the_empty_object_type.name)
> +
> + self._gen.preamble_add(
> + mcgen('''
> +#![allow(unexpected_cfgs)]
> +#![allow(non_camel_case_types)]
> +#![allow(clippy::empty_structs_with_brackets)]
> +#![allow(clippy::large_enum_variant)]
> +#![allow(clippy::pub_underscore_fields)]
> +
> +// Because QAPI structs can contain float, for simplicity we never
> +// derive Eq. Clippy however would complain for those structs
> +// that *could* be Eq too.
> +#![allow(clippy::derive_partial_eq_without_eq)]
> +
> +use util::qobject::QObject;
> +'''))
IIUC, though there's only a single QAPIGenRs, but we can just use
visit_module to cache a flag and don't need to skip something like C
did:
def visit_module(self, name: str) -> None:
self._in_builtin_module = QAPISchemaModule.is_builtin_module(name)
builtin module is created by schema and is maintained in _module_dict
(in QAPISchema.__init__()). When QAPISchema iterates through _module_dict,
it will always visit he builtin module and once it enters builtin module,
visit_module() will be called (QAPISchemaModule.visit()).
With _in_builtin_module flag, then we just need to "proof" it has the
same effect as is_predefined() method.
> + def visit_object_type(self,
> + name: str,
> + info: Optional[QAPISourceInfo],
> + ifcond: QAPISchemaIfCond,
> + features: List[QAPISchemaFeature],
> + base: Optional[QAPISchemaObjectType],
> + members: List[QAPISchemaObjectTypeMember],
> + branches: Optional[QAPISchemaVariants]) -> None:
> + assert self._schema is not None
(if we use self._in_builtin_module, this assertion can go away.)
> + if self._schema.is_predefined(name) or name.startswith('q_'):
This is called by QAPISchemaObjectType not QAPISchemaBuiltinType, and
QType is QPAISchemaEnumType.
Only q_empty could hit is_predefined(), and q_empty has been included
into builtin module (in QAPISchema._def_predefineds()).
And a good thing is _def_predefineds() doesn't add other object type
into builtin module! So "self._schema.is_predefined(name)" is same as
"self._in_builtin_module"!
> + return
> + self._gen.add(gen_rs_object(name, ifcond, base, members, branches))
> +
> + def visit_enum_type(self,
> + name: str,
> + info: Optional[QAPISourceInfo],
> + ifcond: QAPISchemaIfCond,
> + features: List[QAPISchemaFeature],
> + members: List[QAPISchemaEnumMember],
> + prefix: Optional[str]) -> None:
> + assert self._schema is not None
(if we use self._in_builtin_module, this assertion can go away.
> + if self._schema.is_predefined(name):
> + return
Similiarly, only QType can hit is_predefined(), and builtin module has
only one enum: QType, therefore it's safe to use self._in_builtin_module
here, too.
> + self._gen.add(gen_rs_enum(name, ifcond, members))
>
> + def visit_alternate_type(self,
> + name: str,
> + info: Optional[QAPISourceInfo],
> + ifcond: QAPISchemaIfCond,
> + features: List[QAPISchemaFeature],
> + alternatives: QAPISchemaVariants) -> None:
I think maybe we also should ship builtin for alternatives type.
although for now there's no predefined alternatives...
> + self._gen.add(gen_rs_alternate(name, ifcond, alternatives))
> +
> +
... Overall, it seems checking builtin module is more robust and
porvides forward compatibility, and we don't need to add special case to
check.
Regards,
Zhao
next prev parent reply other threads:[~2026-06-04 9:44 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-26 17:55 [PATCH v3 00/19] rust: QObject and QAPI bindings Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 01/19] rust/qobject: add basic bindings Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 02/19] subprojects: add serde Paolo Bonzini
2026-06-01 14:47 ` Zhao Liu
2026-05-26 17:56 ` [PATCH v3 03/19] rust/qobject: add Serialize implementation Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 04/19] rust/qobject: add Serializer (to_qobject) implementation Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 05/19] rust/qobject: add Deserialize implementation Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 06/19] rust/qobject: add Deserializer (from_qobject) implementation Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 07/19] rust/qobject: add from/to JSON bindings for QObject Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 08/19] rust/qobject: add Display/Debug Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 09/19] scripts/qapi: reject empty enums Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 10/19] scripts/qapi: enum with conditional first item must be optional Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 11/19] scripts/qapi: add QAPISchemaIfCond.rsgen() Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 12/19] scripts/qapi: add QAPISchemaType.is_predefined Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 13/19] scripts/qapi: pull c_name and lstrip from camel_to_upper to caller Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 14/19] scripts/qapi: allow passing multiple segments to mcgen Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 15/19] scripts/qapi: generate high-level Rust bindings Paolo Bonzini
2026-05-26 21:03 ` Marc-André Lureau
2026-06-04 10:11 ` Zhao Liu [this message]
2026-05-26 17:56 ` [PATCH v3 16/19] scripts/rustc_args: add --no-strict-cfg Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 17/19] rust/util: build QAPI types Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 18/19] scripts/qapi: add serde attributes Paolo Bonzini
2026-05-26 17:56 ` [PATCH v3 19/19] rust/tests: QAPI integration tests 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=aiFPYDvtWdAXD1kk@intel.com \
--to=zhao1.liu@intel.com \
--cc=armbru@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=qemu-rust@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.