qapi: Drop simple unions
Simple unions predate flat unions. Having both complicates the QAPI schema language and the QAPI generator. We haven't been using simple unions in new code for a long time, because they are less flexible and somewhat awkward on the wire. The previous commits eliminated simple union from the tree. Now drop them from the QAPI schema language entirely, and update mentions of "flat union" to just "union". Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <20210917143134.412106-22-armbru@redhat.com>
This commit is contained in:
parent
76432d988b
commit
4e99f4b12c
@ -319,13 +319,9 @@ Union types
|
|||||||
Syntax::
|
Syntax::
|
||||||
|
|
||||||
UNION = { 'union': STRING,
|
UNION = { 'union': STRING,
|
||||||
'data': BRANCHES,
|
|
||||||
'*if': COND,
|
|
||||||
'*features': FEATURES }
|
|
||||||
| { 'union': STRING,
|
|
||||||
'data': BRANCHES,
|
|
||||||
'base': ( MEMBERS | STRING ),
|
'base': ( MEMBERS | STRING ),
|
||||||
'discriminator': STRING,
|
'discriminator': STRING,
|
||||||
|
'data': BRANCHES,
|
||||||
'*if': COND,
|
'*if': COND,
|
||||||
'*features': FEATURES }
|
'*features': FEATURES }
|
||||||
BRANCHES = { BRANCH, ... }
|
BRANCHES = { BRANCH, ... }
|
||||||
@ -334,63 +330,30 @@ Syntax::
|
|||||||
|
|
||||||
Member 'union' names the union type.
|
Member 'union' names the union type.
|
||||||
|
|
||||||
There are two flavors of union types: simple (no discriminator or
|
|
||||||
base), and flat (both discriminator and base).
|
|
||||||
|
|
||||||
Each BRANCH of the 'data' object defines a branch of the union. A
|
|
||||||
union must have at least one branch.
|
|
||||||
|
|
||||||
The BRANCH's STRING name is the branch name.
|
|
||||||
|
|
||||||
The BRANCH's value defines the branch's properties, in particular its
|
|
||||||
type. The form TYPE-REF_ is shorthand for :code:`{ 'type': TYPE-REF }`.
|
|
||||||
|
|
||||||
A simple union type defines a mapping from automatic discriminator
|
|
||||||
values to data types like in this example::
|
|
||||||
|
|
||||||
{ 'struct': 'BlockdevOptionsFile', 'data': { 'filename': 'str' } }
|
|
||||||
{ 'struct': 'BlockdevOptionsQcow2',
|
|
||||||
'data': { 'backing': 'str', '*lazy-refcounts': 'bool' } }
|
|
||||||
|
|
||||||
{ 'union': 'BlockdevOptionsSimple',
|
|
||||||
'data': { 'file': 'BlockdevOptionsFile',
|
|
||||||
'qcow2': 'BlockdevOptionsQcow2' } }
|
|
||||||
|
|
||||||
In the Client JSON Protocol, a simple union is represented by an
|
|
||||||
object that contains the 'type' member as a discriminator, and a
|
|
||||||
'data' member that is of the specified data type corresponding to the
|
|
||||||
discriminator value, as in these examples::
|
|
||||||
|
|
||||||
{ "type": "file", "data": { "filename": "/some/place/my-image" } }
|
|
||||||
{ "type": "qcow2", "data": { "backing": "/some/place/my-image",
|
|
||||||
"lazy-refcounts": true } }
|
|
||||||
|
|
||||||
The generated C code uses a struct containing a union. Additionally,
|
|
||||||
an implicit C enum 'NameKind' is created, corresponding to the union
|
|
||||||
'Name', for accessing the various branches of the union. The value
|
|
||||||
for each branch can be of any type.
|
|
||||||
|
|
||||||
Flat unions permit arbitrary common members that occur in all variants
|
|
||||||
of the union, not just a discriminator. Their discriminators need not
|
|
||||||
be named 'type'. They also avoid nesting on the wire.
|
|
||||||
|
|
||||||
The 'base' member defines the common members. If it is a MEMBERS_
|
The 'base' member defines the common members. If it is a MEMBERS_
|
||||||
object, it defines common members just like a struct type's 'data'
|
object, it defines common members just like a struct type's 'data'
|
||||||
member defines struct type members. If it is a STRING, it names a
|
member defines struct type members. If it is a STRING, it names a
|
||||||
struct type whose members are the common members.
|
struct type whose members are the common members.
|
||||||
|
|
||||||
All flat union branches must be `Struct types`_.
|
Member 'discriminator' must name a non-optional enum-typed member of
|
||||||
|
the base struct. That member's value selects a branch by its name.
|
||||||
|
If no such branch exists, an empty branch is assumed.
|
||||||
|
|
||||||
In the Client JSON Protocol, a flat union is represented by an object
|
Each BRANCH of the 'data' object defines a branch of the union. A
|
||||||
with the common members (from the base type) and the selected branch's
|
union must have at least one branch.
|
||||||
members. The two sets of member names must be disjoint. Member
|
|
||||||
'discriminator' must name a non-optional enum-typed member of the base
|
|
||||||
struct.
|
|
||||||
|
|
||||||
The following example enhances the above simple union example by
|
The BRANCH's STRING name is the branch name. It must be a value of
|
||||||
adding an optional common member 'read-only', renaming the
|
the discriminator enum type.
|
||||||
discriminator to something more applicable than the simple union's
|
|
||||||
default of 'type', and reducing the number of ``{}`` required on the wire::
|
The BRANCH's value defines the branch's properties, in particular its
|
||||||
|
type. The type must a struct type. The form TYPE-REF_ is shorthand
|
||||||
|
for :code:`{ 'type': TYPE-REF }`.
|
||||||
|
|
||||||
|
In the Client JSON Protocol, a union is represented by an object with
|
||||||
|
the common members (from the base type) and the selected branch's
|
||||||
|
members. The two sets of member names must be disjoint.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
{ 'enum': 'BlockdevDriver', 'data': [ 'file', 'qcow2' ] }
|
{ 'enum': 'BlockdevDriver', 'data': [ 'file', 'qcow2' ] }
|
||||||
{ 'union': 'BlockdevOptions',
|
{ 'union': 'BlockdevOptions',
|
||||||
@ -406,30 +369,11 @@ Resulting in these JSON objects::
|
|||||||
{ "driver": "qcow2", "read-only": false,
|
{ "driver": "qcow2", "read-only": false,
|
||||||
"backing": "/some/place/my-image", "lazy-refcounts": true }
|
"backing": "/some/place/my-image", "lazy-refcounts": true }
|
||||||
|
|
||||||
Notice that in a flat union, the discriminator name is controlled by
|
The order of branches need not match the order of the enum values.
|
||||||
the user, but because it must map to a base member with enum type, the
|
The branches need not cover all possible enum values. In the
|
||||||
code generator ensures that branches match the existing values of the
|
resulting generated C data types, a union is represented as a struct
|
||||||
enum. The order of branches need not match the order of the enum
|
with the base members in QAPI schema order, and then a union of
|
||||||
values. The branches need not cover all possible enum values.
|
structures for each branch of the struct.
|
||||||
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 in QAPI
|
|
||||||
schema order, and then a union of structures for each branch of the
|
|
||||||
struct.
|
|
||||||
|
|
||||||
A simple union can always be re-written as a flat union where the base
|
|
||||||
class has a single member named 'type', and where each branch of the
|
|
||||||
union has a struct with a single member named 'data'. That is, ::
|
|
||||||
|
|
||||||
{ 'union': 'Simple', 'data': { 'one': 'str', 'two': 'int' } }
|
|
||||||
|
|
||||||
is identical on the wire to::
|
|
||||||
|
|
||||||
{ 'enum': 'Enum', 'data': ['one', 'two'] }
|
|
||||||
{ 'struct': 'Branch1', 'data': { 'data': 'str' } }
|
|
||||||
{ 'struct': 'Branch2', 'data': { 'data': 'int' } }
|
|
||||||
{ 'union': 'Flat', 'base': { 'type': 'Enum' }, 'discriminator': 'type',
|
|
||||||
'data': { 'one': 'Branch1', 'two': 'Branch2' } }
|
|
||||||
|
|
||||||
The optional 'if' member specifies a conditional. See `Configuring
|
The optional 'if' member specifies a conditional. See `Configuring
|
||||||
the schema`_ below for more on this.
|
the schema`_ below for more on this.
|
||||||
@ -1246,7 +1190,7 @@ that provides the variant members for this type tag value). The
|
|||||||
"variants" array is in no particular order, and is not guaranteed to
|
"variants" array is in no particular order, and is not guaranteed to
|
||||||
list cases in the same order as the corresponding "tag" enum type.
|
list cases in the same order as the corresponding "tag" enum type.
|
||||||
|
|
||||||
Example: the SchemaInfo for flat union BlockdevOptions from section
|
Example: the SchemaInfo for union BlockdevOptions from section
|
||||||
`Union types`_ ::
|
`Union types`_ ::
|
||||||
|
|
||||||
{ "name": "BlockdevOptions", "meta-type": "object",
|
{ "name": "BlockdevOptions", "meta-type": "object",
|
||||||
@ -1261,27 +1205,6 @@ Example: the SchemaInfo for flat union BlockdevOptions from section
|
|||||||
Note that base types are "flattened": its members are included in the
|
Note that base types are "flattened": its members are included in the
|
||||||
"members" array.
|
"members" array.
|
||||||
|
|
||||||
A simple union implicitly defines an enumeration type for its implicit
|
|
||||||
discriminator (called "type" on the wire, see section `Union types`_).
|
|
||||||
|
|
||||||
A simple union implicitly defines an object type for each of its
|
|
||||||
variants.
|
|
||||||
|
|
||||||
Example: the SchemaInfo for simple union BlockdevOptionsSimple from section
|
|
||||||
`Union types`_ ::
|
|
||||||
|
|
||||||
{ "name": "BlockdevOptionsSimple", "meta-type": "object",
|
|
||||||
"members": [
|
|
||||||
{ "name": "type", "type": "BlockdevOptionsSimpleKind" } ],
|
|
||||||
"tag": "type",
|
|
||||||
"variants": [
|
|
||||||
{ "case": "file", "type": "q_obj-BlockdevOptionsFile-wrapper" },
|
|
||||||
{ "case": "qcow2", "type": "q_obj-BlockdevOptionsQcow2-wrapper" } ] }
|
|
||||||
|
|
||||||
Enumeration type "BlockdevOptionsSimpleKind" and the object types
|
|
||||||
"q_obj-BlockdevOptionsFile-wrapper", "q_obj-BlockdevOptionsQcow2-wrapper"
|
|
||||||
are implicitly defined.
|
|
||||||
|
|
||||||
The SchemaInfo for an alternate type has meta-type "alternate", and
|
The SchemaInfo for an alternate type has meta-type "alternate", and
|
||||||
variant member "members". "members" is a JSON array. Each element is
|
variant member "members". "members" is a JSON array. Each element is
|
||||||
a JSON object with member "type", which names a type. Values of the
|
a JSON object with member "type", which names a type. Values of the
|
||||||
|
@ -513,17 +513,11 @@ def check_union(expr: _JSONObject, info: QAPISourceInfo) -> None:
|
|||||||
:return: None, ``expr`` is normalized in-place as needed.
|
:return: None, ``expr`` is normalized in-place as needed.
|
||||||
"""
|
"""
|
||||||
name = cast(str, expr['union']) # Checked in check_exprs
|
name = cast(str, expr['union']) # Checked in check_exprs
|
||||||
base = expr.get('base')
|
base = expr['base']
|
||||||
discriminator = expr.get('discriminator')
|
discriminator = expr['discriminator']
|
||||||
members = expr['data']
|
members = expr['data']
|
||||||
|
|
||||||
if discriminator is None: # simple union
|
|
||||||
if base is not None:
|
|
||||||
raise QAPISemError(info, "'base' requires 'discriminator'")
|
|
||||||
else: # flat union
|
|
||||||
check_type(base, info, "'base'", allow_dict=name)
|
check_type(base, info, "'base'", allow_dict=name)
|
||||||
if not base:
|
|
||||||
raise QAPISemError(info, "'discriminator' requires 'base'")
|
|
||||||
check_name_is_str(discriminator, info, "'discriminator'")
|
check_name_is_str(discriminator, info, "'discriminator'")
|
||||||
|
|
||||||
if not isinstance(members, dict):
|
if not isinstance(members, dict):
|
||||||
@ -531,9 +525,6 @@ def check_union(expr: _JSONObject, info: QAPISourceInfo) -> None:
|
|||||||
|
|
||||||
for (key, value) in members.items():
|
for (key, value) in members.items():
|
||||||
source = "'data' member '%s'" % key
|
source = "'data' member '%s'" % key
|
||||||
if discriminator is None:
|
|
||||||
check_name_lower(key, info, source)
|
|
||||||
# else: name is in discriminator enum, which gets checked
|
|
||||||
check_keys(value, info, source, ['type'], ['if'])
|
check_keys(value, info, source, ['type'], ['if'])
|
||||||
check_if(value, info, source)
|
check_if(value, info, source)
|
||||||
check_type(value['type'], info, source, allow_array=not base)
|
check_type(value['type'], info, source, allow_array=not base)
|
||||||
@ -664,8 +655,8 @@ def check_exprs(exprs: List[_JSONObject]) -> List[_JSONObject]:
|
|||||||
check_enum(expr, info)
|
check_enum(expr, info)
|
||||||
elif meta == 'union':
|
elif meta == 'union':
|
||||||
check_keys(expr, info, meta,
|
check_keys(expr, info, meta,
|
||||||
['union', 'data'],
|
['union', 'base', 'discriminator', 'data'],
|
||||||
['base', 'discriminator', 'if', 'features'])
|
['if', 'features'])
|
||||||
normalize_members(expr.get('base'))
|
normalize_members(expr.get('base'))
|
||||||
normalize_members(expr['data'])
|
normalize_members(expr['data'])
|
||||||
check_union(expr, info)
|
check_union(expr, info)
|
||||||
|
@ -321,8 +321,8 @@ class QAPISchemaEnumType(QAPISchemaType):
|
|||||||
m.connect_doc(doc)
|
m.connect_doc(doc)
|
||||||
|
|
||||||
def is_implicit(self):
|
def is_implicit(self):
|
||||||
# See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
|
# See QAPISchema._def_predefineds()
|
||||||
return self.name.endswith('Kind') or self.name == 'QType'
|
return self.name == 'QType'
|
||||||
|
|
||||||
def c_type(self):
|
def c_type(self):
|
||||||
return c_name(self.name)
|
return c_name(self.name)
|
||||||
@ -393,8 +393,7 @@ class QAPISchemaObjectType(QAPISchemaType):
|
|||||||
def __init__(self, name, info, doc, ifcond, features,
|
def __init__(self, name, info, doc, ifcond, features,
|
||||||
base, local_members, variants):
|
base, local_members, variants):
|
||||||
# struct has local_members, optional base, and no variants
|
# struct has local_members, optional base, and no variants
|
||||||
# flat union has base, variants, and no local_members
|
# union has base, variants, and no local_members
|
||||||
# simple union has local_members, variants, and no base
|
|
||||||
super().__init__(name, info, doc, ifcond, features)
|
super().__init__(name, info, doc, ifcond, features)
|
||||||
self.meta = 'union' if variants else 'struct'
|
self.meta = 'union' if variants else 'struct'
|
||||||
assert base is None or isinstance(base, str)
|
assert base is None or isinstance(base, str)
|
||||||
@ -465,15 +464,6 @@ class QAPISchemaObjectType(QAPISchemaType):
|
|||||||
for m in self.local_members:
|
for m in self.local_members:
|
||||||
m.connect_doc(doc)
|
m.connect_doc(doc)
|
||||||
|
|
||||||
@property
|
|
||||||
def ifcond(self):
|
|
||||||
assert self._checked
|
|
||||||
if isinstance(self._ifcond, QAPISchemaType):
|
|
||||||
# Simple union wrapper type inherits from wrapped type;
|
|
||||||
# see _make_implicit_object_type()
|
|
||||||
return self._ifcond.ifcond
|
|
||||||
return self._ifcond
|
|
||||||
|
|
||||||
def is_implicit(self):
|
def is_implicit(self):
|
||||||
# See QAPISchema._make_implicit_object_type(), as well as
|
# See QAPISchema._make_implicit_object_type(), as well as
|
||||||
# _def_predefineds()
|
# _def_predefineds()
|
||||||
@ -576,10 +566,9 @@ class QAPISchemaAlternateType(QAPISchemaType):
|
|||||||
|
|
||||||
class QAPISchemaVariants:
|
class QAPISchemaVariants:
|
||||||
def __init__(self, tag_name, info, tag_member, variants):
|
def __init__(self, tag_name, info, tag_member, variants):
|
||||||
# Flat unions pass tag_name but not tag_member.
|
# Unions pass tag_name but not tag_member.
|
||||||
# Simple unions and alternates pass tag_member but not tag_name.
|
# Alternates pass tag_member but not tag_name.
|
||||||
# After check(), tag_member is always set, and tag_name remains
|
# After check(), tag_member is always set.
|
||||||
# a reliable witness of being used by a flat union.
|
|
||||||
assert bool(tag_member) != bool(tag_name)
|
assert bool(tag_member) != bool(tag_name)
|
||||||
assert (isinstance(tag_name, str) or
|
assert (isinstance(tag_name, str) or
|
||||||
isinstance(tag_member, QAPISchemaObjectTypeMember))
|
isinstance(tag_member, QAPISchemaObjectTypeMember))
|
||||||
@ -595,7 +584,7 @@ class QAPISchemaVariants:
|
|||||||
v.set_defined_in(name)
|
v.set_defined_in(name)
|
||||||
|
|
||||||
def check(self, schema, seen):
|
def check(self, schema, seen):
|
||||||
if not self.tag_member: # flat union
|
if self._tag_name: # union
|
||||||
self.tag_member = seen.get(c_name(self._tag_name))
|
self.tag_member = seen.get(c_name(self._tag_name))
|
||||||
base = "'base'"
|
base = "'base'"
|
||||||
# Pointing to the base type when not implicit would be
|
# Pointing to the base type when not implicit would be
|
||||||
@ -625,11 +614,11 @@ class QAPISchemaVariants:
|
|||||||
self.info,
|
self.info,
|
||||||
"discriminator member '%s' of %s must not be conditional"
|
"discriminator member '%s' of %s must not be conditional"
|
||||||
% (self._tag_name, base))
|
% (self._tag_name, base))
|
||||||
else: # simple union
|
else: # alternate
|
||||||
assert isinstance(self.tag_member.type, QAPISchemaEnumType)
|
assert isinstance(self.tag_member.type, QAPISchemaEnumType)
|
||||||
assert not self.tag_member.optional
|
assert not self.tag_member.optional
|
||||||
assert not self.tag_member.ifcond.is_present()
|
assert not self.tag_member.ifcond.is_present()
|
||||||
if self._tag_name: # flat union
|
if self._tag_name: # union
|
||||||
# branches that are not explicitly covered get an empty type
|
# branches that are not explicitly covered get an empty type
|
||||||
cases = {v.name for v in self.variants}
|
cases = {v.name for v in self.variants}
|
||||||
for m in self.tag_member.type.members:
|
for m in self.tag_member.type.members:
|
||||||
@ -707,18 +696,10 @@ class QAPISchemaMember:
|
|||||||
assert role == 'member'
|
assert role == 'member'
|
||||||
role = 'parameter'
|
role = 'parameter'
|
||||||
elif defined_in.endswith('-base'):
|
elif defined_in.endswith('-base'):
|
||||||
# Implicit type created for a flat union's dict 'base'
|
# Implicit type created for a union's dict 'base'
|
||||||
role = 'base ' + role
|
role = 'base ' + role
|
||||||
else:
|
else:
|
||||||
# Implicit type created for a simple union's branch
|
|
||||||
assert defined_in.endswith('-wrapper')
|
|
||||||
# Unreachable and not implemented
|
|
||||||
assert False
|
assert False
|
||||||
elif defined_in.endswith('Kind'):
|
|
||||||
# See QAPISchema._make_implicit_enum_type()
|
|
||||||
# Implicit enum created for simple union's branches
|
|
||||||
assert role == 'value'
|
|
||||||
role = 'branch'
|
|
||||||
elif defined_in != info.defn_name:
|
elif defined_in != info.defn_name:
|
||||||
return "%s '%s' of type '%s'" % (role, self.name, defined_in)
|
return "%s '%s' of type '%s'" % (role, self.name, defined_in)
|
||||||
return "%s '%s'" % (role, self.name)
|
return "%s '%s'" % (role, self.name)
|
||||||
@ -1004,15 +985,6 @@ class QAPISchema:
|
|||||||
QAPISchemaIfCond(v.get('if')))
|
QAPISchemaIfCond(v.get('if')))
|
||||||
for v in values]
|
for v in values]
|
||||||
|
|
||||||
def _make_implicit_enum_type(self, name, info, ifcond, values):
|
|
||||||
# See also QAPISchemaObjectTypeMember.describe()
|
|
||||||
name = name + 'Kind' # reserved by check_defn_name_str()
|
|
||||||
self._def_entity(QAPISchemaEnumType(
|
|
||||||
name, info, None, ifcond, None,
|
|
||||||
self._make_enum_members(values, info),
|
|
||||||
None))
|
|
||||||
return name
|
|
||||||
|
|
||||||
def _make_array_type(self, element_type, info):
|
def _make_array_type(self, element_type, info):
|
||||||
name = element_type + 'List' # reserved by check_defn_name_str()
|
name = element_type + 'List' # reserved by check_defn_name_str()
|
||||||
if not self.lookup_type(name):
|
if not self.lookup_type(name):
|
||||||
@ -1026,17 +998,9 @@ class QAPISchema:
|
|||||||
name = 'q_obj_%s-%s' % (name, role)
|
name = 'q_obj_%s-%s' % (name, role)
|
||||||
typ = self.lookup_entity(name, QAPISchemaObjectType)
|
typ = self.lookup_entity(name, QAPISchemaObjectType)
|
||||||
if typ:
|
if typ:
|
||||||
# The implicit object type has multiple users. This is
|
# The implicit object type has multiple users. This can
|
||||||
# either a duplicate definition (which will be flagged
|
# only be a duplicate definition, which will be flagged
|
||||||
# later), or an implicit wrapper type used for multiple
|
# later.
|
||||||
# simple unions. In the latter case, ifcond should be the
|
|
||||||
# disjunction of its user's ifconds. Not implemented.
|
|
||||||
# Instead, we always pass the wrapped type's ifcond, which
|
|
||||||
# is trivially the same for all users. It's also
|
|
||||||
# necessary for the wrapper to compile. But it's not
|
|
||||||
# tight: the disjunction need not imply it. We may end up
|
|
||||||
# compiling useless wrapper types.
|
|
||||||
# TODO kill simple unions or implement the disjunction
|
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self._def_entity(QAPISchemaObjectType(
|
self._def_entity(QAPISchemaObjectType(
|
||||||
@ -1084,49 +1048,28 @@ class QAPISchema:
|
|||||||
def _make_variant(self, case, typ, ifcond, info):
|
def _make_variant(self, case, typ, ifcond, info):
|
||||||
return QAPISchemaVariant(case, info, typ, ifcond)
|
return QAPISchemaVariant(case, info, typ, ifcond)
|
||||||
|
|
||||||
def _make_simple_variant(self, case, typ, ifcond, info):
|
|
||||||
if isinstance(typ, list):
|
|
||||||
assert len(typ) == 1
|
|
||||||
typ = self._make_array_type(typ[0], info)
|
|
||||||
typ = self._make_implicit_object_type(
|
|
||||||
typ, info, self.lookup_type(typ),
|
|
||||||
'wrapper', [self._make_member('data', typ, None, None, info)])
|
|
||||||
return QAPISchemaVariant(case, info, typ, ifcond)
|
|
||||||
|
|
||||||
def _def_union_type(self, expr, info, doc):
|
def _def_union_type(self, expr, info, doc):
|
||||||
name = expr['union']
|
name = expr['union']
|
||||||
|
base = expr['base']
|
||||||
|
tag_name = expr['discriminator']
|
||||||
data = expr['data']
|
data = expr['data']
|
||||||
base = expr.get('base')
|
|
||||||
ifcond = QAPISchemaIfCond(expr.get('if'))
|
ifcond = QAPISchemaIfCond(expr.get('if'))
|
||||||
features = self._make_features(expr.get('features'), info)
|
features = self._make_features(expr.get('features'), info)
|
||||||
tag_name = expr.get('discriminator')
|
|
||||||
tag_member = None
|
|
||||||
if isinstance(base, dict):
|
if isinstance(base, dict):
|
||||||
base = self._make_implicit_object_type(
|
base = self._make_implicit_object_type(
|
||||||
name, info, ifcond,
|
name, info, ifcond,
|
||||||
'base', self._make_members(base, info))
|
'base', self._make_members(base, info))
|
||||||
if tag_name:
|
|
||||||
variants = [
|
variants = [
|
||||||
self._make_variant(key, value['type'],
|
self._make_variant(key, value['type'],
|
||||||
QAPISchemaIfCond(value.get('if')),
|
QAPISchemaIfCond(value.get('if')),
|
||||||
info)
|
info)
|
||||||
for (key, value) in data.items()]
|
for (key, value) in data.items()]
|
||||||
members = []
|
members = []
|
||||||
else:
|
|
||||||
variants = [
|
|
||||||
self._make_simple_variant(key, value['type'],
|
|
||||||
QAPISchemaIfCond(value.get('if')),
|
|
||||||
info)
|
|
||||||
for (key, value) in data.items()]
|
|
||||||
enum = [{'name': v.name, 'if': v.ifcond.ifcond} for v in variants]
|
|
||||||
typ = self._make_implicit_enum_type(name, info, ifcond, enum)
|
|
||||||
tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
|
|
||||||
members = [tag_member]
|
|
||||||
self._def_entity(
|
self._def_entity(
|
||||||
QAPISchemaObjectType(name, info, doc, ifcond, features,
|
QAPISchemaObjectType(name, info, doc, ifcond, features,
|
||||||
base, members,
|
base, members,
|
||||||
QAPISchemaVariants(
|
QAPISchemaVariants(
|
||||||
tag_name, info, tag_member, variants)))
|
tag_name, info, None, variants)))
|
||||||
|
|
||||||
def _def_alternate_type(self, expr, info, doc):
|
def _def_alternate_type(self, expr, info, doc):
|
||||||
name = expr['alternate']
|
name = expr['alternate']
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# we require flat union branches to be a struct
|
# we require union branches to be a struct
|
||||||
{ 'enum': 'TestEnum',
|
{ 'enum': 'TestEnum',
|
||||||
'data': [ 'value1', 'value2' ] }
|
'data': [ 'value1', 'value2' ] }
|
||||||
{ 'struct': 'Base',
|
{ 'struct': 'Base',
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# flat union discriminator cannot be empty
|
# union discriminator enum cannot be empty
|
||||||
{ 'enum': 'Empty', 'data': [ ] }
|
{ 'enum': 'Empty', 'data': [ ] }
|
||||||
{ 'struct': 'Base', 'data': { 'type': 'Empty' } }
|
{ 'struct': 'Base', 'data': { 'type': 'Empty' } }
|
||||||
{ 'union': 'Union', 'base': 'Base', 'discriminator': 'type', 'data': { } }
|
{ 'union': 'Union', 'base': 'Base', 'discriminator': 'type', 'data': { } }
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# we require flat union branches to be a struct
|
# we require union branches to be a struct
|
||||||
{ 'enum': 'TestEnum',
|
{ 'enum': 'TestEnum',
|
||||||
'data': [ 'value1', 'value2' ] }
|
'data': [ 'value1', 'value2' ] }
|
||||||
{ 'struct': 'Base',
|
{ 'struct': 'Base',
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
flat-union-no-base.json: In union 'TestUnion':
|
flat-union-no-base.json: In union 'TestUnion':
|
||||||
flat-union-no-base.json:8: 'discriminator' requires 'base'
|
flat-union-no-base.json:8: union misses key 'base'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# flat unions require a base
|
# unions require a base
|
||||||
{ 'struct': 'TestTypeA',
|
{ 'struct': 'TestTypeA',
|
||||||
'data': { 'string': 'str' } }
|
'data': { 'string': 'str' } }
|
||||||
{ 'struct': 'TestTypeB',
|
{ 'struct': 'TestTypeB',
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
{ 'struct': 'Empty1', 'data': { } }
|
{ 'struct': 'Empty1', 'data': { } }
|
||||||
{ 'struct': 'Empty2', 'base': 'Empty1', 'data': { } }
|
{ 'struct': 'Empty2', 'base': 'Empty1', 'data': { } }
|
||||||
|
|
||||||
# Likewise for an empty flat union
|
# Likewise for an empty union
|
||||||
{ 'union': 'Union',
|
{ 'union': 'Union',
|
||||||
'base': { 'type': 'EnumOne' }, 'discriminator': 'type',
|
'base': { 'type': 'EnumOne' }, 'discriminator': 'type',
|
||||||
'data': { } }
|
'data': { } }
|
||||||
|
@ -2,6 +2,6 @@
|
|||||||
# We reject use of 'u' as a member name, to allow it for internal use in
|
# We reject use of 'u' as a member name, to allow it for internal use in
|
||||||
# putting union branch members in a separate namespace from QMP members.
|
# putting union branch members in a separate namespace from QMP members.
|
||||||
# This is true even for non-unions, because it is possible to convert a
|
# This is true even for non-unions, because it is possible to convert a
|
||||||
# struct to flat union while remaining backwards compatible in QMP.
|
# struct to union while remaining backwards compatible in QMP.
|
||||||
# TODO - we could munge the member name to 'q_u' to avoid the collision
|
# TODO - we could munge the member name to 'q_u' to avoid the collision
|
||||||
{ 'struct': 'Oops', 'data': { '*u': 'str' } }
|
{ 'struct': 'Oops', 'data': { '*u': 'str' } }
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Flat union with empty base and therefore without discriminator
|
# Union with empty base and therefore without discriminator
|
||||||
|
|
||||||
{ 'struct': 'Empty', 'data': { } }
|
{ 'struct': 'Empty', 'data': { } }
|
||||||
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
union-base-no-discriminator.json: In union 'TestUnion':
|
union-base-no-discriminator.json: In union 'TestUnion':
|
||||||
union-base-no-discriminator.json:11: 'base' requires 'discriminator'
|
union-base-no-discriminator.json:11: union misses key 'discriminator'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# we reject simple unions with a base (or flat unions without discriminator)
|
# we reject unions without discriminator
|
||||||
{ 'struct': 'TestTypeA',
|
{ 'struct': 'TestTypeA',
|
||||||
'data': { 'string': 'str' } }
|
'data': { 'string': 'str' } }
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user