aboutsummaryrefslogtreecommitdiff
path: root/scripts/qapi
diff options
context:
space:
mode:
authorMarkus Armbruster <armbru@redhat.com>2021-09-17 16:31:32 +0200
committerMarkus Armbruster <armbru@redhat.com>2021-09-27 08:23:25 +0200
commit4e99f4b12c0e47898e8358a5c8fa54267bb16185 (patch)
treea93792619d67b2345037d7862c1ac1d31535c31b /scripts/qapi
parent76432d988b67d95006d0aa66dce2aa5999868d29 (diff)
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>
Diffstat (limited to 'scripts/qapi')
-rw-r--r--scripts/qapi/expr.py21
-rw-r--r--scripts/qapi/schema.py101
2 files changed, 28 insertions, 94 deletions
diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py
index 91959ee79a..819ea6ad97 100644
--- a/scripts/qapi/expr.py
+++ b/scripts/qapi/expr.py
@@ -513,27 +513,18 @@ def check_union(expr: _JSONObject, info: QAPISourceInfo) -> None:
:return: None, ``expr`` is normalized in-place as needed.
"""
name = cast(str, expr['union']) # Checked in check_exprs
- base = expr.get('base')
- discriminator = expr.get('discriminator')
+ base = expr['base']
+ discriminator = expr['discriminator']
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)
- if not base:
- raise QAPISemError(info, "'discriminator' requires 'base'")
- check_name_is_str(discriminator, info, "'discriminator'")
+ check_type(base, info, "'base'", allow_dict=name)
+ check_name_is_str(discriminator, info, "'discriminator'")
if not isinstance(members, dict):
raise QAPISemError(info, "'data' must be an object")
for (key, value) in members.items():
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_if(value, info, source)
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)
elif meta == 'union':
check_keys(expr, info, meta,
- ['union', 'data'],
- ['base', 'discriminator', 'if', 'features'])
+ ['union', 'base', 'discriminator', 'data'],
+ ['if', 'features'])
normalize_members(expr.get('base'))
normalize_members(expr['data'])
check_union(expr, info)
diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 3d72c7dfc9..004d7095ff 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -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']