Right now, our ad hoc parser ensures that we cannot have a
flat union that introduces any QMP member names that would
conflict with the non-variant QMP members already present
from the union's base type (see flat-union-clash-member.json).
However, we want QAPISchema*.check() to make the same check,
so we can later reduce some of the ad hoc checks.

Basically, all branches of a flat union must be qapi structs
with no variants, at which point those members appear in the
same JSON object as all non-variant members.  We already have
a map 'seen' of all non-variant members passed in to
QAPISchemaObjectTypeVariants.check(), which we clone for each
particular variant (since the members of one variant do not
clash with another); all that is additionally needed is to
actually check the each member of the variant type do not add
any collisions.

For simple unions, the same code happens to work (however, our
synthesized wrapper type has a single member 'data' which will
never collide with the one non-variant member 'type').

But for alternates, we do NOT want to check the type members
for adding collisions (an alternate has no parent JSON object
that is merging in member names, the way a flat union does), so
we have to pass in an extra flag to distinguish whether we are
working with a union or an alternate.  The flag is temporary; a
later patch will rework how alternates are laid out by creating
a new subclass of QAPISchemaObjectTypeMember, and detecting the
use of this subclass for variants.tag_member will serve the
same purpose.

Note that an early proposal [1] for what eventually became
commit ac88219a had QAPISchemaObjectTypeVariant.check() ensure
that the variant type was complete, although it was later
removed [2]; the checks added here happen to match what that
earlier attempt meant to do.

[1] https://lists.gnu.org/archive/html/qemu-devel/2015-07/msg00394.html
[2] https://lists.gnu.org/archive/html/qemu-devel/2015-08/msg00450.html

Signed-off-by: Eric Blake <ebl...@redhat.com>

---
v9: new patch, split off from v8 7/17
---
 scripts/qapi.py | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index fff4adb..3cf051f 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1047,7 +1047,8 @@ class QAPISchemaObjectTypeVariants(object):
         self.tag_member = tag_member
         self.variants = variants

-    def check(self, schema, seen):
+    # TODO drop 'union' param once tag_member is sufficient to spot alternates
+    def check(self, schema, seen, union=True):
         if self.tag_name:    # flat union
             self.tag_member = seen[self.tag_name]
             assert self.tag_member
@@ -1055,17 +1056,29 @@ class QAPISchemaObjectTypeVariants(object):
             assert self.tag_member in seen.itervalues()
         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
         for v in self.variants:
-            vseen = dict(seen)
-            v.check(schema, self.tag_member.type, vseen)
+            # Reset seen array for each variant, since QMP names from one
+            # branch do not affect another branch
+            v.check(schema, self.tag_member.type, dict(seen), union)


 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
     def __init__(self, name, typ):
         QAPISchemaObjectTypeMember.__init__(self, name, typ, False)

-    def check(self, schema, tag_type, seen):
-        QAPISchemaObjectTypeMember.check(self, schema, seen)
+    # TODO drop 'union' param once tag_type is sufficient to spot alternates
+    def check(self, schema, tag_type, seen, union):
+        QAPISchemaObjectTypeMember.check(self, schema, dict(seen))
         assert self.name in tag_type.values
+        if union:
+            # If this variant is used within a union, then each member
+            # field must avoid collisions with the non-variant members
+            # already present in the union.
+            assert isinstance(self.type, QAPISchemaObjectType)
+            assert not self.type.variants       # not implemented
+            self.type.check(schema)
+            for m in self.type.members:
+                assert c_name(m.name) not in seen
+                seen[m.name] = m

     # This function exists to support ugly simple union special cases
     # TODO get rid of them, and drop the function
@@ -1088,7 +1101,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
     def check(self, schema):
         seen = {}
         self.variants.tag_member.check(schema, seen)
-        self.variants.check(schema, seen)
+        self.variants.check(schema, seen, False)

     def json_type(self):
         return 'value'
-- 
2.4.3


Reply via email to