From 559833a0d465cf6c71f75a45115d07b415338cac Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Mon, 2 Jul 2018 22:59:31 -0400 Subject: [PATCH] qapi: allow empty branches in flat unions It often happens that just a few discriminator values imply extra data in a flat union. Existing checks did not make possible to leave other values uncovered. Such cases had to be worked around by either stating a dummy (empty) type or introducing another (subset) discriminator enumeration. Both options create redundant entities in qapi files for little profit. With this patch it is not necessary anymore to add designated union fields for every possible value of a discriminator enumeration. Backports commit 800877bb1639d38ffaebe312a37b61c66bb10c83 from qemu --- qemu/scripts/qapi/common.py | 15 ++++++++------- qemu/scripts/qapi/types.py | 2 ++ qemu/scripts/qapi/visit.py | 19 ++++++++++++++----- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/qemu/scripts/qapi/common.py b/qemu/scripts/qapi/common.py index 77e0be72..0e676f2a 100644 --- a/qemu/scripts/qapi/common.py +++ b/qemu/scripts/qapi/common.py @@ -791,13 +791,6 @@ def check_union(expr, info): "enum '%s'" % (key, enum_define['enum'])) - # If discriminator is user-defined, ensure all values are covered - if enum_define: - for value in enum_define['data']: - if value not in members.keys(): - raise QAPISemError(info, "Union '%s' data missing '%s' branch" - % (name, value)) - def check_alternate(expr, info): name = expr['alternate'] @@ -1367,6 +1360,14 @@ class QAPISchemaObjectTypeVariants(object): self.tag_member = seen[c_name(self._tag_name)] assert self._tag_name == self.tag_member.name assert isinstance(self.tag_member.type, QAPISchemaEnumType) + if self._tag_name: # flat union + # branches that are not explicitly covered get an empty type + cases = set([v.name for v in self.variants]) + for val in self.tag_member.type.values: + if val.name not in cases: + v = QAPISchemaObjectTypeVariant(val.name, 'q_empty') + v.set_owner(self.tag_member.owner) + self.variants.append(v) for v in self.variants: v.check(schema) # Union names must match enum values; alternate names are diff --git a/qemu/scripts/qapi/types.py b/qemu/scripts/qapi/types.py index 839cd23b..37824510 100644 --- a/qemu/scripts/qapi/types.py +++ b/qemu/scripts/qapi/types.py @@ -125,6 +125,8 @@ def gen_variants(variants): c_name=c_name(variants.tag_member.name)) for var in variants.variants: + if var.type.name == 'q_empty': + continue ret += mcgen(''' %(c_type)s %(c_name)s; ''', diff --git a/qemu/scripts/qapi/visit.py b/qemu/scripts/qapi/visit.py index 419a58bb..f267a7df 100644 --- a/qemu/scripts/qapi/visit.py +++ b/qemu/scripts/qapi/visit.py @@ -81,15 +81,24 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) c_name=c_name(variants.tag_member.name)) for var in variants.variants: - ret += mcgen(''' + case_str = c_enum_const(variants.tag_member.type.name, + var.name, + variants.tag_member.type.prefix) + if var.type.name == 'q_empty': + # valid variant and nothing to do + ret += mcgen(''' + case %(case)s: + break; +''', + case=case_str) + else: + ret += mcgen(''' case %(case)s: visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err); break; ''', - case=c_enum_const(variants.tag_member.type.name, - var.name, - variants.tag_member.type.prefix), - c_type=var.type.c_name(), c_name=c_name(var.name)) + case=case_str, + c_type=var.type.c_name(), c_name=c_name(var.name)) ret += mcgen(''' default: