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:
Markus Armbruster 2021-09-17 16:31:32 +02:00
parent 76432d988b
commit 4e99f4b12c
13 changed files with 62 additions and 205 deletions

View file

@ -321,8 +321,8 @@ class QAPISchemaEnumType(QAPISchemaType):
m.connect_doc(doc)
def is_implicit(self):
# See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
return self.name.endswith('Kind') or self.name == 'QType'
# See QAPISchema._def_predefineds()
return self.name == 'QType'
def c_type(self):
return c_name(self.name)
@ -393,8 +393,7 @@ class QAPISchemaObjectType(QAPISchemaType):
def __init__(self, name, info, doc, ifcond, features,
base, local_members, variants):
# struct has local_members, optional base, and no variants
# flat union has base, variants, and no local_members
# simple union has local_members, variants, and no base
# union has base, variants, and no local_members
super().__init__(name, info, doc, ifcond, features)
self.meta = 'union' if variants else 'struct'
assert base is None or isinstance(base, str)
@ -465,15 +464,6 @@ class QAPISchemaObjectType(QAPISchemaType):
for m in self.local_members:
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):
# See QAPISchema._make_implicit_object_type(), as well as
# _def_predefineds()
@ -576,10 +566,9 @@ class QAPISchemaAlternateType(QAPISchemaType):
class QAPISchemaVariants:
def __init__(self, tag_name, info, tag_member, variants):
# Flat unions pass tag_name but not tag_member.
# Simple unions and alternates pass tag_member but not tag_name.
# After check(), tag_member is always set, and tag_name remains
# a reliable witness of being used by a flat union.
# Unions pass tag_name but not tag_member.
# Alternates pass tag_member but not tag_name.
# After check(), tag_member is always set.
assert bool(tag_member) != bool(tag_name)
assert (isinstance(tag_name, str) or
isinstance(tag_member, QAPISchemaObjectTypeMember))
@ -595,7 +584,7 @@ class QAPISchemaVariants:
v.set_defined_in(name)
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))
base = "'base'"
# Pointing to the base type when not implicit would be
@ -625,11 +614,11 @@ class QAPISchemaVariants:
self.info,
"discriminator member '%s' of %s must not be conditional"
% (self._tag_name, base))
else: # simple union
else: # alternate
assert isinstance(self.tag_member.type, QAPISchemaEnumType)
assert not self.tag_member.optional
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
cases = {v.name for v in self.variants}
for m in self.tag_member.type.members:
@ -707,18 +696,10 @@ class QAPISchemaMember:
assert role == 'member'
role = 'parameter'
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
else:
# Implicit type created for a simple union's branch
assert defined_in.endswith('-wrapper')
# Unreachable and not implemented
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:
return "%s '%s' of type '%s'" % (role, self.name, defined_in)
return "%s '%s'" % (role, self.name)
@ -1004,15 +985,6 @@ class QAPISchema:
QAPISchemaIfCond(v.get('if')))
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):
name = element_type + 'List' # reserved by check_defn_name_str()
if not self.lookup_type(name):
@ -1026,17 +998,9 @@ class QAPISchema:
name = 'q_obj_%s-%s' % (name, role)
typ = self.lookup_entity(name, QAPISchemaObjectType)
if typ:
# The implicit object type has multiple users. This is
# either a duplicate definition (which will be flagged
# later), or an implicit wrapper type used for multiple
# 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
# The implicit object type has multiple users. This can
# only be a duplicate definition, which will be flagged
# later.
pass
else:
self._def_entity(QAPISchemaObjectType(
@ -1084,49 +1048,28 @@ class QAPISchema:
def _make_variant(self, case, typ, ifcond, info):
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):
name = expr['union']
base = expr['base']
tag_name = expr['discriminator']
data = expr['data']
base = expr.get('base')
ifcond = QAPISchemaIfCond(expr.get('if'))
features = self._make_features(expr.get('features'), info)
tag_name = expr.get('discriminator')
tag_member = None
if isinstance(base, dict):
base = self._make_implicit_object_type(
name, info, ifcond,
'base', self._make_members(base, info))
if tag_name:
variants = [
self._make_variant(key, value['type'],
QAPISchemaIfCond(value.get('if')),
info)
for (key, value) in data.items()]
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]
variants = [
self._make_variant(key, value['type'],
QAPISchemaIfCond(value.get('if')),
info)
for (key, value) in data.items()]
members = []
self._def_entity(
QAPISchemaObjectType(name, info, doc, ifcond, features,
base, members,
QAPISchemaVariants(
tag_name, info, tag_member, variants)))
tag_name, info, None, variants)))
def _def_alternate_type(self, expr, info, doc):
name = expr['alternate']