diff options
author | Markus Armbruster <armbru@redhat.com> | 2021-09-17 16:31:32 +0200 |
---|---|---|
committer | Markus Armbruster <armbru@redhat.com> | 2021-09-27 08:23:25 +0200 |
commit | 4e99f4b12c0e47898e8358a5c8fa54267bb16185 (patch) | |
tree | a93792619d67b2345037d7862c1ac1d31535c31b /scripts/qapi | |
parent | 76432d988b67d95006d0aa66dce2aa5999868d29 (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.py | 21 | ||||
-rw-r--r-- | scripts/qapi/schema.py | 101 |
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'] |