@@ -496,9 +496,11 @@ Resulting in these JSON objects:
Notice that in a flat union, the discriminator name is controlled by
the user, but because it must map to a base member with enum type, the
-code generator can ensure that branches exist for all values of the
-enum (although the order of the keys need not match the declaration of
-the enum). In the resulting generated C data types, a flat union is
+code generator ensures that branches match the existing values of the
+enum. The order of the keys need not match the declaration of the enum.
+The keys need not cover all possible enum values. Omitted enum values
+are still valid branches that add no additional members to the data type.
+In the resulting generated C data types, a flat union is
represented as a struct with the base members included directly, and
then a union of structures for each branch of the struct.
deleted file mode 100644
@@ -1,9 +0,0 @@
-# we require all branches of the union to be covered
-{ 'enum': 'TestEnum',
- 'data': [ 'value1', 'value2' ] }
-{ 'struct': 'TestTypeA',
- 'data': { 'string': 'str' } }
-{ 'union': 'TestUnion',
- 'base': { 'type': 'TestEnum' },
- 'discriminator': 'type',
- 'data': { 'value1': 'TestTypeA' } }
@@ -39,7 +39,7 @@
'*enum1': 'EnumOne' } } # intentional forward reference
{ 'enum': 'EnumOne',
- 'data': [ 'value1', 'value2', 'value3' ] }
+ 'data': [ 'value1', 'value2', 'value3', 'value4' ] }
{ 'struct': 'UserDefZero',
'data': { 'integer': 'int' } }
@@ -76,7 +76,7 @@
'discriminator': 'enum1',
'data': { 'value1' : 'UserDefA',
'value2' : 'UserDefB',
- 'value3' : 'UserDefB' } }
+ 'value3' : 'UserDefB' } } # skip 'value4'
{ 'struct': 'UserDefUnionBase',
'base': 'UserDefZero',
@@ -782,13 +782,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']
@@ -1644,6 +1637,10 @@ class QAPISchema(object):
if tag_name:
variants = [self._make_variant(key, value)
for (key, value) in data.items()]
+ # branches that are not explicitly covered get an empty type
+ variants += [self._make_variant(key, 'q_empty')
+ for key in discriminator_find_enum_define(expr)['data']
+ if key not in data.keys()]
members = []
else:
variants = [self._make_simple_variant(key, value, info)
@@ -124,7 +124,9 @@ def gen_variants(variants):
''',
c_name=c_name(variants.tag_member.name))
- for var in variants.variants:
+ # filter out the empty types
+ for var in filter(lambda var: var.type.name != 'q_empty',
+ variants.variants):
ret += mcgen('''
%(c_type)s %(c_name)s;
''',
@@ -81,14 +81,23 @@ 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),
+ case=case_str,
c_type=var.type.c_name(), c_name=c_name(var.name))
ret += mcgen('''
@@ -494,7 +494,6 @@ qapi-schema += flat-union-base-any.json
qapi-schema += flat-union-base-union.json
qapi-schema += flat-union-clash-member.json
qapi-schema += flat-union-empty.json
-qapi-schema += flat-union-incomplete-branch.json
qapi-schema += flat-union-inline.json
qapi-schema += flat-union-int-branch.json
qapi-schema += flat-union-invalid-branch-key.json
deleted file mode 100644
@@ -1 +0,0 @@
-tests/qapi-schema/flat-union-incomplete-branch.json:6: Union 'TestUnion' data missing 'value2' branch
deleted file mode 100644
@@ -1 +0,0 @@
-1
deleted file mode 100644
@@ -23,7 +23,7 @@ object UserDefOne
base UserDefZero
member string: str optional=False
member enum1: EnumOne optional=True
-enum EnumOne ['value1', 'value2', 'value3']
+enum EnumOne ['value1', 'value2', 'value3', 'value4']
object UserDefZero
member integer: int optional=False
object UserDefTwoDictDict
@@ -52,6 +52,7 @@ object UserDefFlatUnion
case value1: UserDefA
case value2: UserDefB
case value3: UserDefB
+ case value4: q_empty
object UserDefUnionBase
base UserDefZero
member string: str optional=False
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. Signed-off-by: Anton Nefedov <anton.nefedov@virtuozzo.com> --- docs/devel/qapi-code-gen.txt | 8 +++++--- tests/qapi-schema/flat-union-incomplete-branch.json | 9 --------- tests/qapi-schema/qapi-schema-test.json | 4 ++-- scripts/qapi/common.py | 11 ++++------- scripts/qapi/types.py | 4 +++- scripts/qapi/visit.py | 17 +++++++++++++---- tests/Makefile.include | 1 - tests/qapi-schema/flat-union-incomplete-branch.err | 1 - tests/qapi-schema/flat-union-incomplete-branch.exit | 1 - tests/qapi-schema/flat-union-incomplete-branch.out | 0 tests/qapi-schema/qapi-schema-test.out | 3 ++- 11 files changed, 29 insertions(+), 30 deletions(-) delete mode 100644 tests/qapi-schema/flat-union-incomplete-branch.json delete mode 100644 tests/qapi-schema/flat-union-incomplete-branch.err delete mode 100644 tests/qapi-schema/flat-union-incomplete-branch.exit delete mode 100644 tests/qapi-schema/flat-union-incomplete-branch.out