From: "Daniel P. Berrangé" <berrange@redhat.com>
To: qemu-devel@nongnu.org
Cc: "Michael Roth" <michael.roth@amd.com>,
"Eric Blake" <eblake@redhat.com>,
"Markus Armbruster" <armbru@redhat.com>,
"Het Gala" <het.gala@nutanix.com>,
"Daniel P. Berrangé" <berrange@redhat.com>
Subject: [PATCH] qapi: allow unions to contain further unions
Date: Tue, 14 Feb 2023 18:44:04 +0000 [thread overview]
Message-ID: <20230214184404.1865237-1-berrange@redhat.com> (raw)
This extends the QAPI schema validation to permit unions inside unions,
provided the checks for clashing fields pass.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
This patch comes out of the discussion on Het's migration series
starting at this patch:
https://lists.gnu.org/archive/html/qemu-devel/2023-02/msg02111.html
Markus had described his desired improved architecture
https://lists.gnu.org/archive/html/qemu-devel/2023-02/msg02719.html
but I don't think I have enough knowledge of the QAPI code to attempt
to fuse the handling of structs/unions as mentioned. This patch does
what looks to be the bare minimum to permit unions in unions, while
keeping validation checks for clashing fields.
I've not tested beyond the unit tests, but if this is acceptable
from Markus' POV, I'd expect Het to insert this patch at the
start of his migration series and thus test it more fully.
scripts/qapi/schema.py | 6 +--
.../union-invalid-union-subfield.err | 2 +
.../union-invalid-union-subfield.json | 27 +++++++++++++
.../union-invalid-union-subfield.out | 0
.../union-invalid-union-subtype.err | 2 +
.../union-invalid-union-subtype.json | 26 +++++++++++++
.../union-invalid-union-subtype.out | 0
tests/qapi-schema/union-union-branch.err | 0
tests/qapi-schema/union-union-branch.json | 26 +++++++++++++
tests/qapi-schema/union-union-branch.out | 38 +++++++++++++++++++
10 files changed, 124 insertions(+), 3 deletions(-)
create mode 100644 tests/qapi-schema/union-invalid-union-subfield.err
create mode 100644 tests/qapi-schema/union-invalid-union-subfield.json
create mode 100644 tests/qapi-schema/union-invalid-union-subfield.out
create mode 100644 tests/qapi-schema/union-invalid-union-subtype.err
create mode 100644 tests/qapi-schema/union-invalid-union-subtype.json
create mode 100644 tests/qapi-schema/union-invalid-union-subtype.out
create mode 100644 tests/qapi-schema/union-union-branch.err
create mode 100644 tests/qapi-schema/union-union-branch.json
create mode 100644 tests/qapi-schema/union-union-branch.out
diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index cd8661125c..062c6bbb00 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -465,9 +465,10 @@ def check(self, schema):
# on behalf of info, which is not necessarily self.info
def check_clash(self, info, seen):
assert self._checked
- assert not self.variants # not implemented
for m in self.members:
m.check_clash(info, seen)
+ if self.variants:
+ self.variants.check_clash(info, seen)
def connect_doc(self, doc=None):
super().connect_doc(doc)
@@ -652,8 +653,7 @@ def check(self, schema, seen):
self.info,
"branch '%s' is not a value of %s"
% (v.name, self.tag_member.type.describe()))
- if (not isinstance(v.type, QAPISchemaObjectType)
- or v.type.variants):
+ if not isinstance(v.type, QAPISchemaObjectType):
raise QAPISemError(
self.info,
"%s cannot use %s"
diff --git a/tests/qapi-schema/union-invalid-union-subfield.err b/tests/qapi-schema/union-invalid-union-subfield.err
new file mode 100644
index 0000000000..d3a2e31aff
--- /dev/null
+++ b/tests/qapi-schema/union-invalid-union-subfield.err
@@ -0,0 +1,2 @@
+union-invalid-union-subfield.json: In union 'TestUnion':
+union-invalid-union-subfield.json:22: member 'teeth' of type 'TestTypeFish' collides with base member 'teeth'
diff --git a/tests/qapi-schema/union-invalid-union-subfield.json b/tests/qapi-schema/union-invalid-union-subfield.json
new file mode 100644
index 0000000000..235f76d7da
--- /dev/null
+++ b/tests/qapi-schema/union-invalid-union-subfield.json
@@ -0,0 +1,27 @@
+{ 'enum': 'TestEnum',
+ 'data': [ 'animals', 'plants' ] }
+
+{ 'enum': 'TestAnimals',
+ 'data': [ 'fish', 'birds'] }
+
+{ 'struct': 'TestTypeFish',
+ 'data': { 'scales': 'int', 'teeth': 'int' } }
+
+{ 'struct': 'TestTypeBirds',
+ 'data': { 'feathers': 'int' } }
+
+{ 'union': 'TestTypeAnimals',
+ 'base': { 'atype': 'TestAnimals' },
+ 'discriminator': 'atype',
+ 'data': { 'fish': 'TestTypeFish',
+ 'birds': 'TestTypeBirds' } }
+
+{ 'struct': 'TestTypePlants',
+ 'data': { 'integer': 'int' } }
+
+{ 'union': 'TestUnion',
+ 'base': { 'type': 'TestEnum',
+ 'teeth': 'int' },
+ 'discriminator': 'type',
+ 'data': { 'animals': 'TestTypeAnimals',
+ 'plants': 'TestTypePlants' } }
diff --git a/tests/qapi-schema/union-invalid-union-subfield.out b/tests/qapi-schema/union-invalid-union-subfield.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/union-invalid-union-subtype.err b/tests/qapi-schema/union-invalid-union-subtype.err
new file mode 100644
index 0000000000..7b8679c08f
--- /dev/null
+++ b/tests/qapi-schema/union-invalid-union-subtype.err
@@ -0,0 +1,2 @@
+union-invalid-union-subtype.json: In union 'TestUnion':
+union-invalid-union-subtype.json:22: base member 'type' collides with base member 'type'
diff --git a/tests/qapi-schema/union-invalid-union-subtype.json b/tests/qapi-schema/union-invalid-union-subtype.json
new file mode 100644
index 0000000000..59ca4b0385
--- /dev/null
+++ b/tests/qapi-schema/union-invalid-union-subtype.json
@@ -0,0 +1,26 @@
+{ 'enum': 'TestEnum',
+ 'data': [ 'value-a', 'value-b' ] }
+
+{ 'enum': 'TestEnumA',
+ 'data': [ 'value-a1', 'value-a2' ] }
+
+{ 'struct': 'TestTypeA1',
+ 'data': { 'integer': 'int' } }
+
+{ 'struct': 'TestTypeA2',
+ 'data': { 'integer': 'int' } }
+
+{ 'union': 'TestTypeA',
+ 'base': { 'type': 'TestEnumA' },
+ 'discriminator': 'type',
+ 'data': { 'value-a1': 'TestTypeA1',
+ 'value-a2': 'TestTypeA2' } }
+
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+
+{ 'union': 'TestUnion',
+ 'base': { 'type': 'TestEnum' },
+ 'discriminator': 'type',
+ 'data': { 'value-a': 'TestTypeA',
+ 'value-b': 'TestTypeB' } }
diff --git a/tests/qapi-schema/union-invalid-union-subtype.out b/tests/qapi-schema/union-invalid-union-subtype.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/union-union-branch.err b/tests/qapi-schema/union-union-branch.err
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/union-union-branch.json b/tests/qapi-schema/union-union-branch.json
new file mode 100644
index 0000000000..d3d7ce57c6
--- /dev/null
+++ b/tests/qapi-schema/union-union-branch.json
@@ -0,0 +1,26 @@
+{ 'enum': 'TestEnum',
+ 'data': [ 'value-a', 'value-b' ] }
+
+{ 'enum': 'TestEnumA',
+ 'data': [ 'value-a1', 'value-a2' ] }
+
+{ 'struct': 'TestTypeA1',
+ 'data': { 'integer': 'int' } }
+
+{ 'struct': 'TestTypeA2',
+ 'data': { 'integer': 'int' } }
+
+{ 'union': 'TestTypeA',
+ 'base': { 'type-a': 'TestEnumA' },
+ 'discriminator': 'type-a',
+ 'data': { 'value-a1': 'TestTypeA1',
+ 'value-a2': 'TestTypeA2' } }
+
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+
+{ 'union': 'TestUnion',
+ 'base': { 'type': 'TestEnum' },
+ 'discriminator': 'type',
+ 'data': { 'value-a': 'TestTypeA',
+ 'value-b': 'TestTypeB' } }
diff --git a/tests/qapi-schema/union-union-branch.out b/tests/qapi-schema/union-union-branch.out
new file mode 100644
index 0000000000..d0c37495c2
--- /dev/null
+++ b/tests/qapi-schema/union-union-branch.out
@@ -0,0 +1,38 @@
+module ./builtin
+object q_empty
+enum QType
+ prefix QTYPE
+ member none
+ member qnull
+ member qnum
+ member qstring
+ member qdict
+ member qlist
+ member qbool
+module union-union-branch.json
+enum TestEnum
+ member value-a
+ member value-b
+enum TestEnumA
+ member value-a1
+ member value-a2
+object TestTypeA1
+ member integer: int optional=False
+object TestTypeA2
+ member integer: int optional=False
+object q_obj_TestTypeA-base
+ member type-a: TestEnumA optional=False
+object TestTypeA
+ base q_obj_TestTypeA-base
+ tag type-a
+ case value-a1: TestTypeA1
+ case value-a2: TestTypeA2
+object TestTypeB
+ member integer: int optional=False
+object q_obj_TestUnion-base
+ member type: TestEnum optional=False
+object TestUnion
+ base q_obj_TestUnion-base
+ tag type
+ case value-a: TestTypeA
+ case value-b: TestTypeB
--
2.39.1
next reply other threads:[~2023-02-14 18:44 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-02-14 18:44 Daniel P. Berrangé [this message]
2023-02-23 7:24 ` [PATCH] qapi: allow unions to contain further unions Markus Armbruster
2023-02-23 13:40 ` Daniel P. Berrangé
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=20230214184404.1865237-1-berrange@redhat.com \
--to=berrange@redhat.com \
--cc=armbru@redhat.com \
--cc=eblake@redhat.com \
--cc=het.gala@nutanix.com \
--cc=michael.roth@amd.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).