diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index 4ce67752a752..ec2d3744836a 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -436,8 +436,7 @@ Union types are used to let the user choose between several different variants for an object. There are two flavors: simple (no discriminator or base), and flat (both discriminator and base). A union type is defined using a data dictionary as explained in the following -paragraphs. The data dictionary for either type of union must not -be empty. +paragraphs. Unions must have at least one branch. A simple union type defines a mapping from automatic discriminator values to data types like in this example: diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 99db18f3d6c5..3393a049cc7b 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -852,7 +852,7 @@ def check_union(expr, info): # With no discriminator it is a simple union. if discriminator is None: - enum_define = None + enum_values = members.keys() allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum'] if base is not None: raise QAPISemError(info, "Simple union '%s' must not have a base" % @@ -885,16 +885,17 @@ def check_union(expr, info): 'must not be conditional' % (base, discriminator, name)) enum_define = enum_types.get(discriminator_value['type']) - allow_metas = ['struct'] # Do not allow string discriminator if not enum_define: raise QAPISemError(info, "Discriminator '%s' must be of enumeration " "type" % discriminator) + enum_values = enum_get_names(enum_define) + allow_metas = ['struct'] + + if (len(enum_values) == 0): + raise QAPISemError(info, "Union '%s' has no branches" % name) - # Check every branch; don't allow an empty union - if len(members) == 0: - raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name) for (key, value) in members.items(): check_name(info, "Member of union '%s'" % name, key) @@ -907,8 +908,8 @@ def check_union(expr, info): # If the discriminator names an enum type, then all members # of 'data' must also be members of the enum type. - if enum_define: - if key not in enum_get_names(enum_define): + if discriminator is not None: + if key not in enum_values: raise QAPISemError(info, "Discriminator value '%s' is not found in " "enum '%s'" @@ -1578,7 +1579,6 @@ def __init__(self, tag_name, tag_member, variants): assert bool(tag_member) != bool(tag_name) assert (isinstance(tag_name, str) or isinstance(tag_member, QAPISchemaObjectTypeMember)) - assert len(variants) > 0 for v in variants: assert isinstance(v, QAPISchemaObjectTypeVariant) self._tag_name = tag_name diff --git a/tests/qapi-schema/flat-union-empty.err b/tests/qapi-schema/flat-union-empty.err index 15754f54eb92..fedbc0d1cfee 100644 --- a/tests/qapi-schema/flat-union-empty.err +++ b/tests/qapi-schema/flat-union-empty.err @@ -1 +1 @@ -tests/qapi-schema/flat-union-empty.json:4: Union 'Union' cannot have empty 'data' +tests/qapi-schema/flat-union-empty.json:4: Union 'Union' has no branches diff --git a/tests/qapi-schema/flat-union-empty.json b/tests/qapi-schema/flat-union-empty.json index 77f1d9abfb8f..83e1cc7b96be 100644 --- a/tests/qapi-schema/flat-union-empty.json +++ b/tests/qapi-schema/flat-union-empty.json @@ -1,4 +1,4 @@ -# flat unions cannot be empty +# flat union discriminator cannot be empty { 'enum': 'Empty', 'data': [ ] } { 'struct': 'Base', 'data': { 'type': 'Empty' } } { 'union': 'Union', 'base': 'Base', 'discriminator': 'type', 'data': { } } diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 8b0d47c4ab4e..75c42eb0e3e9 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -25,6 +25,11 @@ { 'struct': 'Empty1', 'data': { } } { 'struct': 'Empty2', 'base': 'Empty1', 'data': { } } +# Likewise for an empty flat union +{ 'union': 'Union', + 'base': { 'type': 'EnumOne' }, 'discriminator': 'type', + 'data': { } } + { 'command': 'user_def_cmd0', 'data': 'Empty2', 'returns': 'Empty2' } # for testing override of default naming heuristic diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index bea7976bbbbd..98031da96f0c 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -23,6 +23,15 @@ enum MyEnum object Empty1 object Empty2 base Empty1 +object q_obj_Union-base + member type: EnumOne optional=False +object Union + base q_obj_Union-base + tag type + case value1: q_empty + case value2: q_empty + case value3: q_empty + case value4: q_empty command user_def_cmd0 Empty2 -> Empty2 gen=True success_response=True boxed=False oob=False preconfig=False enum QEnumTwo diff --git a/tests/qapi-schema/union-empty.err b/tests/qapi-schema/union-empty.err index 12c20221bdfb..d4241a38a2c4 100644 --- a/tests/qapi-schema/union-empty.err +++ b/tests/qapi-schema/union-empty.err @@ -1 +1 @@ -tests/qapi-schema/union-empty.json:2: Union 'Union' cannot have empty 'data' +tests/qapi-schema/union-empty.json:2: Union 'Union' has no branches diff --git a/tests/qapi-schema/union-empty.json b/tests/qapi-schema/union-empty.json index 1f0b13ca21ae..df3e5e639a5e 100644 --- a/tests/qapi-schema/union-empty.json +++ b/tests/qapi-schema/union-empty.json @@ -1,2 +1,2 @@ -# unions cannot be empty +# simple unions cannot be empty { 'union': 'Union', 'data': { } }