Skip to content

Commit

Permalink
qapi: allow empty branches in flat unions
Browse files Browse the repository at this point in the history
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>
Message-Id: <1529311206-76847-2-git-send-email-anton.nefedov@virtuozzo.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
  • Loading branch information
xantnef authored and Markus Armbruster committed Jun 22, 2018
1 parent fe170d8 commit 800877b
Show file tree
Hide file tree
Showing 11 changed files with 35 additions and 30 deletions.
8 changes: 5 additions & 3 deletions docs/devel/qapi-code-gen.txt
Expand Up @@ -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.

Expand Down
15 changes: 8 additions & 7 deletions scripts/qapi/common.py
Expand Up @@ -779,13 +779,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']
Expand Down Expand Up @@ -1357,6 +1350,14 @@ def check(self, schema, seen):
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
Expand Down
2 changes: 2 additions & 0 deletions scripts/qapi/types.py
Expand Up @@ -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;
''',
Expand Down
19 changes: 14 additions & 5 deletions scripts/qapi/visit.py
Expand Up @@ -81,15 +81,24 @@ def gen_visit_object_members(name, base, members, variants):
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:
Expand Down
1 change: 0 additions & 1 deletion tests/Makefile.include
Expand Up @@ -499,7 +499,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
Expand Down
1 change: 0 additions & 1 deletion tests/qapi-schema/flat-union-incomplete-branch.err

This file was deleted.

1 change: 0 additions & 1 deletion tests/qapi-schema/flat-union-incomplete-branch.exit

This file was deleted.

9 changes: 0 additions & 9 deletions tests/qapi-schema/flat-union-incomplete-branch.json

This file was deleted.

Empty file.
6 changes: 4 additions & 2 deletions tests/qapi-schema/qapi-schema-test.json
Expand Up @@ -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' } }
Expand Down Expand Up @@ -76,7 +76,9 @@
'discriminator': 'enum1',
'data': { 'value1' : 'UserDefA',
'value2' : 'UserDefB',
'value3' : 'UserDefB' } }
'value3' : 'UserDefB'
# 'value4' defaults to empty
} }

{ 'struct': 'UserDefUnionBase',
'base': 'UserDefZero',
Expand Down
3 changes: 2 additions & 1 deletion tests/qapi-schema/qapi-schema-test.out
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 800877b

Please sign in to comment.