All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types
@ 2016-02-25 23:38 Eric Blake
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 01/19] qapi: Rename 'fields' to 'members' in internal interface Eric Blake
                   ` (19 more replies)
  0 siblings, 20 replies; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Eduardo Habkost

Merge of two previous series:
https://lists.gnu.org/archive/html/qemu-devel/2016-02/msg05492.html
https://lists.gnu.org/archive/html/qemu-devel/2016-02/msg04703.html

Depends on Markus' qapi-next branch.

Also available as a tag at this location:
git fetch git://repo.or.cz/qemu/ericb.git qapi-implicit-v2

and will soon be part of my branch at:
http://repo.or.cz/qemu/ericb.git/shortlog/refs/heads/qapi

Patch 1 of v1 each of those series has already been proposed for
inclusion elsewhere (Markus' qapi-next branch, Paolo's misc changes
pull request).  There's a new patch 1 that does some renaming
suggested by Markus, then the rest of the patches have some fallout
due to the renaming.  backport-diff gets a bit confused on the
patches whose title text changed (2, 10, 15).

001/19:[down] 'qapi: Rename 'fields' to 'members' in internal interface'
002/19:[down] 'qapi-visit: Expose visit_type_FOO_members()'
003/19:[0019] [FC] 'qapi: Update docs to match recent generator changes'
004/19:[----] [--] 'chardev: Shorten references into ChardevBackend'
005/19:[----] [--] 'util: Shorten references into SocketAddress'
006/19:[----] [--] 'ui: Shorten references into InputEvent'
007/19:[----] [--] 'qapi: Avoid use of 'data' member of qapi unions'
008/19:[----] [--] 'chardev: Drop useless ChardevDummy type'
009/19:[----] [--] 'qapi: Drop useless 'data' member of unions'
010/19:[down] 'qapi-visit: Factor out gen_visit_members_call()'
011/19:[----] [-C] 'qapi: Add type.is_empty() helper'
012/19:[----] [-C] 'qapi: Fix command with named empty argument type'
013/19:[----] [-C] 'qapi-visit: Simplify visit of empty branch in union'
014/19:[0020] [FC] 'qapi: Don't special-case simple union wrappers'
015/19:[down] 'qapi-visit: Move error check into gen_visit_members_call()'
016/19:[0010] [FC] 'qapi: Allow anonymous base for flat union'
017/19:[----] [--] 'qapi: Use anonymous base in SchemaInfo'
018/19:[----] [--] 'qapi: Use anonymous base in CpuInfo'
019/19:[----] [--] 'qapi: Make c_type() more OO-like'

Eric Blake (19):
  qapi: Rename 'fields' to 'members' in internal interface
  qapi-visit: Expose visit_type_FOO_members()
  qapi: Update docs to match recent generator changes
  chardev: Shorten references into ChardevBackend
  util: Shorten references into SocketAddress
  ui: Shorten references into InputEvent
  qapi: Avoid use of 'data' member of qapi unions
  chardev: Drop useless ChardevDummy type
  qapi: Drop useless 'data' member of unions
  qapi-visit: Factor out gen_visit_members_call()
  qapi: Add type.is_empty() helper
  qapi: Fix command with named empty argument type
  qapi-visit: Simplify visit of empty branch in union
  qapi: Don't special-case simple union wrappers
  qapi-visit: Move error check into gen_visit_members_call()
  qapi: Allow anonymous base for flat union
  qapi: Use anonymous base in SchemaInfo
  qapi: Use anonymous base in CpuInfo
  qapi: Make c_type() more OO-like

 scripts/qapi.py                            |  86 +++++---
 scripts/qapi-commands.py                   |  10 +-
 scripts/qapi-event.py                      |  11 +-
 scripts/qapi-types.py                      |  55 ++---
 scripts/qapi-visit.py                      | 102 ++++-----
 backends/baum.c                            |   2 +-
 backends/msmouse.c                         |   2 +-
 block/nbd.c                                |  16 +-
 block/qcow2.c                              |   6 +-
 block/vmdk.c                               |   8 +-
 blockdev.c                                 |  49 +++--
 hmp.c                                      |   8 +-
 hw/char/escc.c                             |  12 +-
 hw/input/hid.c                             |  36 ++--
 hw/input/ps2.c                             |  27 ++-
 hw/input/virtio-input-hid.c                |  33 +--
 hw/mem/pc-dimm.c                           |   2 +-
 net/dump.c                                 |   2 +-
 net/hub.c                                  |   2 +-
 net/l2tpv3.c                               |   2 +-
 net/net.c                                  |   4 +-
 net/netmap.c                               |   2 +-
 net/slirp.c                                |   2 +-
 net/socket.c                               |   2 +-
 net/tap-win32.c                            |   2 +-
 net/tap.c                                  |   4 +-
 net/vde.c                                  |   2 +-
 net/vhost-user.c                           |   2 +-
 numa.c                                     |   4 +-
 qemu-char.c                                | 221 ++++++++++---------
 qemu-nbd.c                                 |  13 +-
 replay/replay-input.c                      |  63 +++---
 spice-qemu-char.c                          |  14 +-
 tests/test-io-channel-socket.c             |  58 ++---
 tests/test-qmp-commands.c                  |   7 +-
 tests/test-qmp-input-visitor.c             |  25 +--
 tests/test-qmp-output-visitor.c            |  24 +--
 tpm.c                                      |   2 +-
 ui/console.c                               |   4 +-
 ui/input-keymap.c                          |  10 +-
 ui/input-legacy.c                          |  26 ++-
 ui/input.c                                 |  72 ++++---
 ui/vnc-auth-sasl.c                         |   3 +-
 ui/vnc.c                                   |  62 +++---
 util/qemu-sockets.c                        |  44 ++--
 docs/qapi-code-gen.txt                     | 335 +++++++++++++++--------------
 qapi-schema.json                           |  35 ++-
 qapi/introspect.json                       |  12 +-
 tests/Makefile                             |   1 -
 tests/qapi-schema/flat-union-bad-base.err  |   2 +-
 tests/qapi-schema/flat-union-bad-base.json |   5 +-
 tests/qapi-schema/qapi-schema-test.json    |  10 +-
 tests/qapi-schema/qapi-schema-test.out     |  15 +-
 tests/qapi-schema/union-clash-data.err     |   0
 tests/qapi-schema/union-clash-data.exit    |   1 -
 tests/qapi-schema/union-clash-data.json    |   7 -
 tests/qapi-schema/union-clash-data.out     |   9 -
 57 files changed, 824 insertions(+), 751 deletions(-)
 delete mode 100644 tests/qapi-schema/union-clash-data.err
 delete mode 100644 tests/qapi-schema/union-clash-data.exit
 delete mode 100644 tests/qapi-schema/union-clash-data.json
 delete mode 100644 tests/qapi-schema/union-clash-data.out

-- 
2.5.0

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 01/19] qapi: Rename 'fields' to 'members' in internal interface
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-02 17:15   ` Markus Armbruster
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 02/19] qapi-visit: Expose visit_type_FOO_members() Eric Blake
                   ` (18 subsequent siblings)
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

C types and JSON objects don't have fields, but members.  We
shouldn't gratuitously invent terminology.  This patch is a
strict renaming of generator code and static genarated functions,
plus the naming of the dummy filler member for empty structs,
before the next patch exposes some of that naming to the rest of
the code base.

Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>

---
v2: new patch
---
 scripts/qapi.py          | 20 ++++++++++----------
 scripts/qapi-commands.py |  4 ++--
 scripts/qapi-event.py    |  4 ++--
 scripts/qapi-types.py    | 10 +++++-----
 scripts/qapi-visit.py    | 40 ++++++++++++++++++++--------------------
 5 files changed, 39 insertions(+), 39 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 8497777..6c52fe5 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -328,7 +328,7 @@ class QAPISchemaParser(object):
 #


-def find_base_fields(base):
+def find_base_members(base):
     base_struct_define = find_struct(base)
     if not base_struct_define:
         return None
@@ -357,11 +357,11 @@ def discriminator_find_enum_define(expr):
     if not (discriminator and base):
         return None

-    base_fields = find_base_fields(base)
-    if not base_fields:
+    base_members = find_base_members(base)
+    if not base_members:
         return None

-    discriminator_type = base_fields.get(discriminator)
+    discriminator_type = base_members.get(discriminator)
     if not discriminator_type:
         return None

@@ -569,14 +569,14 @@ def check_union(expr, expr_info):
             raise QAPIExprError(expr_info,
                                 "Flat union '%s' must have a base"
                                 % name)
-        base_fields = find_base_fields(base)
-        assert base_fields
+        base_members = find_base_members(base)
+        assert base_members

         # The value of member 'discriminator' must name a non-optional
         # member of the base struct.
         check_name(expr_info, "Discriminator of flat union '%s'" % name,
                    discriminator)
-        discriminator_type = base_fields.get(discriminator)
+        discriminator_type = base_members.get(discriminator)
         if not discriminator_type:
             raise QAPIExprError(expr_info,
                                 "Discriminator '%s' is not a member of base "
@@ -971,7 +971,7 @@ class QAPISchemaObjectType(QAPISchemaType):
             assert self.variants.tag_member in self.members
             self.variants.check_clash(schema, self.info, seen)

-    # Check that the members of this type do not cause duplicate JSON fields,
+    # Check that the members of this type do not cause duplicate JSON members,
     # and update seen to track the members seen so far. Report any errors
     # on behalf of info, which is not necessarily self.info
     def check_clash(self, schema, info, seen):
@@ -1649,8 +1649,8 @@ def gen_err_check(label='out', skiperr=False):
                  label=label)


-def gen_visit_fields(members, prefix='', need_cast=False, skiperr=False,
-                     label='out'):
+def gen_visit_members(members, prefix='', need_cast=False, skiperr=False,
+                      label='out'):
     ret = ''
     if skiperr:
         errparg = 'NULL'
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index f831621..f44e01f 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -111,7 +111,7 @@ def gen_marshal_input_visit(arg_type, dealloc=False):
     v = qmp_input_get_visitor(qiv);
 ''')

-    ret += gen_visit_fields(arg_type.members, skiperr=dealloc)
+    ret += gen_visit_members(arg_type.members, skiperr=dealloc)

     if dealloc:
         ret += mcgen('''
@@ -175,7 +175,7 @@ def gen_marshal(name, arg_type, ret_type):
     ret += gen_marshal_input_visit(arg_type)
     ret += gen_call(name, arg_type, ret_type)

-    # 'goto out' produced by gen_marshal_input_visit->gen_visit_fields()
+    # 'goto out' produced by gen_marshal_input_visit->gen_visit_members()
     # for each arg_type member, and by gen_call() for ret_type
     if (arg_type and arg_type.members) or ret_type:
         ret += mcgen('''
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 544ae12..fb579dd 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -67,8 +67,8 @@ def gen_event_send(name, arg_type):
 ''',
                      name=name)
         ret += gen_err_check()
-        ret += gen_visit_fields(arg_type.members, need_cast=True,
-                                label='out_obj')
+        ret += gen_visit_members(arg_type.members, need_cast=True,
+                                 label='out_obj')
         ret += mcgen('''
 out_obj:
     visit_end_struct(v, err ? NULL : &err);
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index eac90d2..19d1fff 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -38,7 +38,7 @@ struct %(c_name)s {
                  c_name=c_name(name), c_type=element_type.c_type())


-def gen_struct_fields(members):
+def gen_struct_members(members):
     ret = ''
     for memb in members:
         if memb.optional:
@@ -77,22 +77,22 @@ struct %(c_name)s {
     /* Members inherited from %(c_name)s: */
 ''',
                      c_name=base.c_name())
-        ret += gen_struct_fields(base.members)
+        ret += gen_struct_members(base.members)
         ret += mcgen('''
     /* Own members: */
 ''')
-    ret += gen_struct_fields(members)
+    ret += gen_struct_members(members)

     if variants:
         ret += gen_variants(variants)

-    # Make sure that all structs have at least one field; this avoids
+    # Make sure that all structs have at least one member; this avoids
     # potential issues with attempting to malloc space for zero-length
     # structs in C, and also incompatibility with C++ (where an empty
     # struct is size 1).
     if not (base and base.members) and not members and not variants:
         ret += mcgen('''
-    char qapi_dummy_field_for_empty_struct;
+    char qapi_dummy_for_empty_struct;
 ''')

     ret += mcgen('''
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 2308268..1e52f76 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -15,9 +15,9 @@
 from qapi import *
 import re

-# visit_type_FOO_fields() is always emitted; track if a forward declaration
+# visit_type_FOO_members() is always emitted; track if a forward declaration
 # or implementation has already been output.
-struct_fields_seen = set()
+object_members_seen = set()


 def gen_visit_decl(name, scalar=False):
@@ -30,32 +30,32 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **
                  c_name=c_name(name), c_type=c_type)


-def gen_visit_fields_decl(typ):
-    if typ.name in struct_fields_seen:
+def gen_visit_members_decl(typ):
+    if typ.name in object_members_seen:
         return ''
-    struct_fields_seen.add(typ.name)
+    object_members_seen.add(typ.name)
     return mcgen('''

-static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s *obj, Error **errp);
+static void visit_type_%(c_type)s_members(Visitor *v, %(c_type)s *obj, Error **errp);
 ''',
                  c_type=typ.c_name())


-def gen_visit_struct_fields(name, base, members, variants):
+def gen_visit_object_members(name, base, members, variants):
     ret = ''

     if base:
-        ret += gen_visit_fields_decl(base)
+        ret += gen_visit_members_decl(base)
     if variants:
         for var in variants.variants:
             # Ugly special case for simple union TODO get rid of it
             if not var.simple_union_type():
-                ret += gen_visit_fields_decl(var.type)
+                ret += gen_visit_members_decl(var.type)

-    struct_fields_seen.add(name)
+    object_members_seen.add(name)
     ret += mcgen('''

-static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s *obj, Error **errp)
+static void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
 {
     Error *err = NULL;

@@ -64,12 +64,12 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s *obj, Error **er

     if base:
         ret += mcgen('''
-    visit_type_%(c_type)s_fields(v, (%(c_type)s *)obj, &err);
+    visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, &err);
 ''',
                      c_type=base.c_name())
         ret += gen_err_check()

-    ret += gen_visit_fields(members, prefix='obj->')
+    ret += gen_visit_members(members, prefix='obj->')

     if variants:
         ret += mcgen('''
@@ -94,7 +94,7 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s *obj, Error **er
                              c_name=c_name(var.name))
             else:
                 ret += mcgen('''
-        visit_type_%(c_type)s_fields(v, &obj->u.%(c_name)s, &err);
+        visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err);
 ''',
                              c_type=var.type.c_name(),
                              c_name=c_name(var.name))
@@ -108,7 +108,7 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s *obj, Error **er
     }
 ''')

-    # 'goto out' produced for base, by gen_visit_fields() for each member,
+    # 'goto out' produced for base, by gen_visit_members() for each member,
     # and if variants were present
     if base or members or variants:
         ret += mcgen('''
@@ -174,7 +174,7 @@ def gen_visit_alternate(name, variants):
         if var.type.alternate_qtype() == 'QTYPE_QINT':
             promote_int = 'false'
         if isinstance(var.type, QAPISchemaObjectType):
-            ret += gen_visit_fields_decl(var.type)
+            ret += gen_visit_members_decl(var.type)

     ret += mcgen('''

@@ -202,7 +202,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
         if (err) {
             break;
         }
-        visit_type_%(c_type)s_fields(v, &(*obj)->u.%(c_name)s, &err);
+        visit_type_%(c_type)s_members(v, &(*obj)->u.%(c_name)s, &err);
         error_propagate(errp, err);
         err = NULL;
         visit_end_struct(v, &err);
@@ -235,10 +235,10 @@ out:


 def gen_visit_object(name, base, members, variants):
-    ret = gen_visit_struct_fields(name, base, members, variants)
+    ret = gen_visit_object_members(name, base, members, variants)

     # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
-    # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
+    # *obj, but then visit_type_FOO_members() fails, we should clean up *obj
     # rather than leaving it non-NULL. As currently written, the caller must
     # call qapi_free_FOO() to avoid a memory leak of the partial FOO.
     ret += mcgen('''
@@ -254,7 +254,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
     if (!*obj) {
         goto out_obj;
     }
-    visit_type_%(c_name)s_fields(v, *obj, &err);
+    visit_type_%(c_name)s_members(v, *obj, &err);
     error_propagate(errp, err);
     err = NULL;
 out_obj:
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 02/19] qapi-visit: Expose visit_type_FOO_members()
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 01/19] qapi: Rename 'fields' to 'members' in internal interface Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-02 17:24   ` Markus Armbruster
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 03/19] qapi: Update docs to match recent generator changes Eric Blake
                   ` (17 subsequent siblings)
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Dan Berrange reported a case where he needs to work with a
QCryptoBlockOptions union type using the OptsVisitor, but only
visit one of the branches of that type (the discriminator is not
visited directly, but learned externally).  When things were
boxed, it was easy: just visit the variant directly, which took
care of both allocating the variant and visiting its members, then
store that pointer in the union type.  But now that things are
unboxed, we need a way to visit the members without allocation,
done by exposing visit_type_FOO_members() to the user.

Before the patch, we had quite a bit of code associated with
object_members_seen to make sure that a declaration of the helper
was in scope before any use of the function.  But now that the
helper is public and declared in the header, the .c file no
longer needs to worry about topological sorting (the helper is
always in scope), which leads to some nice cleanups.

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

---
v2: Rebase atop s/fields/members/rename, don't provide useless
declaration on alternates
---
 scripts/qapi-visit.py | 33 +++++++--------------------------
 1 file changed, 7 insertions(+), 26 deletions(-)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 1e52f76..a712e9a 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -15,10 +15,6 @@
 from qapi import *
 import re

-# visit_type_FOO_members() is always emitted; track if a forward declaration
-# or implementation has already been output.
-object_members_seen = set()
-

 def gen_visit_decl(name, scalar=False):
     c_type = c_name(name) + ' *'
@@ -30,37 +26,23 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **
                  c_name=c_name(name), c_type=c_type)


-def gen_visit_members_decl(typ):
-    if typ.name in object_members_seen:
-        return ''
-    object_members_seen.add(typ.name)
+def gen_visit_members_decl(name):
     return mcgen('''

-static void visit_type_%(c_type)s_members(Visitor *v, %(c_type)s *obj, Error **errp);
+void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
 ''',
-                 c_type=typ.c_name())
+                 c_name=c_name(name))


 def gen_visit_object_members(name, base, members, variants):
-    ret = ''
+    ret = mcgen('''

-    if base:
-        ret += gen_visit_members_decl(base)
-    if variants:
-        for var in variants.variants:
-            # Ugly special case for simple union TODO get rid of it
-            if not var.simple_union_type():
-                ret += gen_visit_members_decl(var.type)
-
-    object_members_seen.add(name)
-    ret += mcgen('''
-
-static void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
+void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
 {
     Error *err = NULL;

 ''',
-                 c_name=c_name(name))
+                c_name=c_name(name))

     if base:
         ret += mcgen('''
@@ -173,8 +155,6 @@ def gen_visit_alternate(name, variants):
     for var in variants.variants:
         if var.type.alternate_qtype() == 'QTYPE_QINT':
             promote_int = 'false'
-        if isinstance(var.type, QAPISchemaObjectType):
-            ret += gen_visit_members_decl(var.type)

     ret += mcgen('''

@@ -316,6 +296,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
             self.defn += defn

     def visit_object_type(self, name, info, base, members, variants):
+        self.decl += gen_visit_members_decl(name)
         self.decl += gen_visit_decl(name)
         self.defn += gen_visit_object(name, base, members, variants)

-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 03/19] qapi: Update docs to match recent generator changes
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 01/19] qapi: Rename 'fields' to 'members' in internal interface Eric Blake
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 02/19] qapi-visit: Expose visit_type_FOO_members() Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 04/19] chardev: Shorten references into ChardevBackend Eric Blake
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Several commits have been changing the generator, but not updating
the docs to match:
- The implicit tag member is named "type", not "kind".  Screwed up in
commit 39a1815.
- Commit 9f08c8ec made list types lazy, and thereby dropped
UserDefOneList if nothing explicitly uses the list type.
- Commit 51e72bc1 switched the parameter order with 'name' occurring
earlier.
- Commit e65d89bf changed the layout of UserDefOneList.
- We now expose visit_type_FOO_members() for objects.
- etc.

Rework the examples to show slightly more output (we don't want to
show too much; that's what the testsuite is for), and regenerate the
output to match all recent changes.  Also, rearrange output to show
.h files before .c (understanding the interface first often makes
the implementation easier to follow).

Reported-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>

---
v2: Fix minor errors and rebase to s/fields/members/ rename
Some content from Markus, hence his S-o-b.
A former version of this patch was posted with subset E v9.
---
 docs/qapi-code-gen.txt | 307 ++++++++++++++++++++++++++-----------------------
 1 file changed, 161 insertions(+), 146 deletions(-)

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 999f3b9..2519c07 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -1,7 +1,7 @@
 = How to use the QAPI code generator =

 Copyright IBM Corp. 2011
-Copyright (C) 2012-2015 Red Hat, Inc.
+Copyright (C) 2012-2016 Red Hat, Inc.

 This work is licensed under the terms of the GNU GPL, version 2 or
 later. See the COPYING file in the top-level directory.
@@ -656,7 +656,7 @@ Union types

     { "name": "BlockdevOptions", "meta-type": "object",
       "members": [
-          { "name": "kind", "type": "BlockdevOptionsKind" } ],
+          { "name": "type", "type": "BlockdevOptionsKind" } ],
       "tag": "type",
       "variants": [
           { "case": "file", "type": ":obj-FileOptions-wrapper" },
@@ -722,33 +722,38 @@ the names of built-in types.  Clients should examine member

 == Code generation ==

-Schemas are fed into four scripts to generate all the code/files that,
+Schemas are fed into five scripts to generate all the code/files that,
 paired with the core QAPI libraries, comprise everything required to
 take JSON commands read in by a Client JSON Protocol server, unmarshal
 the arguments into the underlying C types, call into the corresponding
-C function, and map the response back to a Client JSON Protocol
-response to be returned to the user.
+C function, map the response back to a Client JSON Protocol response
+to be returned to the user, and introspect the commands.

-As an example, we'll use the following schema, which describes a single
-complex user-defined type (which will produce a C struct, along with a list
-node structure that can be used to chain together a list of such types in
-case we want to accept/return a list of this type with a command), and a
-command which takes that type as a parameter and returns the same type:
+As an example, we'll use the following schema, which describes a
+single complex user-defined type, along with command which takes a
+list of that type as a parameter, and returns a single element of that
+type.  The user is responsible for writing the implementation of
+qmp_my_command(); everything else is produced by the generator.

     $ cat example-schema.json
     { 'struct': 'UserDefOne',
-      'data': { 'integer': 'int', 'string': 'str' } }
+      'data': { 'integer': 'int', '*string': 'str' } }

     { 'command': 'my-command',
-      'data':    {'arg1': 'UserDefOne'},
+      'data': { 'arg1': ['UserDefOne'] },
       'returns': 'UserDefOne' }

     { 'event': 'MY_EVENT' }

+For a more thorough look at generated code, the testsuite includes
+tests/qapi-schema/qapi-schema-tests.json that covers more examples of
+what the generator will accept, and compiles the resulting C code as
+part of 'make check-unit'.
+
 === scripts/qapi-types.py ===

-Used to generate the C types defined by a schema. The following files are
-created:
+Used to generate the C types defined by a schema, along with
+supporting code. The following files are created:

 $(prefix)qapi-types.h - C types corresponding to types defined in
                         the schema you pass in
@@ -763,38 +768,6 @@ Example:

     $ python scripts/qapi-types.py --output-dir="qapi-generated" \
     --prefix="example-" example-schema.json
-    $ cat qapi-generated/example-qapi-types.c
-[Uninteresting stuff omitted...]
-
-    void qapi_free_UserDefOne(UserDefOne *obj)
-    {
-        QapiDeallocVisitor *qdv;
-        Visitor *v;
-
-        if (!obj) {
-            return;
-        }
-
-        qdv = qapi_dealloc_visitor_new();
-        v = qapi_dealloc_get_visitor(qdv);
-        visit_type_UserDefOne(v, &obj, NULL, NULL);
-        qapi_dealloc_visitor_cleanup(qdv);
-    }
-
-    void qapi_free_UserDefOneList(UserDefOneList *obj)
-    {
-        QapiDeallocVisitor *qdv;
-        Visitor *v;
-
-        if (!obj) {
-            return;
-        }
-
-        qdv = qapi_dealloc_visitor_new();
-        v = qapi_dealloc_get_visitor(qdv);
-        visit_type_UserDefOneList(v, &obj, NULL, NULL);
-        qapi_dealloc_visitor_cleanup(qdv);
-    }
     $ cat qapi-generated/example-qapi-types.h
 [Uninteresting stuff omitted...]

@@ -809,29 +782,59 @@ Example:

     struct UserDefOne {
         int64_t integer;
+        bool has_string;
         char *string;
     };

     void qapi_free_UserDefOne(UserDefOne *obj);

     struct UserDefOneList {
-        union {
-            UserDefOne *value;
-            uint64_t padding;
-        };
         UserDefOneList *next;
+        UserDefOne *value;
     };

     void qapi_free_UserDefOneList(UserDefOneList *obj);

     #endif
+    $ cat qapi-generated/example-qapi-types.c
+[Uninteresting stuff omitted...]
+
+    void qapi_free_UserDefOne(UserDefOne *obj)
+    {
+        QapiDeallocVisitor *qdv;
+        Visitor *v;
+
+        if (!obj) {
+            return;
+        }
+
+        qdv = qapi_dealloc_visitor_new();
+        v = qapi_dealloc_get_visitor(qdv);
+        visit_type_UserDefOne(v, NULL, &obj, NULL);
+        qapi_dealloc_visitor_cleanup(qdv);
+    }
+
+    void qapi_free_UserDefOneList(UserDefOneList *obj)
+    {
+        QapiDeallocVisitor *qdv;
+        Visitor *v;
+
+        if (!obj) {
+            return;
+        }
+
+        qdv = qapi_dealloc_visitor_new();
+        v = qapi_dealloc_get_visitor(qdv);
+        visit_type_UserDefOneList(v, NULL, &obj, NULL);
+        qapi_dealloc_visitor_cleanup(qdv);
+    }

 === scripts/qapi-visit.py ===

-Used to generate the visitor functions used to walk through and convert
-a QObject (as provided by QMP) to a native C data structure and
-vice-versa, as well as the visitor function used to dealloc a complex
-schema-defined C type.
+Used to generate the visitor functions used to walk through and
+convert between a native QAPI C data structure and some other format
+(such as QObject); the generated functions are named visit_type_FOO()
+and visit_type_FOO_members().

 The following files are generated:

@@ -848,41 +851,62 @@ Example:

     $ python scripts/qapi-visit.py --output-dir="qapi-generated"
     --prefix="example-" example-schema.json
+    $ cat qapi-generated/example-qapi-visit.h
+[Uninteresting stuff omitted...]
+
+    #ifndef EXAMPLE_QAPI_VISIT_H
+    #define EXAMPLE_QAPI_VISIT_H
+
+[Visitors for built-in types omitted...]
+
+    void visit_type_UserDefOne_members(Visitor *v, UserDefOne *obj, Error **errp);
+    void visit_type_UserDefOne(Visitor *v, const char *name, UserDefOne **obj, Error **errp);
+    void visit_type_UserDefOneList(Visitor *v, const char *name, UserDefOneList **obj, Error **errp);
+
+    #endif
     $ cat qapi-generated/example-qapi-visit.c
 [Uninteresting stuff omitted...]

-    static void visit_type_UserDefOne_fields(Visitor *v, UserDefOne **obj, Error **errp)
+    void visit_type_UserDefOne_members(Visitor *v, UserDefOne *obj, Error **errp)
     {
         Error *err = NULL;

-        visit_type_int(v, &(*obj)->integer, "integer", &err);
+        visit_type_int(v, "integer", &obj->integer, &err);
         if (err) {
             goto out;
         }
-        visit_type_str(v, &(*obj)->string, "string", &err);
-        if (err) {
-            goto out;
-        }
-
-    out:
-        error_propagate(errp, err);
-    }
-
-    void visit_type_UserDefOne(Visitor *v, UserDefOne **obj, const char *name, Error **errp)
-    {
-        Error *err = NULL;
-
-        visit_start_struct(v, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), &err);
-        if (!err) {
-            if (*obj) {
-                visit_type_UserDefOne_fields(v, obj, errp);
+        if (visit_optional(v, "string", &obj->has_string)) {
+            visit_type_str(v, "string", &obj->string, &err);
+            if (err) {
+                goto out;
             }
-            visit_end_struct(v, &err);
         }
+
+    out:
+        error_propagate(errp, err);
+    }
+
+    void visit_type_UserDefOne(Visitor *v, const char *name, UserDefOne **obj, Error **errp)
+    {
+        Error *err = NULL;
+
+        visit_start_struct(v, name, (void **)obj, sizeof(UserDefOne), &err);
+        if (err) {
+            goto out;
+        }
+        if (!*obj) {
+            goto out_obj;
+        }
+        visit_type_UserDefOne_members(v, *obj, &err);
+        error_propagate(errp, err);
+        err = NULL;
+    out_obj:
+        visit_end_struct(v, &err);
+    out:
         error_propagate(errp, err);
     }

-    void visit_type_UserDefOneList(Visitor *v, UserDefOneList **obj, const char *name, Error **errp)
+    void visit_type_UserDefOneList(Visitor *v, const char *name, UserDefOneList **obj, Error **errp)
     {
         Error *err = NULL;
         GenericList *i, **prev;
@@ -893,35 +917,24 @@ Example:
         }

         for (prev = (GenericList **)obj;
-             !err && (i = visit_next_list(v, prev, &err)) != NULL;
+             !err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL;
              prev = &i) {
             UserDefOneList *native_i = (UserDefOneList *)i;
-            visit_type_UserDefOne(v, &native_i->value, NULL, &err);
+            visit_type_UserDefOne(v, NULL, &native_i->value, &err);
         }

-        error_propagate(errp, err);
-        err = NULL;
-        visit_end_list(v, &err);
+        visit_end_list(v);
     out:
         error_propagate(errp, err);
     }
-    $ cat qapi-generated/example-qapi-visit.h
-[Uninteresting stuff omitted...]
-
-    #ifndef EXAMPLE_QAPI_VISIT_H
-    #define EXAMPLE_QAPI_VISIT_H
-
-[Visitors for built-in types omitted...]
-
-    void visit_type_UserDefOne(Visitor *v, UserDefOne **obj, const char *name, Error **errp);
-    void visit_type_UserDefOneList(Visitor *v, UserDefOneList **obj, const char *name, Error **errp);
-
-    #endif

 === scripts/qapi-commands.py ===

-Used to generate the marshaling/dispatch functions for the commands defined
-in the schema. The following files are generated:
+Used to generate the marshaling/dispatch functions for the commands
+defined in the schema. The generated code implements
+qmp_marshal_COMMAND() (mentioned in qmp-commands.hx, and registered
+automatically), and declares qmp_COMMAND() that the user must
+implement.  The following files are generated:

 $(prefix)qmp-marshal.c: command marshal/dispatch functions for each
                         QMP command defined in the schema. Functions
@@ -939,6 +952,19 @@ Example:

     $ python scripts/qapi-commands.py --output-dir="qapi-generated"
     --prefix="example-" example-schema.json
+    $ cat qapi-generated/example-qmp-commands.h
+[Uninteresting stuff omitted...]
+
+    #ifndef EXAMPLE_QMP_COMMANDS_H
+    #define EXAMPLE_QMP_COMMANDS_H
+
+    #include "example-qapi-types.h"
+    #include "qapi/qmp/qdict.h"
+    #include "qapi/error.h"
+
+    UserDefOne *qmp_my_command(UserDefOneList *arg1, Error **errp);
+
+    #endif
     $ cat qapi-generated/example-qmp-marshal.c
 [Uninteresting stuff omitted...]

@@ -950,7 +976,7 @@ Example:
         Visitor *v;

         v = qmp_output_get_visitor(qov);
-        visit_type_UserDefOne(v, &ret_in, "unused", &err);
+        visit_type_UserDefOne(v, "unused", &ret_in, &err);
         if (err) {
             goto out;
         }
@@ -961,7 +987,7 @@ Example:
         qmp_output_visitor_cleanup(qov);
         qdv = qapi_dealloc_visitor_new();
         v = qapi_dealloc_get_visitor(qdv);
-        visit_type_UserDefOne(v, &ret_in, "unused", NULL);
+        visit_type_UserDefOne(v, "unused", &ret_in, NULL);
         qapi_dealloc_visitor_cleanup(qdv);
     }

@@ -972,10 +998,10 @@ Example:
         QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
         QapiDeallocVisitor *qdv;
         Visitor *v;
-        UserDefOne *arg1 = NULL;
+        UserDefOneList *arg1 = NULL;

         v = qmp_input_get_visitor(qiv);
-        visit_type_UserDefOne(v, &arg1, "arg1", &err);
+        visit_type_UserDefOneList(v, "arg1", &arg1, &err);
         if (err) {
             goto out;
         }
@@ -992,7 +1018,7 @@ Example:
         qmp_input_visitor_cleanup(qiv);
         qdv = qapi_dealloc_visitor_new();
         v = qapi_dealloc_get_visitor(qdv);
-        visit_type_UserDefOne(v, &arg1, "arg1", NULL);
+        visit_type_UserDefOneList(v, "arg1", &arg1, NULL);
         qapi_dealloc_visitor_cleanup(qdv);
     }

@@ -1002,24 +1028,12 @@ Example:
     }

     qapi_init(qmp_init_marshal);
-    $ cat qapi-generated/example-qmp-commands.h
-[Uninteresting stuff omitted...]
-
-    #ifndef EXAMPLE_QMP_COMMANDS_H
-    #define EXAMPLE_QMP_COMMANDS_H
-
-    #include "example-qapi-types.h"
-    #include "qapi/qmp/qdict.h"
-    #include "qapi/error.h"
-
-    UserDefOne *qmp_my_command(UserDefOne *arg1, Error **errp);
-
-    #endif

 === scripts/qapi-event.py ===

-Used to generate the event-related C code defined by a schema. The
-following files are created:
+Used to generate the event-related C code defined by a schema, with
+implementations for qapi_event_send_FOO(). The following files are
+created:

 $(prefix)qapi-event.h - Function prototypes for each event type, plus an
                         enumeration of all event names
@@ -1029,6 +1043,27 @@ Example:

     $ python scripts/qapi-event.py --output-dir="qapi-generated"
     --prefix="example-" example-schema.json
+    $ cat qapi-generated/example-qapi-event.h
+[Uninteresting stuff omitted...]
+
+    #ifndef EXAMPLE_QAPI_EVENT_H
+    #define EXAMPLE_QAPI_EVENT_H
+
+    #include "qapi/error.h"
+    #include "qapi/qmp/qdict.h"
+    #include "example-qapi-types.h"
+
+
+    void qapi_event_send_my_event(Error **errp);
+
+    typedef enum example_QAPIEvent {
+        EXAMPLE_QAPI_EVENT_MY_EVENT = 0,
+        EXAMPLE_QAPI_EVENT__MAX = 1,
+    } example_QAPIEvent;
+
+    extern const char *const example_QAPIEvent_lookup[];
+
+    #endif
     $ cat qapi-generated/example-qapi-event.c
 [Uninteresting stuff omitted...]

@@ -1054,27 +1089,6 @@ Example:
         [EXAMPLE_QAPI_EVENT_MY_EVENT] = "MY_EVENT",
         [EXAMPLE_QAPI_EVENT__MAX] = NULL,
     };
-    $ cat qapi-generated/example-qapi-event.h
-[Uninteresting stuff omitted...]
-
-    #ifndef EXAMPLE_QAPI_EVENT_H
-    #define EXAMPLE_QAPI_EVENT_H
-
-    #include "qapi/error.h"
-    #include "qapi/qmp/qdict.h"
-    #include "example-qapi-types.h"
-
-
-    void qapi_event_send_my_event(Error **errp);
-
-    typedef enum example_QAPIEvent {
-        EXAMPLE_QAPI_EVENT_MY_EVENT = 0,
-        EXAMPLE_QAPI_EVENT__MAX = 1,
-    } example_QAPIEvent;
-
-    extern const char *const example_QAPIEvent_lookup[];
-
-    #endif

 === scripts/qapi-introspect.py ===

@@ -1089,6 +1103,15 @@ Example:

     $ python scripts/qapi-introspect.py --output-dir="qapi-generated"
     --prefix="example-" example-schema.json
+    $ cat qapi-generated/example-qmp-introspect.h
+[Uninteresting stuff omitted...]
+
+    #ifndef EXAMPLE_QMP_INTROSPECT_H
+    #define EXAMPLE_QMP_INTROSPECT_H
+
+    extern const char example_qmp_schema_json[];
+
+    #endif
     $ cat qapi-generated/example-qmp-introspect.c
 [Uninteresting stuff omitted...]

@@ -1096,16 +1119,8 @@ Example:
         "{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
         "{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, "
         "{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, "
-        "{\"members\": [{\"name\": \"arg1\", \"type\": \"2\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
-        "{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
+        "{\"members\": [{\"name\": \"arg1\", \"type\": \"[2]\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
+        "{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"default\": null, \"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
+        "{\"element-type\": \"2\", \"meta-type\": \"array\", \"name\": \"[2]\"}, "
         "{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, "
         "{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]";
-    $ cat qapi-generated/example-qmp-introspect.h
-[Uninteresting stuff omitted...]
-
-    #ifndef EXAMPLE_QMP_INTROSPECT_H
-    #define EXAMPLE_QMP_INTROSPECT_H
-
-    extern const char example_qmp_schema_json[];
-
-    #endif
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 04/19] chardev: Shorten references into ChardevBackend
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (2 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 03/19] qapi: Update docs to match recent generator changes Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-02 17:55   ` Markus Armbruster
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 05/19] util: Shorten references into SocketAddress Eric Blake
                   ` (15 subsequent siblings)
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, armbru

An upcoming patch will alter how simple unions, like ChardevBackend,
are laid out, which will impact all lines of the form 'backend->u.XXX'.
To minimize the impact of that patch, use a temporary variable to
reduce the number of lines needing modification when an internal
reference within ChardevBackend changes layout.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-By: Daniel P. Berrange <berrange@redhat.com>

---
v2: add R-b
---
 qemu-char.c | 122 ++++++++++++++++++++++++++++++++----------------------------
 1 file changed, 66 insertions(+), 56 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index fc8ffda..5ea1d34 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -724,7 +724,7 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
     ChardevMux *mux = backend->u.mux;
     CharDriverState *chr, *drv;
     MuxDriver *d;
-    ChardevCommon *common = qapi_ChardevMux_base(backend->u.mux);
+    ChardevCommon *common = qapi_ChardevMux_base(mux);

     drv = qemu_chr_find(mux->chardev);
     if (drv == NULL) {
@@ -1043,7 +1043,7 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
     char *filename_in;
     char *filename_out;
     const char *filename = opts->device;
-    ChardevCommon *common = qapi_ChardevHostdev_base(backend->u.pipe);
+    ChardevCommon *common = qapi_ChardevHostdev_base(opts);


     filename_in = g_strdup_printf("%s.in", filename);
@@ -1123,7 +1123,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
     ChardevStdio *opts = backend->u.stdio;
     CharDriverState *chr;
     struct sigaction act;
-    ChardevCommon *common = qapi_ChardevStdio_base(backend->u.stdio);
+    ChardevCommon *common = qapi_ChardevStdio_base(opts);

     if (is_daemonized()) {
         error_setg(errp, "cannot use stdio with -daemonize");
@@ -2141,7 +2141,7 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
     const char *filename = opts->device;
     CharDriverState *chr;
     WinCharState *s;
-    ChardevCommon *common = qapi_ChardevHostdev_base(backend->u.pipe);
+    ChardevCommon *common = qapi_ChardevHostdev_base(opts);

     chr = qemu_chr_alloc(common, errp);
     if (!chr) {
@@ -3216,7 +3216,7 @@ static CharDriverState *qemu_chr_open_ringbuf(const char *id,
                                               Error **errp)
 {
     ChardevRingbuf *opts = backend->u.ringbuf;
-    ChardevCommon *common = qapi_ChardevRingbuf_base(backend->u.ringbuf);
+    ChardevCommon *common = qapi_ChardevRingbuf_base(opts);
     CharDriverState *chr;
     RingBufCharDriver *d;

@@ -3506,26 +3506,29 @@ static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend,
                                     Error **errp)
 {
     const char *path = qemu_opt_get(opts, "path");
+    ChardevFile *file;

     if (path == NULL) {
         error_setg(errp, "chardev: file: no filename given");
         return;
     }
-    backend->u.file = g_new0(ChardevFile, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevFile_base(backend->u.file));
-    backend->u.file->out = g_strdup(path);
+    file = backend->u.file = g_new0(ChardevFile, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevFile_base(file));
+    file->out = g_strdup(path);

-    backend->u.file->has_append = true;
-    backend->u.file->append = qemu_opt_get_bool(opts, "append", false);
+    file->has_append = true;
+    file->append = qemu_opt_get_bool(opts, "append", false);
 }

 static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend,
                                  Error **errp)
 {
-    backend->u.stdio = g_new0(ChardevStdio, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevStdio_base(backend->u.stdio));
-    backend->u.stdio->has_signal = true;
-    backend->u.stdio->signal = qemu_opt_get_bool(opts, "signal", true);
+    ChardevStdio *stdio;
+
+    stdio = backend->u.stdio = g_new0(ChardevStdio, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevStdio_base(stdio));
+    stdio->has_signal = true;
+    stdio->signal = qemu_opt_get_bool(opts, "signal", true);
 }

 #ifdef HAVE_CHARDEV_SERIAL
@@ -3533,14 +3536,15 @@ static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
                                   Error **errp)
 {
     const char *device = qemu_opt_get(opts, "path");
+    ChardevHostdev *serial;

     if (device == NULL) {
         error_setg(errp, "chardev: serial/tty: no device path given");
         return;
     }
-    backend->u.serial = g_new0(ChardevHostdev, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(backend->u.serial));
-    backend->u.serial->device = g_strdup(device);
+    serial = backend->u.serial = g_new0(ChardevHostdev, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(serial));
+    serial->device = g_strdup(device);
 }
 #endif

@@ -3549,14 +3553,15 @@ static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
                                     Error **errp)
 {
     const char *device = qemu_opt_get(opts, "path");
+    ChardevHostdev *parallel;

     if (device == NULL) {
         error_setg(errp, "chardev: parallel: no device path given");
         return;
     }
-    backend->u.parallel = g_new0(ChardevHostdev, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(backend->u.parallel));
-    backend->u.parallel->device = g_strdup(device);
+    parallel = backend->u.parallel = g_new0(ChardevHostdev, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(parallel));
+    parallel->device = g_strdup(device);
 }
 #endif

@@ -3564,28 +3569,30 @@ static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend,
                                 Error **errp)
 {
     const char *device = qemu_opt_get(opts, "path");
+    ChardevHostdev *dev;

     if (device == NULL) {
         error_setg(errp, "chardev: pipe: no device path given");
         return;
     }
-    backend->u.pipe = g_new0(ChardevHostdev, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(backend->u.pipe));
-    backend->u.pipe->device = g_strdup(device);
+    dev = backend->u.pipe = g_new0(ChardevHostdev, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(dev));
+    dev->device = g_strdup(device);
 }

 static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
                                    Error **errp)
 {
     int val;
+    ChardevRingbuf *ringbuf;

-    backend->u.ringbuf = g_new0(ChardevRingbuf, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevRingbuf_base(backend->u.ringbuf));
+    ringbuf = backend->u.ringbuf = g_new0(ChardevRingbuf, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevRingbuf_base(ringbuf));

     val = qemu_opt_get_size(opts, "size", 0);
     if (val != 0) {
-        backend->u.ringbuf->has_size = true;
-        backend->u.ringbuf->size = val;
+        ringbuf->has_size = true;
+        ringbuf->size = val;
     }
 }

@@ -3593,14 +3600,15 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
                                Error **errp)
 {
     const char *chardev = qemu_opt_get(opts, "chardev");
+    ChardevMux *mux;

     if (chardev == NULL) {
         error_setg(errp, "chardev: mux: no chardev given");
         return;
     }
-    backend->u.mux = g_new0(ChardevMux, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevMux_base(backend->u.mux));
-    backend->u.mux->chardev = g_strdup(chardev);
+    mux = backend->u.mux = g_new0(ChardevMux, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevMux_base(mux));
+    mux->chardev = g_strdup(chardev);
 }

 static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
@@ -3616,6 +3624,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
     const char *port = qemu_opt_get(opts, "port");
     const char *tls_creds = qemu_opt_get(opts, "tls-creds");
     SocketAddress *addr;
+    ChardevSocket *sock;

     if (!path) {
         if (!host) {
@@ -3633,20 +3642,20 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
         }
     }

-    backend->u.socket = g_new0(ChardevSocket, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevSocket_base(backend->u.socket));
+    sock = backend->u.socket = g_new0(ChardevSocket, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevSocket_base(sock));

-    backend->u.socket->has_nodelay = true;
-    backend->u.socket->nodelay = do_nodelay;
-    backend->u.socket->has_server = true;
-    backend->u.socket->server = is_listen;
-    backend->u.socket->has_telnet = true;
-    backend->u.socket->telnet = is_telnet;
-    backend->u.socket->has_wait = true;
-    backend->u.socket->wait = is_waitconnect;
-    backend->u.socket->has_reconnect = true;
-    backend->u.socket->reconnect = reconnect;
-    backend->u.socket->tls_creds = g_strdup(tls_creds);
+    sock->has_nodelay = true;
+    sock->nodelay = do_nodelay;
+    sock->has_server = true;
+    sock->server = is_listen;
+    sock->has_telnet = true;
+    sock->telnet = is_telnet;
+    sock->has_wait = true;
+    sock->wait = is_waitconnect;
+    sock->has_reconnect = true;
+    sock->reconnect = reconnect;
+    sock->tls_creds = g_strdup(tls_creds);

     addr = g_new0(SocketAddress, 1);
     if (path) {
@@ -3665,7 +3674,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
         addr->u.inet->has_ipv6 = qemu_opt_get(opts, "ipv6");
         addr->u.inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0);
     }
-    backend->u.socket->addr = addr;
+    sock->addr = addr;
 }

 static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
@@ -3677,6 +3686,7 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
     const char *localport = qemu_opt_get(opts, "localport");
     bool has_local = false;
     SocketAddress *addr;
+    ChardevUdp *udp;

     if (host == NULL || strlen(host) == 0) {
         host = "localhost";
@@ -3696,8 +3706,8 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
         has_local = true;
     }

-    backend->u.udp = g_new0(ChardevUdp, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevUdp_base(backend->u.udp));
+    udp = backend->u.udp = g_new0(ChardevUdp, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevUdp_base(udp));

     addr = g_new0(SocketAddress, 1);
     addr->type = SOCKET_ADDRESS_KIND_INET;
@@ -3708,16 +3718,16 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
     addr->u.inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0);
     addr->u.inet->has_ipv6 = qemu_opt_get(opts, "ipv6");
     addr->u.inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0);
-    backend->u.udp->remote = addr;
+    udp->remote = addr;

     if (has_local) {
-        backend->u.udp->has_local = true;
+        udp->has_local = true;
         addr = g_new0(SocketAddress, 1);
         addr->type = SOCKET_ADDRESS_KIND_INET;
         addr->u.inet = g_new0(InetSocketAddress, 1);
         addr->u.inet->host = g_strdup(localaddr);
         addr->u.inet->port = g_strdup(localport);
-        backend->u.udp->local = addr;
+        udp->local = addr;
     }
 }

@@ -4128,7 +4138,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
                                               Error **errp)
 {
     ChardevFile *file = backend->u.file;
-    ChardevCommon *common = qapi_ChardevFile_base(backend->u.file);
+    ChardevCommon *common = qapi_ChardevFile_base(file);
     HANDLE out;

     if (file->has_in) {
@@ -4151,7 +4161,7 @@ static CharDriverState *qmp_chardev_open_serial(const char *id,
                                                 Error **errp)
 {
     ChardevHostdev *serial = backend->u.serial;
-    ChardevCommon *common = qapi_ChardevHostdev_base(backend->u.serial);
+    ChardevCommon *common = qapi_ChardevHostdev_base(serial);
     return qemu_chr_open_win_path(serial->device, common, errp);
 }

@@ -4175,7 +4185,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
                                               Error **errp)
 {
     ChardevFile *file = backend->u.file;
-    ChardevCommon *common = qapi_ChardevFile_base(backend->u.file);
+    ChardevCommon *common = qapi_ChardevFile_base(file);
     int flags, in = -1, out;

     flags = O_WRONLY | O_CREAT | O_BINARY;
@@ -4209,7 +4219,7 @@ static CharDriverState *qmp_chardev_open_serial(const char *id,
                                                 Error **errp)
 {
     ChardevHostdev *serial = backend->u.serial;
-    ChardevCommon *common = qapi_ChardevHostdev_base(backend->u.serial);
+    ChardevCommon *common = qapi_ChardevHostdev_base(serial);
     int fd;

     fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
@@ -4228,7 +4238,7 @@ static CharDriverState *qmp_chardev_open_parallel(const char *id,
                                                   Error **errp)
 {
     ChardevHostdev *parallel = backend->u.parallel;
-    ChardevCommon *common = qapi_ChardevHostdev_base(backend->u.parallel);
+    ChardevCommon *common = qapi_ChardevHostdev_base(parallel);
     int fd;

     fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
@@ -4280,7 +4290,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     bool is_telnet      = sock->has_telnet  ? sock->telnet  : false;
     bool is_waitconnect = sock->has_wait    ? sock->wait    : false;
     int64_t reconnect   = sock->has_reconnect ? sock->reconnect : 0;
-    ChardevCommon *common = qapi_ChardevSocket_base(backend->u.socket);
+    ChardevCommon *common = qapi_ChardevSocket_base(sock);

     chr = qemu_chr_alloc(common, errp);
     if (!chr) {
@@ -4380,7 +4390,7 @@ static CharDriverState *qmp_chardev_open_udp(const char *id,
                                              Error **errp)
 {
     ChardevUdp *udp = backend->u.udp;
-    ChardevCommon *common = qapi_ChardevUdp_base(backend->u.udp);
+    ChardevCommon *common = qapi_ChardevUdp_base(udp);
     QIOChannelSocket *sioc = qio_channel_socket_new();

     if (qio_channel_socket_dgram_sync(sioc,
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 05/19] util: Shorten references into SocketAddress
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (3 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 04/19] chardev: Shorten references into ChardevBackend Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-02 18:03   ` Markus Armbruster
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 06/19] ui: Shorten references into InputEvent Eric Blake
                   ` (14 subsequent siblings)
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, open list:Block layer core, armbru, Gerd Hoffmann,
	Paolo Bonzini

An upcoming patch will alter how simple unions, like SocketAddress,
are laid out, which will impact all lines of the form 'addr->u.XXX'.
To minimize the impact of that patch, use C99 initialization or a
temporary variable to reduce the number of lines needing modification
when an internal reference within SocketAddress changes layout.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>

---
v2: add R-b
---
 block/nbd.c                    | 14 ++++++++------
 qemu-char.c                    | 43 ++++++++++++++++++++++++------------------
 qemu-nbd.c                     |  9 +++++----
 tests/test-io-channel-socket.c | 26 ++++++++++++++++---------
 ui/vnc.c                       | 39 +++++++++++++++++++-------------------
 util/qemu-sockets.c            | 11 ++++++-----
 6 files changed, 81 insertions(+), 61 deletions(-)

diff --git a/block/nbd.c b/block/nbd.c
index db57b49..9f333c9 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -204,18 +204,20 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, char **export,
     saddr = g_new0(SocketAddress, 1);

     if (qdict_haskey(options, "path")) {
+        UnixSocketAddress *q_unix;
         saddr->type = SOCKET_ADDRESS_KIND_UNIX;
-        saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
-        saddr->u.q_unix->path = g_strdup(qdict_get_str(options, "path"));
+        q_unix = saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
+        q_unix->path = g_strdup(qdict_get_str(options, "path"));
         qdict_del(options, "path");
     } else {
+        InetSocketAddress *inet;
         saddr->type = SOCKET_ADDRESS_KIND_INET;
-        saddr->u.inet = g_new0(InetSocketAddress, 1);
-        saddr->u.inet->host = g_strdup(qdict_get_str(options, "host"));
+        inet = saddr->u.inet = g_new0(InetSocketAddress, 1);
+        inet->host = g_strdup(qdict_get_str(options, "host"));
         if (!qdict_get_try_str(options, "port")) {
-            saddr->u.inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
+            inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
         } else {
-            saddr->u.inet->port = g_strdup(qdict_get_str(options, "port"));
+            inet->port = g_strdup(qdict_get_str(options, "port"));
         }
         qdict_del(options, "host");
         qdict_del(options, "port");
diff --git a/qemu-char.c b/qemu-char.c
index 5ea1d34..cfc82bc 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -3659,20 +3659,23 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,

     addr = g_new0(SocketAddress, 1);
     if (path) {
+        UnixSocketAddress *q_unix;
         addr->type = SOCKET_ADDRESS_KIND_UNIX;
-        addr->u.q_unix = g_new0(UnixSocketAddress, 1);
-        addr->u.q_unix->path = g_strdup(path);
+        q_unix = addr->u.q_unix = g_new0(UnixSocketAddress, 1);
+        q_unix->path = g_strdup(path);
     } else {
         addr->type = SOCKET_ADDRESS_KIND_INET;
         addr->u.inet = g_new0(InetSocketAddress, 1);
-        addr->u.inet->host = g_strdup(host);
-        addr->u.inet->port = g_strdup(port);
-        addr->u.inet->has_to = qemu_opt_get(opts, "to");
-        addr->u.inet->to = qemu_opt_get_number(opts, "to", 0);
-        addr->u.inet->has_ipv4 = qemu_opt_get(opts, "ipv4");
-        addr->u.inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0);
-        addr->u.inet->has_ipv6 = qemu_opt_get(opts, "ipv6");
-        addr->u.inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0);
+        *addr->u.inet = (InetSocketAddress) {
+            .host = g_strdup(host),
+            .port = g_strdup(port),
+            .has_to = qemu_opt_get(opts, "to"),
+            .to = qemu_opt_get_number(opts, "to", 0),
+            .has_ipv4 = qemu_opt_get(opts, "ipv4"),
+            .ipv4 = qemu_opt_get_bool(opts, "ipv4", 0),
+            .has_ipv6 = qemu_opt_get(opts, "ipv6"),
+            .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
+        };
     }
     sock->addr = addr;
 }
@@ -3712,12 +3715,14 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
     addr = g_new0(SocketAddress, 1);
     addr->type = SOCKET_ADDRESS_KIND_INET;
     addr->u.inet = g_new0(InetSocketAddress, 1);
-    addr->u.inet->host = g_strdup(host);
-    addr->u.inet->port = g_strdup(port);
-    addr->u.inet->has_ipv4 = qemu_opt_get(opts, "ipv4");
-    addr->u.inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0);
-    addr->u.inet->has_ipv6 = qemu_opt_get(opts, "ipv6");
-    addr->u.inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0);
+    *addr->u.inet = (InetSocketAddress) {
+        .host = g_strdup(host),
+        .port = g_strdup(port),
+        .has_ipv4 = qemu_opt_get(opts, "ipv4"),
+        .ipv4 = qemu_opt_get_bool(opts, "ipv4", 0),
+        .has_ipv6 = qemu_opt_get(opts, "ipv6"),
+        .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
+    };
     udp->remote = addr;

     if (has_local) {
@@ -3725,8 +3730,10 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
         addr = g_new0(SocketAddress, 1);
         addr->type = SOCKET_ADDRESS_KIND_INET;
         addr->u.inet = g_new0(InetSocketAddress, 1);
-        addr->u.inet->host = g_strdup(localaddr);
-        addr->u.inet->port = g_strdup(localport);
+        *addr->u.inet = (InetSocketAddress) {
+            .host = g_strdup(localaddr),
+            .port = g_strdup(localport),
+        };
         udp->local = addr;
     }
 }
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 5fe94d0..a5c1d95 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -380,13 +380,14 @@ static SocketAddress *nbd_build_socket_address(const char *sockpath,
         saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
         saddr->u.q_unix->path = g_strdup(sockpath);
     } else {
+        InetSocketAddress *inet;
         saddr->type = SOCKET_ADDRESS_KIND_INET;
-        saddr->u.inet = g_new0(InetSocketAddress, 1);
-        saddr->u.inet->host = g_strdup(bindto);
+        inet = saddr->u.inet = g_new0(InetSocketAddress, 1);
+        inet->host = g_strdup(bindto);
         if (port) {
-            saddr->u.inet->port = g_strdup(port);
+            inet->port = g_strdup(port);
         } else  {
-            saddr->u.inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
+            inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
         }
     }

diff --git a/tests/test-io-channel-socket.c b/tests/test-io-channel-socket.c
index 0697363..afe46d5 100644
--- a/tests/test-io-channel-socket.c
+++ b/tests/test-io-channel-socket.c
@@ -1,7 +1,7 @@
 /*
  * QEMU I/O channel sockets test
  *
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -284,13 +284,17 @@ static void test_io_channel_ipv4(bool async)

     listen_addr->type = SOCKET_ADDRESS_KIND_INET;
     listen_addr->u.inet = g_new0(InetSocketAddress, 1);
-    listen_addr->u.inet->host = g_strdup("127.0.0.1");
-    listen_addr->u.inet->port = NULL; /* Auto-select */
+    *listen_addr->u.inet = (InetSocketAddress) {
+        .host = g_strdup("127.0.0.1"),
+        .port = NULL, /* Auto-select */
+    };

     connect_addr->type = SOCKET_ADDRESS_KIND_INET;
     connect_addr->u.inet = g_new0(InetSocketAddress, 1);
-    connect_addr->u.inet->host = g_strdup("127.0.0.1");
-    connect_addr->u.inet->port = NULL; /* Filled in later */
+    *connect_addr->u.inet = (InetSocketAddress) {
+        .host = g_strdup("127.0.0.1"),
+        .port = NULL, /* Filled in later */
+    };

     test_io_channel(async, listen_addr, connect_addr, false);

@@ -318,13 +322,17 @@ static void test_io_channel_ipv6(bool async)

     listen_addr->type = SOCKET_ADDRESS_KIND_INET;
     listen_addr->u.inet = g_new0(InetSocketAddress, 1);
-    listen_addr->u.inet->host = g_strdup("::1");
-    listen_addr->u.inet->port = NULL; /* Auto-select */
+    *listen_addr->u.inet = (InetSocketAddress) {
+        .host = g_strdup("::1"),
+        .port = NULL, /* Auto-select */
+    };

     connect_addr->type = SOCKET_ADDRESS_KIND_INET;
     connect_addr->u.inet = g_new0(InetSocketAddress, 1);
-    connect_addr->u.inet->host = g_strdup("::1");
-    connect_addr->u.inet->port = NULL; /* Filled in later */
+    *connect_addr->u.inet = (InetSocketAddress) {
+        .host = g_strdup("::1"),
+        .port = NULL, /* Filled in later */
+    };

     test_io_channel(async, listen_addr, connect_addr, false);

diff --git a/ui/vnc.c b/ui/vnc.c
index b6bbea5..b955e4e 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -3530,12 +3530,13 @@ void vnc_display_open(const char *id, Error **errp)
             }
         } else {
             unsigned long long baseport;
+            InetSocketAddress *inet;
             saddr->type = SOCKET_ADDRESS_KIND_INET;
-            saddr->u.inet = g_new0(InetSocketAddress, 1);
+            inet = saddr->u.inet = g_new0(InetSocketAddress, 1);
             if (vnc[0] == '[' && vnc[hlen - 1] == ']') {
-                saddr->u.inet->host = g_strndup(vnc + 1, hlen - 2);
+                inet->host = g_strndup(vnc + 1, hlen - 2);
             } else {
-                saddr->u.inet->host = g_strndup(vnc, hlen);
+                inet->host = g_strndup(vnc, hlen);
             }
             if (parse_uint_full(h + 1, &baseport, 10) < 0) {
                 error_setg(errp, "can't convert to a number: %s", h + 1);
@@ -3546,32 +3547,32 @@ void vnc_display_open(const char *id, Error **errp)
                 error_setg(errp, "port %s out of range", h + 1);
                 goto fail;
             }
-            saddr->u.inet->port = g_strdup_printf(
+            inet->port = g_strdup_printf(
                 "%d", (int)baseport + 5900);

             if (to) {
-                saddr->u.inet->has_to = true;
-                saddr->u.inet->to = to + 5900;
+                inet->has_to = true;
+                inet->to = to + 5900;
             }
-            saddr->u.inet->ipv4 = ipv4;
-            saddr->u.inet->has_ipv4 = has_ipv4;
-            saddr->u.inet->ipv6 = ipv6;
-            saddr->u.inet->has_ipv6 = has_ipv6;
+            inet->ipv4 = ipv4;
+            inet->has_ipv4 = has_ipv4;
+            inet->ipv6 = ipv6;
+            inet->has_ipv6 = has_ipv6;

             if (vs->ws_enabled) {
                 wsaddr->type = SOCKET_ADDRESS_KIND_INET;
-                wsaddr->u.inet = g_new0(InetSocketAddress, 1);
-                wsaddr->u.inet->host = g_strdup(saddr->u.inet->host);
-                wsaddr->u.inet->port = g_strdup(websocket);
+                inet = wsaddr->u.inet = g_new0(InetSocketAddress, 1);
+                inet->host = g_strdup(saddr->u.inet->host);
+                inet->port = g_strdup(websocket);

                 if (to) {
-                    wsaddr->u.inet->has_to = true;
-                    wsaddr->u.inet->to = to;
+                    inet->has_to = true;
+                    inet->to = to;
                 }
-                wsaddr->u.inet->ipv4 = ipv4;
-                wsaddr->u.inet->has_ipv4 = has_ipv4;
-                wsaddr->u.inet->ipv6 = ipv6;
-                wsaddr->u.inet->has_ipv6 = has_ipv6;
+                inet->ipv4 = ipv4;
+                inet->has_ipv4 = has_ipv4;
+                inet->ipv6 = ipv6;
+                inet->has_ipv6 = has_ipv6;
             }
         }
     } else {
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 557da20..ad7c00c 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -1003,6 +1003,7 @@ socket_sockaddr_to_address_inet(struct sockaddr_storage *sa,
     char host[NI_MAXHOST];
     char serv[NI_MAXSERV];
     SocketAddress *addr;
+    InetSocketAddress *inet;
     int ret;

     ret = getnameinfo((struct sockaddr *)sa, salen,
@@ -1017,13 +1018,13 @@ socket_sockaddr_to_address_inet(struct sockaddr_storage *sa,

     addr = g_new0(SocketAddress, 1);
     addr->type = SOCKET_ADDRESS_KIND_INET;
-    addr->u.inet = g_new0(InetSocketAddress, 1);
-    addr->u.inet->host = g_strdup(host);
-    addr->u.inet->port = g_strdup(serv);
+    inet = addr->u.inet = g_new0(InetSocketAddress, 1);
+    inet->host = g_strdup(host);
+    inet->port = g_strdup(serv);
     if (sa->ss_family == AF_INET) {
-        addr->u.inet->has_ipv4 = addr->u.inet->ipv4 = true;
+        inet->has_ipv4 = inet->ipv4 = true;
     } else {
-        addr->u.inet->has_ipv6 = addr->u.inet->ipv6 = true;
+        inet->has_ipv6 = inet->ipv6 = true;
     }

     return addr;
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 06/19] ui: Shorten references into InputEvent
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (4 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 05/19] util: Shorten references into SocketAddress Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-01 15:32   ` [Qemu-devel] [PATCH v2.5 " Eric Blake
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 07/19] qapi: Avoid use of 'data' member of qapi unions Eric Blake
                   ` (13 subsequent siblings)
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, Gerd Hoffmann, armbru, Michael S. Tsirkin

An upcoming patch will alter how simple unions, like InputEvent,
are laid out, which will impact all lines of the form 'evt->u.XXX'.
To minimize the impact of that patch, use a temporary variable to
reduce the number of lines needing modification when an internal
reference within InputEvent changes layout.

There was one instance in hid.c:hid_pointer_event() where the code
was referring to evt->u.rel inside the case label where evt->u.abs
is the correct name; thankfully, both members of the union have the
same type, so it happened to work, but it is now cleaner.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-By: Daniel P. Berrange <berrange@redhat.com>

---
v2: add R-b
---
 hw/char/escc.c              | 12 +++++-----
 hw/input/hid.c              | 36 +++++++++++++++++-------------
 hw/input/ps2.c              | 27 ++++++++++++++---------
 hw/input/virtio-input-hid.c | 33 ++++++++++++++++-----------
 replay/replay-input.c       | 31 ++++++++++++++++----------
 ui/input-legacy.c           | 26 +++++++++++++---------
 ui/input.c                  | 54 ++++++++++++++++++++++++++-------------------
 7 files changed, 130 insertions(+), 89 deletions(-)

diff --git a/hw/char/escc.c b/hw/char/escc.c
index 98a1c21..c7a24ac 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -842,14 +842,16 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
 {
     ChannelState *s = (ChannelState *)dev;
     int qcode, keycode;
+    InputKeyEvent *key;

     assert(evt->type == INPUT_EVENT_KIND_KEY);
-    qcode = qemu_input_key_value_to_qcode(evt->u.key->key);
+    key = evt->u.key;
+    qcode = qemu_input_key_value_to_qcode(key->key);
     trace_escc_sunkbd_event_in(qcode, QKeyCode_lookup[qcode],
-                               evt->u.key->down);
+                               key->down);

     if (qcode == Q_KEY_CODE_CAPS_LOCK) {
-        if (evt->u.key->down) {
+        if (key->down) {
             s->caps_lock_mode ^= 1;
             if (s->caps_lock_mode == 2) {
                 return; /* Drop second press */
@@ -863,7 +865,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
     }

     if (qcode == Q_KEY_CODE_NUM_LOCK) {
-        if (evt->u.key->down) {
+        if (key->down) {
             s->num_lock_mode ^= 1;
             if (s->num_lock_mode == 2) {
                 return; /* Drop second press */
@@ -877,7 +879,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
     }

     keycode = qcode_to_keycode[qcode];
-    if (!evt->u.key->down) {
+    if (!key->down) {
         keycode |= 0x80;
     }
     trace_escc_sunkbd_event_out(keycode);
diff --git a/hw/input/hid.c b/hw/input/hid.c
index b41efbb..5db4d68 100644
--- a/hw/input/hid.c
+++ b/hw/input/hid.c
@@ -116,37 +116,42 @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
     };
     HIDState *hs = (HIDState *)dev;
     HIDPointerEvent *e;
+    InputMoveEvent *move;
+    InputBtnEvent *btn;

     assert(hs->n < QUEUE_LENGTH);
     e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];

     switch (evt->type) {
     case INPUT_EVENT_KIND_REL:
-        if (evt->u.rel->axis == INPUT_AXIS_X) {
-            e->xdx += evt->u.rel->value;
-        } else if (evt->u.rel->axis == INPUT_AXIS_Y) {
-            e->ydy += evt->u.rel->value;
+        move = evt->u.rel;
+        if (move->axis == INPUT_AXIS_X) {
+            e->xdx += move->value;
+        } else if (move->axis == INPUT_AXIS_Y) {
+            e->ydy += move->value;
         }
         break;

     case INPUT_EVENT_KIND_ABS:
-        if (evt->u.rel->axis == INPUT_AXIS_X) {
-            e->xdx = evt->u.rel->value;
-        } else if (evt->u.rel->axis == INPUT_AXIS_Y) {
-            e->ydy = evt->u.rel->value;
+        move = evt->u.abs;
+        if (move->axis == INPUT_AXIS_X) {
+            e->xdx = move->value;
+        } else if (move->axis == INPUT_AXIS_Y) {
+            e->ydy = move->value;
         }
         break;

     case INPUT_EVENT_KIND_BTN:
-        if (evt->u.btn->down) {
-            e->buttons_state |= bmap[evt->u.btn->button];
-            if (evt->u.btn->button == INPUT_BUTTON_WHEELUP) {
+        btn = evt->u.btn;
+        if (btn->down) {
+            e->buttons_state |= bmap[btn->button];
+            if (btn->button == INPUT_BUTTON_WHEELUP) {
                 e->dz--;
-            } else if (evt->u.btn->button == INPUT_BUTTON_WHEELDOWN) {
+            } else if (btn->button == INPUT_BUTTON_WHEELDOWN) {
                 e->dz++;
             }
         } else {
-            e->buttons_state &= ~bmap[evt->u.btn->button];
+            e->buttons_state &= ~bmap[btn->button];
         }
         break;

@@ -223,9 +228,10 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
     HIDState *hs = (HIDState *)dev;
     int scancodes[3], i, count;
     int slot;
+    InputKeyEvent *key = evt->u.key;

-    count = qemu_input_key_value_to_scancode(evt->u.key->key,
-                                             evt->u.key->down,
+    count = qemu_input_key_value_to_scancode(key->key,
+                                             key->down,
                                              scancodes);
     if (hs->n + count > QUEUE_LENGTH) {
         fprintf(stderr, "usb-kbd: warning: key event queue full\n");
diff --git a/hw/input/ps2.c b/hw/input/ps2.c
index b6f0e8d..7bab7c2 100644
--- a/hw/input/ps2.c
+++ b/hw/input/ps2.c
@@ -182,10 +182,11 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
 {
     PS2KbdState *s = (PS2KbdState *)dev;
     int scancodes[3], i, count;
+    InputKeyEvent *key = evt->u.key;

     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
-    count = qemu_input_key_value_to_scancode(evt->u.key->key,
-                                             evt->u.key->down,
+    count = qemu_input_key_value_to_scancode(key->key,
+                                             key->down,
                                              scancodes);
     for (i = 0; i < count; i++) {
         ps2_put_keycode(s, scancodes[i]);
@@ -389,6 +390,8 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
         [INPUT_BUTTON_RIGHT]  = MOUSE_EVENT_RBUTTON,
     };
     PS2MouseState *s = (PS2MouseState *)dev;
+    InputMoveEvent *move;
+    InputBtnEvent *btn;

     /* check if deltas are recorded when disabled */
     if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
@@ -396,23 +399,25 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,

     switch (evt->type) {
     case INPUT_EVENT_KIND_REL:
-        if (evt->u.rel->axis == INPUT_AXIS_X) {
-            s->mouse_dx += evt->u.rel->value;
-        } else if (evt->u.rel->axis == INPUT_AXIS_Y) {
-            s->mouse_dy -= evt->u.rel->value;
+        move = evt->u.rel;
+        if (move->axis == INPUT_AXIS_X) {
+            s->mouse_dx += move->value;
+        } else if (move->axis == INPUT_AXIS_Y) {
+            s->mouse_dy -= move->value;
         }
         break;

     case INPUT_EVENT_KIND_BTN:
-        if (evt->u.btn->down) {
-            s->mouse_buttons |= bmap[evt->u.btn->button];
-            if (evt->u.btn->button == INPUT_BUTTON_WHEELUP) {
+        btn = evt->u.btn;
+        if (btn->down) {
+            s->mouse_buttons |= bmap[btn->button];
+            if (btn->button == INPUT_BUTTON_WHEELUP) {
                 s->mouse_dz--;
-            } else if (evt->u.btn->button == INPUT_BUTTON_WHEELDOWN) {
+            } else if (btn->button == INPUT_BUTTON_WHEELDOWN) {
                 s->mouse_dz++;
             }
         } else {
-            s->mouse_buttons &= ~bmap[evt->u.btn->button];
+            s->mouse_buttons &= ~bmap[btn->button];
         }
         break;

diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c
index c4af0be..7053f75 100644
--- a/hw/input/virtio-input-hid.c
+++ b/hw/input/virtio-input-hid.c
@@ -191,46 +191,53 @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src,
     VirtIOInput *vinput = VIRTIO_INPUT(dev);
     virtio_input_event event;
     int qcode;
+    InputKeyEvent *key;
+    InputMoveEvent *move;
+    InputBtnEvent *btn;

     switch (evt->type) {
     case INPUT_EVENT_KIND_KEY:
-        qcode = qemu_input_key_value_to_qcode(evt->u.key->key);
+        key = evt->u.key;
+        qcode = qemu_input_key_value_to_qcode(key->key);
         if (qcode && keymap_qcode[qcode]) {
             event.type  = cpu_to_le16(EV_KEY);
             event.code  = cpu_to_le16(keymap_qcode[qcode]);
-            event.value = cpu_to_le32(evt->u.key->down ? 1 : 0);
+            event.value = cpu_to_le32(key->down ? 1 : 0);
             virtio_input_send(vinput, &event);
         } else {
-            if (evt->u.key->down) {
+            if (key->down) {
                 fprintf(stderr, "%s: unmapped key: %d [%s]\n", __func__,
                         qcode, QKeyCode_lookup[qcode]);
             }
         }
         break;
     case INPUT_EVENT_KIND_BTN:
-        if (keymap_button[evt->u.btn->button]) {
+        btn = evt->u.btn;
+        if (keymap_button[btn->button]) {
             event.type  = cpu_to_le16(EV_KEY);
-            event.code  = cpu_to_le16(keymap_button[evt->u.btn->button]);
-            event.value = cpu_to_le32(evt->u.btn->down ? 1 : 0);
+            event.code  = cpu_to_le16(keymap_button[btn->button]);
+            event.value = cpu_to_le32(btn->down ? 1 : 0);
             virtio_input_send(vinput, &event);
         } else {
-            if (evt->u.btn->down) {
+            if (btn->down) {
                 fprintf(stderr, "%s: unmapped button: %d [%s]\n", __func__,
-                        evt->u.btn->button,
-                        InputButton_lookup[evt->u.btn->button]);
+                        btn->button,
+                        InputButton_lookup[btn->button]);
             }
         }
         break;
     case INPUT_EVENT_KIND_REL:
+        move = evt->u.rel;
         event.type  = cpu_to_le16(EV_REL);
-        event.code  = cpu_to_le16(axismap_rel[evt->u.rel->axis]);
-        event.value = cpu_to_le32(evt->u.rel->value);
+        event.code  = cpu_to_le16(axismap_rel[move->axis]);
+        event.value = cpu_to_le32(move->value);
         virtio_input_send(vinput, &event);
         break;
     case INPUT_EVENT_KIND_ABS:
+        move = evt->u.abs;
         event.type  = cpu_to_le16(EV_ABS);
-        event.code  = cpu_to_le16(axismap_abs[evt->u.abs->axis]);
-        event.value = cpu_to_le32(evt->u.abs->value);
+        event.code  = cpu_to_le16(axismap_abs[move->axis]);
+        event.value = cpu_to_le32(move->value);
         virtio_input_send(vinput, &event);
         break;
     default:
diff --git a/replay/replay-input.c b/replay/replay-input.c
index 93616be..c38af50 100644
--- a/replay/replay-input.c
+++ b/replay/replay-input.c
@@ -47,20 +47,24 @@ static InputEvent *qapi_clone_InputEvent(InputEvent *src)

 void replay_save_input_event(InputEvent *evt)
 {
+    InputKeyEvent *key;
+    InputBtnEvent *btn;
+    InputMoveEvent *move;
     replay_put_dword(evt->type);

     switch (evt->type) {
     case INPUT_EVENT_KIND_KEY:
-        replay_put_dword(evt->u.key->key->type);
+        key = evt->u.key;
+        replay_put_dword(key->key->type);

-        switch (evt->u.key->key->type) {
+        switch (key->key->type) {
         case KEY_VALUE_KIND_NUMBER:
-            replay_put_qword(evt->u.key->key->u.number);
-            replay_put_byte(evt->u.key->down);
+            replay_put_qword(key->key->u.number);
+            replay_put_byte(key->down);
             break;
         case KEY_VALUE_KIND_QCODE:
-            replay_put_dword(evt->u.key->key->u.qcode);
-            replay_put_byte(evt->u.key->down);
+            replay_put_dword(key->key->u.qcode);
+            replay_put_byte(key->down);
             break;
         case KEY_VALUE_KIND__MAX:
             /* keep gcc happy */
@@ -68,16 +72,19 @@ void replay_save_input_event(InputEvent *evt)
         }
         break;
     case INPUT_EVENT_KIND_BTN:
-        replay_put_dword(evt->u.btn->button);
-        replay_put_byte(evt->u.btn->down);
+        btn = evt->u.btn;
+        replay_put_dword(btn->button);
+        replay_put_byte(btn->down);
         break;
     case INPUT_EVENT_KIND_REL:
-        replay_put_dword(evt->u.rel->axis);
-        replay_put_qword(evt->u.rel->value);
+        move = evt->u.rel;
+        replay_put_dword(move->axis);
+        replay_put_qword(move->value);
         break;
     case INPUT_EVENT_KIND_ABS:
-        replay_put_dword(evt->u.abs->axis);
-        replay_put_qword(evt->u.abs->value);
+        move = evt->u.abs;
+        replay_put_dword(move->axis);
+        replay_put_qword(move->value);
         break;
     case INPUT_EVENT_KIND__MAX:
         /* keep gcc happy */
diff --git a/ui/input-legacy.c b/ui/input-legacy.c
index c97eac1..5119fe8 100644
--- a/ui/input-legacy.c
+++ b/ui/input-legacy.c
@@ -110,12 +110,13 @@ static void legacy_kbd_event(DeviceState *dev, QemuConsole *src,
 {
     QEMUPutKbdEntry *entry = (QEMUPutKbdEntry *)dev;
     int scancodes[3], i, count;
+    InputKeyEvent *key = evt->u.key;

     if (!entry || !entry->put_kbd) {
         return;
     }
-    count = qemu_input_key_value_to_scancode(evt->u.key->key,
-                                             evt->u.key->down,
+    count = qemu_input_key_value_to_scancode(key->key,
+                                             key->down,
                                              scancodes);
     for (i = 0; i < count; i++) {
         entry->put_kbd(entry->opaque, scancodes[i]);
@@ -150,23 +151,26 @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src,
         [INPUT_BUTTON_RIGHT]  = MOUSE_EVENT_RBUTTON,
     };
     QEMUPutMouseEntry *s = (QEMUPutMouseEntry *)dev;
+    InputBtnEvent *btn;
+    InputMoveEvent *move;

     switch (evt->type) {
     case INPUT_EVENT_KIND_BTN:
-        if (evt->u.btn->down) {
-            s->buttons |= bmap[evt->u.btn->button];
+        btn = evt->u.btn;
+        if (btn->down) {
+            s->buttons |= bmap[btn->button];
         } else {
-            s->buttons &= ~bmap[evt->u.btn->button];
+            s->buttons &= ~bmap[btn->button];
         }
-        if (evt->u.btn->down && evt->u.btn->button == INPUT_BUTTON_WHEELUP) {
+        if (btn->down && btn->button == INPUT_BUTTON_WHEELUP) {
             s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque,
                                     s->axis[INPUT_AXIS_X],
                                     s->axis[INPUT_AXIS_Y],
                                     -1,
                                     s->buttons);
         }
-        if (evt->u.btn->down &&
-            evt->u.btn->button == INPUT_BUTTON_WHEELDOWN) {
+        if (btn->down &&
+            btn->button == INPUT_BUTTON_WHEELDOWN) {
             s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque,
                                     s->axis[INPUT_AXIS_X],
                                     s->axis[INPUT_AXIS_Y],
@@ -175,10 +179,12 @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src,
         }
         break;
     case INPUT_EVENT_KIND_ABS:
-        s->axis[evt->u.abs->axis] = evt->u.abs->value;
+        move = evt->u.abs;
+        s->axis[move->axis] = move->value;
         break;
     case INPUT_EVENT_KIND_REL:
-        s->axis[evt->u.rel->axis] += evt->u.rel->value;
+        move = evt->u.rel;
+        s->axis[move->axis] += move->value;
         break;
     default:
         break;
diff --git a/ui/input.c b/ui/input.c
index bdcb974..e15c618 100644
--- a/ui/input.c
+++ b/ui/input.c
@@ -168,24 +168,25 @@ void qmp_x_input_send_event(bool has_console, int64_t console,

 static void qemu_input_transform_abs_rotate(InputEvent *evt)
 {
+    InputMoveEvent *move = evt->u.abs;
     switch (graphic_rotate) {
     case 90:
-        if (evt->u.abs->axis == INPUT_AXIS_X) {
-            evt->u.abs->axis = INPUT_AXIS_Y;
-        } else if (evt->u.abs->axis == INPUT_AXIS_Y) {
-            evt->u.abs->axis = INPUT_AXIS_X;
-            evt->u.abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->u.abs->value;
+        if (move->axis == INPUT_AXIS_X) {
+            move->axis = INPUT_AXIS_Y;
+        } else if (move->axis == INPUT_AXIS_Y) {
+            move->axis = INPUT_AXIS_X;
+            move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value;
         }
         break;
     case 180:
-        evt->u.abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->u.abs->value;
+        move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value;
         break;
     case 270:
-        if (evt->u.abs->axis == INPUT_AXIS_X) {
-            evt->u.abs->axis = INPUT_AXIS_Y;
-            evt->u.abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->u.abs->value;
-        } else if (evt->u.abs->axis == INPUT_AXIS_Y) {
-            evt->u.abs->axis = INPUT_AXIS_X;
+        if (move->axis == INPUT_AXIS_X) {
+            move->axis = INPUT_AXIS_Y;
+            move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value;
+        } else if (move->axis == INPUT_AXIS_Y) {
+            move->axis = INPUT_AXIS_X;
         }
         break;
     }
@@ -195,22 +196,26 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
 {
     const char *name;
     int qcode, idx = -1;
+    InputKeyEvent *key;
+    InputBtnEvent *btn;
+    InputMoveEvent *move;

     if (src) {
         idx = qemu_console_get_index(src);
     }
     switch (evt->type) {
     case INPUT_EVENT_KIND_KEY:
-        switch (evt->u.key->key->type) {
+        key = evt->u.key;
+        switch (key->key->type) {
         case KEY_VALUE_KIND_NUMBER:
-            qcode = qemu_input_key_number_to_qcode(evt->u.key->key->u.number);
+            qcode = qemu_input_key_number_to_qcode(key->key->u.number);
             name = QKeyCode_lookup[qcode];
-            trace_input_event_key_number(idx, evt->u.key->key->u.number,
-                                         name, evt->u.key->down);
+            trace_input_event_key_number(idx, key->key->u.number,
+                                         name, key->down);
             break;
         case KEY_VALUE_KIND_QCODE:
-            name = QKeyCode_lookup[evt->u.key->key->u.qcode];
-            trace_input_event_key_qcode(idx, name, evt->u.key->down);
+            name = QKeyCode_lookup[key->key->u.qcode];
+            trace_input_event_key_qcode(idx, name, key->down);
             break;
         case KEY_VALUE_KIND__MAX:
             /* keep gcc happy */
@@ -218,16 +223,19 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
         }
         break;
     case INPUT_EVENT_KIND_BTN:
-        name = InputButton_lookup[evt->u.btn->button];
-        trace_input_event_btn(idx, name, evt->u.btn->down);
+        btn = evt->u.btn;
+        name = InputButton_lookup[btn->button];
+        trace_input_event_btn(idx, name, btn->down);
         break;
     case INPUT_EVENT_KIND_REL:
-        name = InputAxis_lookup[evt->u.rel->axis];
-        trace_input_event_rel(idx, name, evt->u.rel->value);
+        move = evt->u.rel;
+        name = InputAxis_lookup[move->axis];
+        trace_input_event_rel(idx, name, move->value);
         break;
     case INPUT_EVENT_KIND_ABS:
-        name = InputAxis_lookup[evt->u.abs->axis];
-        trace_input_event_abs(idx, name, evt->u.abs->value);
+        move = evt->u.abs;
+        name = InputAxis_lookup[move->axis];
+        trace_input_event_abs(idx, name, move->value);
         break;
     case INPUT_EVENT_KIND__MAX:
         /* keep gcc happy */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 07/19] qapi: Avoid use of 'data' member of qapi unions
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (5 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 06/19] ui: Shorten references into InputEvent Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-02 18:18   ` Markus Armbruster
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 08/19] chardev: Drop useless ChardevDummy type Eric Blake
                   ` (12 subsequent siblings)
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, armbru, open list:Block layer core, Gerd Hoffmann

qapi code generators currently create a 'void *data' member as
part of the anonymous union embedded in the C struct corresponding
to a qapi union.  However, directly assigning to this member of
the union feels a bit fishy, when we can directly use the rest
of the struct instead.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
---
v2: add R-b
v1: no change
Previously posted as part of qapi cleanup series F:
v6: rebase to latest
---
 blockdev.c | 31 +++++++++++++++++--------------
 ui/input.c |  2 +-
 2 files changed, 18 insertions(+), 15 deletions(-)

diff --git a/blockdev.c b/blockdev.c
index d4bc435..0f20c65 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1202,15 +1202,11 @@ void hmp_commit(Monitor *mon, const QDict *qdict)
     }
 }

-static void blockdev_do_action(TransactionActionKind type, void *data,
-                               Error **errp)
+static void blockdev_do_action(TransactionAction *action, Error **errp)
 {
-    TransactionAction action;
     TransactionActionList list;

-    action.type = type;
-    action.u.data = data;
-    list.value = &action;
+    list.value = action;
     list.next = NULL;
     qmp_transaction(&list, false, NULL, errp);
 }
@@ -1236,8 +1232,11 @@ void qmp_blockdev_snapshot_sync(bool has_device, const char *device,
         .has_mode = has_mode,
         .mode = mode,
     };
-    blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC,
-                       &snapshot, errp);
+    TransactionAction action = {
+        .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC,
+        .u.blockdev_snapshot_sync = &snapshot,
+    };
+    blockdev_do_action(&action, errp);
 }

 void qmp_blockdev_snapshot(const char *node, const char *overlay,
@@ -1247,9 +1246,11 @@ void qmp_blockdev_snapshot(const char *node, const char *overlay,
         .node = (char *) node,
         .overlay = (char *) overlay
     };
-
-    blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT,
-                       &snapshot_data, errp);
+    TransactionAction action = {
+        .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT,
+        .u.blockdev_snapshot = &snapshot_data,
+    };
+    blockdev_do_action(&action, errp);
 }

 void qmp_blockdev_snapshot_internal_sync(const char *device,
@@ -1260,9 +1261,11 @@ void qmp_blockdev_snapshot_internal_sync(const char *device,
         .device = (char *) device,
         .name = (char *) name
     };
-
-    blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC,
-                       &snapshot, errp);
+    TransactionAction action = {
+        .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC,
+        .u.blockdev_snapshot_internal_sync = &snapshot,
+    };
+    blockdev_do_action(&action, errp);
 }

 SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
diff --git a/ui/input.c b/ui/input.c
index e15c618..1e81c25 100644
--- a/ui/input.c
+++ b/ui/input.c
@@ -472,7 +472,7 @@ InputEvent *qemu_input_event_new_move(InputEventKind kind,
     InputMoveEvent *move = g_new0(InputMoveEvent, 1);

     evt->type = kind;
-    evt->u.data = move;
+    evt->u.rel = move; /* also would work as evt->u.abs */
     move->axis = axis;
     move->value = value;
     return evt;
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 08/19] chardev: Drop useless ChardevDummy type
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (6 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 07/19] qapi: Avoid use of 'data' member of qapi unions Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 09/19] qapi: Drop useless 'data' member of unions Eric Blake
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: Samuel Thibault, armbru, Paolo Bonzini

Commit d0d7708b made ChardevDummy be an empty wrapper type around
ChardevCommon.  But there is no technical reason for this indirection,
so simplify the code by directly using the base type.

Also change the fallback assignment to assign u.null rather than
u.data, since a future patch will remove the data member of the C
struct generated for QAPI unions.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>

---
v2: add R-b
---
 backends/baum.c    |  2 +-
 backends/msmouse.c |  2 +-
 qemu-char.c        |  6 +++---
 qapi-schema.json   | 15 ++++++---------
 4 files changed, 11 insertions(+), 14 deletions(-)

diff --git a/backends/baum.c b/backends/baum.c
index 374562a..c11320e 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -567,7 +567,7 @@ static CharDriverState *chr_baum_init(const char *id,
                                       ChardevReturn *ret,
                                       Error **errp)
 {
-    ChardevCommon *common = qapi_ChardevDummy_base(backend->u.braille);
+    ChardevCommon *common = backend->u.braille;
     BaumDriverState *baum;
     CharDriverState *chr;
     brlapi_handle_t *handle;
diff --git a/backends/msmouse.c b/backends/msmouse.c
index 9a82efd..5e1833c 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -68,7 +68,7 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
                                               ChardevReturn *ret,
                                               Error **errp)
 {
-    ChardevCommon *common = qapi_ChardevDummy_base(backend->u.msmouse);
+    ChardevCommon *common = backend->u.msmouse;
     CharDriverState *chr;

     chr = qemu_chr_alloc(common, errp);
diff --git a/qemu-char.c b/qemu-char.c
index cfc82bc..6a0fc74 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -420,7 +420,7 @@ static CharDriverState *qemu_chr_open_null(const char *id,
                                            Error **errp)
 {
     CharDriverState *chr;
-    ChardevCommon *common = qapi_ChardevDummy_base(backend->u.null);
+    ChardevCommon *common = backend->u.null;

     chr = qemu_chr_alloc(common, errp);
     if (!chr) {
@@ -1366,7 +1366,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
     PtyCharDriver *s;
     int master_fd, slave_fd;
     char pty_name[PATH_MAX];
-    ChardevCommon *common = qapi_ChardevDummy_base(backend->u.pty);
+    ChardevCommon *common = backend->u.pty;

     master_fd = qemu_openpty_raw(&slave_fd, pty_name);
     if (master_fd < 0) {
@@ -3817,7 +3817,7 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
     } else {
         ChardevCommon *cc = g_new0(ChardevCommon, 1);
         qemu_chr_parse_common(opts, cc);
-        backend->u.data = cc;
+        backend->u.null = cc; /* Any ChardevCommon member would work */
     }

     ret = qmp_chardev_add(bid ? bid : id, backend, errp);
diff --git a/qapi-schema.json b/qapi-schema.json
index 7b8f2a1..66cc364 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3323,23 +3323,20 @@
 #
 # Since: 1.4 (testdev since 2.2)
 ##
-{ 'struct': 'ChardevDummy', 'data': { },
-  'base': 'ChardevCommon' }
-
 { 'union': 'ChardevBackend', 'data': { 'file'   : 'ChardevFile',
                                        'serial' : 'ChardevHostdev',
                                        'parallel': 'ChardevHostdev',
                                        'pipe'   : 'ChardevHostdev',
                                        'socket' : 'ChardevSocket',
                                        'udp'    : 'ChardevUdp',
-                                       'pty'    : 'ChardevDummy',
-                                       'null'   : 'ChardevDummy',
+                                       'pty'    : 'ChardevCommon',
+                                       'null'   : 'ChardevCommon',
                                        'mux'    : 'ChardevMux',
-                                       'msmouse': 'ChardevDummy',
-                                       'braille': 'ChardevDummy',
-                                       'testdev': 'ChardevDummy',
+                                       'msmouse': 'ChardevCommon',
+                                       'braille': 'ChardevCommon',
+                                       'testdev': 'ChardevCommon',
                                        'stdio'  : 'ChardevStdio',
-                                       'console': 'ChardevDummy',
+                                       'console': 'ChardevCommon',
                                        'spicevmc' : 'ChardevSpiceChannel',
                                        'spiceport' : 'ChardevSpicePort',
                                        'vc'     : 'ChardevVC',
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 09/19] qapi: Drop useless 'data' member of unions
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (7 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 08/19] chardev: Drop useless ChardevDummy type Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-02 18:30   ` Markus Armbruster
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 10/19] qapi-visit: Factor out gen_visit_members_call() Eric Blake
                   ` (10 subsequent siblings)
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Now that we no longer have any clients of the 'void *data'
member injected into unions, we can drop it.  Update the
testsuite to drop the negative test union-clash-data, and
replace it with a positive test in qapi-schema-test that
proves that we no longer have a name collision.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>

---
v2: add R-b
v1: drop patch that forced :empty as base to all structs
Previously posted as part of qapi cleanup subset F:
v6: rebase to earlier changes
---
 scripts/qapi-types.py                   | 9 ---------
 tests/Makefile                          | 1 -
 tests/qapi-schema/qapi-schema-test.json | 2 +-
 tests/qapi-schema/qapi-schema-test.out  | 4 ++--
 tests/qapi-schema/union-clash-data.err  | 0
 tests/qapi-schema/union-clash-data.exit | 1 -
 tests/qapi-schema/union-clash-data.json | 7 -------
 tests/qapi-schema/union-clash-data.out  | 9 ---------
 8 files changed, 3 insertions(+), 30 deletions(-)
 delete mode 100644 tests/qapi-schema/union-clash-data.err
 delete mode 100644 tests/qapi-schema/union-clash-data.exit
 delete mode 100644 tests/qapi-schema/union-clash-data.json
 delete mode 100644 tests/qapi-schema/union-clash-data.out

diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 19d1fff..0306a88 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -116,17 +116,8 @@ static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj)


 def gen_variants(variants):
-    # FIXME: What purpose does data serve, besides preventing a union that
-    # has a branch named 'data'? We use it in qapi-visit.py to decide
-    # whether to bypass the switch statement if visiting the discriminator
-    # failed; but since we 0-initialize structs, and cannot tell what
-    # branch of the union is in use if the discriminator is invalid, there
-    # should not be any data leaks even without a data pointer.  Or, if
-    # 'data' is merely added to guarantee we don't have an empty union,
-    # shouldn't we enforce that at .json parse time?
     ret = mcgen('''
     union { /* union tag is @%(c_name)s */
-        void *data;
 ''',
                 c_name=c_name(variants.tag_member.name))

diff --git a/tests/Makefile b/tests/Makefile
index 04e34b5..cd4bbd4 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -358,7 +358,6 @@ qapi-schema += unicode-str.json
 qapi-schema += union-base-no-discriminator.json
 qapi-schema += union-branch-case.json
 qapi-schema += union-clash-branches.json
-qapi-schema += union-clash-data.json
 qapi-schema += union-empty.json
 qapi-schema += union-invalid-base.json
 qapi-schema += union-optional-branch.json
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 632964a..b5d0c53 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -115,7 +115,7 @@
             'number': ['number'],
             'boolean': ['bool'],
             'string': ['str'],
-            'sizes': ['size'],
+            'data': ['size'],
             'any': ['any'] } }

 # testing commands
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index f5e2a73..225e2db 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -139,9 +139,9 @@ object UserDefNativeListUnion
     case number: :obj-numberList-wrapper
     case boolean: :obj-boolList-wrapper
     case string: :obj-strList-wrapper
-    case sizes: :obj-sizeList-wrapper
+    case data: :obj-sizeList-wrapper
     case any: :obj-anyList-wrapper
-enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'sizes', 'any']
+enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'data', 'any']
 object UserDefOne
     base UserDefZero
     member string: str optional=False
diff --git a/tests/qapi-schema/union-clash-data.err b/tests/qapi-schema/union-clash-data.err
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/qapi-schema/union-clash-data.exit b/tests/qapi-schema/union-clash-data.exit
deleted file mode 100644
index 573541a..0000000
--- a/tests/qapi-schema/union-clash-data.exit
+++ /dev/null
@@ -1 +0,0 @@
-0
diff --git a/tests/qapi-schema/union-clash-data.json b/tests/qapi-schema/union-clash-data.json
deleted file mode 100644
index 7308e69..0000000
--- a/tests/qapi-schema/union-clash-data.json
+++ /dev/null
@@ -1,7 +0,0 @@
-# Union branch 'data'
-# FIXME: this parses, but then fails to compile due to a duplicate 'data'
-# (one from the branch name, another as a filler to avoid an empty union).
-# we should either detect the collision at parse time, or change the
-# generated struct to allow this to compile.
-{ 'union': 'TestUnion',
-  'data': { 'data': 'int' } }
diff --git a/tests/qapi-schema/union-clash-data.out b/tests/qapi-schema/union-clash-data.out
deleted file mode 100644
index f5752f4..0000000
--- a/tests/qapi-schema/union-clash-data.out
+++ /dev/null
@@ -1,9 +0,0 @@
-object :empty
-object :obj-int-wrapper
-    member data: int optional=False
-enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
-    prefix QTYPE
-object TestUnion
-    member type: TestUnionKind optional=False
-    case data: :obj-int-wrapper
-enum TestUnionKind ['data']
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 10/19] qapi-visit: Factor out gen_visit_members_call()
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (8 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 09/19] qapi: Drop useless 'data' member of unions Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-02 18:53   ` Markus Armbruster
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 11/19] qapi: Add type.is_empty() helper Eric Blake
                   ` (9 subsequent siblings)
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Upcoming patches will be adding several contexts where we want
to handle the visit of an implicit type (an anonymous base type,
or an anonymous branch of a flat union) by directly inlining
the visit of each member of the implicit type. The work is made
easier by factoring out a new helper, gen_visit_members_call(),
so that the caller doesn't need to care whether the type it is
visiting is implicit or normal.

For now, the only implicit type we encounter are the branches
of a simple union; the initial implementation of the helper
method is hard-coded to that usage, but it gets us one step
closer to completely dropping the hack of simple_union_type().

Generated output is unchanged.

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

---
v2: retitle, rebase to s/fields/members/ changes
---
 scripts/qapi-visit.py | 42 ++++++++++++++++++++++++------------------
 1 file changed, 24 insertions(+), 18 deletions(-)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index a712e9a..dbae00c 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -34,6 +34,25 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
                  c_name=c_name(name))


+def gen_visit_members_call(typ, c_name):
+    ret = ''
+    assert isinstance(typ, QAPISchemaObjectType)
+    if typ.is_implicit():
+        # TODO ugly special case for simple union
+        assert len(typ.members) == 1
+        assert not typ.variants
+        ret += mcgen('''
+    visit_type_%(c_type)s(v, "data", %(c_name)s, &err);
+''',
+                     c_type=typ.members[0].type.c_name(), c_name=c_name)
+    else:
+        ret += mcgen('''
+    visit_type_%(c_type)s_members(v, %(c_name)s, &err);
+''',
+                     c_type=typ.c_name(), c_name=c_name)
+    return ret
+
+
 def gen_visit_object_members(name, base, members, variants):
     ret = mcgen('''

@@ -45,10 +64,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
                 c_name=c_name(name))

     if base:
-        ret += mcgen('''
-    visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, &err);
-''',
-                     c_type=base.c_name())
+        ret += gen_visit_members_call(base, '(%s *)obj' % base.c_name())
         ret += gen_err_check()

     ret += gen_visit_members(members, prefix='obj->')
@@ -60,26 +76,16 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
                      c_name=c_name(variants.tag_member.name))

         for var in variants.variants:
-            # TODO ugly special case for simple union
-            simple_union_type = var.simple_union_type()
             ret += mcgen('''
     case %(case)s:
 ''',
                          case=c_enum_const(variants.tag_member.type.name,
                                            var.name,
                                            variants.tag_member.type.prefix))
-            if simple_union_type:
-                ret += mcgen('''
-        visit_type_%(c_type)s(v, "data", &obj->u.%(c_name)s, &err);
-''',
-                             c_type=simple_union_type.c_name(),
-                             c_name=c_name(var.name))
-            else:
-                ret += mcgen('''
-        visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err);
-''',
-                             c_type=var.type.c_name(),
-                             c_name=c_name(var.name))
+            push_indent()
+            ret += gen_visit_members_call(var.type,
+                                          '&obj->u.' + c_name(var.name))
+            pop_indent()
             ret += mcgen('''
         break;
 ''')
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 11/19] qapi: Add type.is_empty() helper
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (9 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 10/19] qapi-visit: Factor out gen_visit_members_call() Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-02 19:04   ` Markus Armbruster
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 12/19] qapi: Fix command with named empty argument type Eric Blake
                   ` (8 subsequent siblings)
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

And use it in qapi-types and qapi-event.  Down the road, we may
want to lift our artificial restriction of no variants at the
top level of an event, at which point, inlining our check for
whether members is empty will no longer be sufficient, but
adding a check for variants adds verbosity; in the meantime,
add some asserts in places where we don't handle variants.

More immediately, the new .is_empty() helper will help fix a bug
in qapi-visit in the next patch, where the generator did not
handle an explicit empty type in the same was as a missing type.

No change to generated code.

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

---
v2: no change
v1: add some asserts
Previously posted as part of qapi cleanup subset E:
v9: improve commit message
v8: no change
v7: rebase to context change
v6: new patch
---
 scripts/qapi.py       | 5 +++++
 scripts/qapi-event.py | 7 ++++---
 scripts/qapi-types.py | 2 +-
 3 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 6c52fe5..83080b3 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -962,6 +962,7 @@ class QAPISchemaObjectType(QAPISchemaType):
             assert isinstance(self.base, QAPISchemaObjectType)
             self.base.check(schema)
             self.base.check_clash(schema, self.info, seen)
+            assert not self.base.variants
         for m in self.local_members:
             m.check(schema)
             m.check_clash(self.info, seen)
@@ -983,6 +984,10 @@ class QAPISchemaObjectType(QAPISchemaType):
         # See QAPISchema._make_implicit_object_type()
         return self.name[0] == ':'

+    def is_empty(self):
+        assert self.members is not None
+        return not self.members and not self.variants
+
     def c_name(self):
         assert not self.is_implicit()
         return QAPISchemaType.c_name(self)
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index fb579dd..808ed80 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -39,7 +39,7 @@ def gen_event_send(name, arg_type):
 ''',
                 proto=gen_event_send_proto(name, arg_type))

-    if arg_type and arg_type.members:
+    if arg_type and not arg_type.is_empty():
         ret += mcgen('''
     QmpOutputVisitor *qov;
     Visitor *v;
@@ -58,7 +58,8 @@ def gen_event_send(name, arg_type):
 ''',
                  name=name)

-    if arg_type and arg_type.members:
+    if arg_type and not arg_type.is_empty():
+        assert not arg_type.variants
         ret += mcgen('''
     qov = qmp_output_visitor_new();
     v = qmp_output_get_visitor(qov);
@@ -88,7 +89,7 @@ out_obj:
 ''',
                  c_enum=c_enum_const(event_enum_name, name))

-    if arg_type and arg_type.members:
+    if arg_type and not arg_type.is_empty():
         ret += mcgen('''
 out:
     qmp_output_visitor_cleanup(qov);
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 0306a88..6c1923d 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -90,7 +90,7 @@ struct %(c_name)s {
     # potential issues with attempting to malloc space for zero-length
     # structs in C, and also incompatibility with C++ (where an empty
     # struct is size 1).
-    if not (base and base.members) and not members and not variants:
+    if (not base or base.is_empty()) and not members and not variants:
         ret += mcgen('''
     char qapi_dummy_for_empty_struct;
 ''')
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 12/19] qapi: Fix command with named empty argument type
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (10 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 11/19] qapi: Add type.is_empty() helper Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-03  8:54   ` Markus Armbruster
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 13/19] qapi-visit: Simplify visit of empty branch in union Eric Blake
                   ` (7 subsequent siblings)
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

The generator special-cased

 { 'command':'foo', 'data': {} }

to avoid emitting a visitor variable, but failed to see that

 { 'struct':'NamedEmptyType, 'data': {} }
 { 'command':'foo', 'data':'NamedEmptyType' }

needs the same treatment.  There, the generator happily generates a
visitor to get no arguments, and a visitor to destroy no arguments;
and the compiler isn't happy with that, as demonstrated by the updated
qapi-schema-test.json:

  tests/test-qmp-marshal.c: In function ‘qmp_marshal_user_def_cmd0’:
  tests/test-qmp-marshal.c:264:14: error: variable ‘v’ set but not used [-Werror=unused-but-set-variable]
       Visitor *v;
                ^

No change to generated code except for the testsuite addition.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v2: no change
v1: enhance commit message
Previously posted as part of qapi cleanup subset E:
v9: no change
v8: no change
v7: no change
v6: new patch
---
 scripts/qapi-commands.py                | 6 +++---
 tests/test-qmp-commands.c               | 5 +++++
 tests/qapi-schema/qapi-schema-test.json | 2 ++
 tests/qapi-schema/qapi-schema-test.out  | 2 ++
 4 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index f44e01f..7c9cfbf 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -65,7 +65,7 @@ def gen_marshal_vars(arg_type, ret_type):
 ''',
                      c_type=ret_type.c_type())

-    if arg_type:
+    if arg_type and not arg_type.is_empty():
         ret += mcgen('''
     QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
     QapiDeallocVisitor *qdv;
@@ -97,7 +97,7 @@ def gen_marshal_vars(arg_type, ret_type):
 def gen_marshal_input_visit(arg_type, dealloc=False):
     ret = ''

-    if not arg_type:
+    if not arg_type or arg_type.is_empty():
         return ret

     if dealloc:
@@ -177,7 +177,7 @@ def gen_marshal(name, arg_type, ret_type):

     # 'goto out' produced by gen_marshal_input_visit->gen_visit_members()
     # for each arg_type member, and by gen_call() for ret_type
-    if (arg_type and arg_type.members) or ret_type:
+    if (arg_type and not arg_type.is_empty()) or ret_type:
         ret += mcgen('''

 out:
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index d6171f2..650ba46 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -13,6 +13,11 @@ void qmp_user_def_cmd(Error **errp)
 {
 }

+Empty2 *qmp_user_def_cmd0(Error **errp)
+{
+    return g_new0(Empty2, 1);
+}
+
 void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp)
 {
 }
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index b5d0c53..33e8517 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -18,6 +18,8 @@
 { 'struct': 'Empty1', 'data': { } }
 { 'struct': 'Empty2', 'base': 'Empty1', 'data': { } }

+{ 'command': 'user_def_cmd0', 'data': 'Empty2', 'returns': 'Empty2' }
+
 # for testing override of default naming heuristic
 { 'enum': 'QEnumTwo',
   'prefix': 'QENUM_TWO',
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 225e2db..6b97fa5 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -203,6 +203,8 @@ command guest-sync :obj-guest-sync-arg -> any
    gen=True success_response=True
 command user_def_cmd None -> None
    gen=True success_response=True
+command user_def_cmd0 Empty2 -> Empty2
+   gen=True success_response=True
 command user_def_cmd1 :obj-user_def_cmd1-arg -> None
    gen=True success_response=True
 command user_def_cmd2 :obj-user_def_cmd2-arg -> UserDefTwo
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 13/19] qapi-visit: Simplify visit of empty branch in union
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (11 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 12/19] qapi: Fix command with named empty argument type Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-03  9:14   ` Markus Armbruster
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 14/19] qapi: Don't special-case simple union wrappers Eric Blake
                   ` (6 subsequent siblings)
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Now that we have is_empty() and gen_visit_fields_call(), it's
fairly easy to skip the visit of a variant type that has no
members.  Only one such instance exists at the moment
(CpuInfoOther), but the idea of a union where some branches
add no fields beyond the base type is common enough that we
may add syntax for other cases, as in
  { 'union':'U', 'base':'B', 'discriminator':'D',
    'data': { 'default': {}, 'extra':'Type' } }

Note that the Abort type is another example of an empty type,
but it is only used by a simple union rather than a flat
union; and with simple unions, the wrapper type providing
the 'data' QMP key is not empty.

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

---
v2: no change
---
 scripts/qapi-visit.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index dbae00c..a9de393 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -37,7 +37,9 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
 def gen_visit_members_call(typ, c_name):
     ret = ''
     assert isinstance(typ, QAPISchemaObjectType)
-    if typ.is_implicit():
+    if typ.is_empty():
+        pass
+    elif typ.is_implicit():
         # TODO ugly special case for simple union
         assert len(typ.members) == 1
         assert not typ.variants
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 14/19] qapi: Don't special-case simple union wrappers
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (12 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 13/19] qapi-visit: Simplify visit of empty branch in union Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-03 10:59   ` Markus Armbruster
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 15/19] qapi-visit: Move error check into gen_visit_members_call() Eric Blake
                   ` (5 subsequent siblings)
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Eduardo Habkost,
	open list:Block layer core, Michael S. Tsirkin, Jan Kiszka,
	Jason Wang, Vincenzo Maffione, armbru, Igor Mammedov,
	Gerd Hoffmann, Paolo Bonzini, Samuel Thibault, Giuseppe Lettieri,
	Luiz Capitulino, Luigi Rizzo, Michael Roth

Simple unions were carrying a special case that hid their 'data'
QMP member from the resulting C struct, via the hack method
QAPISchemaObjectTypeVariant.simple_union_type().  But using the
work we started by unboxing flat union and alternate branches, we
expose the simple union's implicit type in qapi-types.h as an
anonymous type, and drop our last use of the hack.

| struct ImageInfoSpecific {
|     ImageInfoSpecificKind type;
|     union { /* union tag is @type */
|         void *data;
|-        ImageInfoSpecificQCow2 *qcow2;
|-        ImageInfoSpecificVmdk *vmdk;
|+        struct {
|+            ImageInfoSpecificQCow2 *data;
|+        } qcow2;
|+        struct {
|+            ImageInfoSpecificVmdk *data;
|+        } vmdk;
|     } u;
| };

All clients of simple unions have to adjust from using su->u.member
to now using su->u.member.data; while this touches a number of
files in the tree, some earlier cleanup patches helped minimize
the change to the initialization of a temporary variable rather
than every single member access.  The generated qapi-visit.c code
is included in the files affected by the layout change, with a
diff that looks like:

|@@ -4510,10 +4567,16 @@ static void visit_type_ImageInfoSpecific
|     }
|     switch (obj->type) {
|     case IMAGE_INFO_SPECIFIC_KIND_QCOW2:
|-        visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err);
|+        visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2.data, &err);
|+        if (err) {
|+            goto out;
|+        }
|         break;
|     case IMAGE_INFO_SPECIFIC_KIND_VMDK:
|-        visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err);
|+        visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk.data, &err);
|+        if (err) {
|+            goto out;
|+        }
|         break;
|     default:
|         abort();
|     }
|
| out:
|     error_propagate(errp, err);

The added error checks there are a side effect of visiting all
members of each implicit struct (there is only one such member for
simple unions); but do not change semantics, and will be important
when later patches allow for flat unions with anonymous branches
with more than one member.  That future work will look like:
{ 'union': 'Foo', 'base': 'Base', 'discriminator': 'type',
  'data': { 'branch1': { 'anonymous': 'str', 'number': 'int' },
            'branch2': 'Named' } }

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

---
v2: rebase onto s/fields/members/ change, changes in master; pick
up missing net/ files
---
 scripts/qapi.py                 | 10 -----
 scripts/qapi-types.py           | 22 ++++++++---
 scripts/qapi-visit.py           | 15 +++-----
 backends/baum.c                 |  2 +-
 backends/msmouse.c              |  2 +-
 block/nbd.c                     |  6 +--
 block/qcow2.c                   |  6 +--
 block/vmdk.c                    |  8 ++--
 blockdev.c                      | 24 ++++++------
 hmp.c                           |  8 ++--
 hw/char/escc.c                  |  2 +-
 hw/input/hid.c                  |  8 ++--
 hw/input/ps2.c                  |  6 +--
 hw/input/virtio-input-hid.c     |  8 ++--
 hw/mem/pc-dimm.c                |  2 +-
 net/dump.c                      |  2 +-
 net/hub.c                       |  2 +-
 net/l2tpv3.c                    |  2 +-
 net/net.c                       |  4 +-
 net/netmap.c                    |  2 +-
 net/slirp.c                     |  2 +-
 net/socket.c                    |  2 +-
 net/tap-win32.c                 |  2 +-
 net/tap.c                       |  4 +-
 net/vde.c                       |  2 +-
 net/vhost-user.c                |  2 +-
 numa.c                          |  4 +-
 qemu-char.c                     | 82 +++++++++++++++++++++--------------------
 qemu-nbd.c                      |  6 +--
 replay/replay-input.c           | 44 +++++++++++-----------
 spice-qemu-char.c               | 14 ++++---
 tests/test-io-channel-socket.c  | 40 ++++++++++----------
 tests/test-qmp-commands.c       |  2 +-
 tests/test-qmp-input-visitor.c  | 25 +++++++------
 tests/test-qmp-output-visitor.c | 24 ++++++------
 tpm.c                           |  2 +-
 ui/console.c                    |  4 +-
 ui/input-keymap.c               | 10 ++---
 ui/input-legacy.c               |  8 ++--
 ui/input.c                      | 34 ++++++++---------
 ui/vnc-auth-sasl.c              |  3 +-
 ui/vnc.c                        | 29 ++++++++-------
 util/qemu-sockets.c             | 35 +++++++++---------
 43 files changed, 263 insertions(+), 258 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 83080b3..38121c5 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1115,16 +1115,6 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
     def __init__(self, name, typ):
         QAPISchemaObjectTypeMember.__init__(self, name, typ, False)

-    # This function exists to support ugly simple union special cases
-    # TODO get rid of them, and drop the function
-    def simple_union_type(self):
-        if (self.type.is_implicit() and
-                isinstance(self.type, QAPISchemaObjectType)):
-            assert len(self.type.members) == 1
-            assert not self.type.variants
-            return self.type.members[0].type
-        return None
-

 class QAPISchemaAlternateType(QAPISchemaType):
     def __init__(self, name, info, variants):
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 6c1923d..1f090e6 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -122,14 +122,24 @@ def gen_variants(variants):
                 c_name=c_name(variants.tag_member.name))

     for var in variants.variants:
-        # Ugly special case for simple union TODO get rid of it
-        simple_union_type = var.simple_union_type()
-        typ = simple_union_type or var.type
-        ret += mcgen('''
+        if (isinstance(var.type, QAPISchemaObjectType) and
+                var.type.is_implicit()):
+            ret += mcgen('''
+        struct {
+''')
+            push_indent(8)
+            ret += gen_struct_members(var.type.members)
+            pop_indent(8)
+            ret += mcgen('''
+        } %(c_name)s;
+''',
+                         c_name=c_name(var.name))
+        else:
+            ret += mcgen('''
         %(c_type)s %(c_name)s;
 ''',
-                     c_type=typ.c_type(is_unboxed=not simple_union_type),
-                     c_name=c_name(var.name))
+                         c_type=var.type.c_type(is_unboxed=True),
+                         c_name=c_name(var.name))

     ret += mcgen('''
     } u;
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index a9de393..e281d21 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -34,24 +34,20 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
                  c_name=c_name(name))


-def gen_visit_members_call(typ, c_name):
+def gen_visit_members_call(typ, direct_name, implicit_name=None):
     ret = ''
     assert isinstance(typ, QAPISchemaObjectType)
     if typ.is_empty():
         pass
     elif typ.is_implicit():
-        # TODO ugly special case for simple union
-        assert len(typ.members) == 1
+        assert implicit_name
         assert not typ.variants
-        ret += mcgen('''
-    visit_type_%(c_type)s(v, "data", %(c_name)s, &err);
-''',
-                     c_type=typ.members[0].type.c_name(), c_name=c_name)
+        ret += gen_visit_members(typ.members, prefix=implicit_name)
     else:
         ret += mcgen('''
     visit_type_%(c_type)s_members(v, %(c_name)s, &err);
 ''',
-                     c_type=typ.c_name(), c_name=c_name)
+                     c_type=typ.c_name(), c_name=direct_name)
     return ret


@@ -86,7 +82,8 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
                                            variants.tag_member.type.prefix))
             push_indent()
             ret += gen_visit_members_call(var.type,
-                                          '&obj->u.' + c_name(var.name))
+                                          '&obj->u.' + c_name(var.name),
+                                          'obj->u.' + c_name(var.name) + '.')
             pop_indent()
             ret += mcgen('''
         break;
diff --git a/backends/baum.c b/backends/baum.c
index c11320e..eef3467 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -567,7 +567,7 @@ static CharDriverState *chr_baum_init(const char *id,
                                       ChardevReturn *ret,
                                       Error **errp)
 {
-    ChardevCommon *common = backend->u.braille;
+    ChardevCommon *common = backend->u.braille.data;
     BaumDriverState *baum;
     CharDriverState *chr;
     brlapi_handle_t *handle;
diff --git a/backends/msmouse.c b/backends/msmouse.c
index 5e1833c..8dea5a1 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -68,7 +68,7 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
                                               ChardevReturn *ret,
                                               Error **errp)
 {
-    ChardevCommon *common = backend->u.msmouse;
+    ChardevCommon *common = backend->u.msmouse.data;
     CharDriverState *chr;

     chr = qemu_chr_alloc(common, errp);
diff --git a/block/nbd.c b/block/nbd.c
index 9f333c9..836424c 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -206,13 +206,13 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, char **export,
     if (qdict_haskey(options, "path")) {
         UnixSocketAddress *q_unix;
         saddr->type = SOCKET_ADDRESS_KIND_UNIX;
-        q_unix = saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
+        q_unix = saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
         q_unix->path = g_strdup(qdict_get_str(options, "path"));
         qdict_del(options, "path");
     } else {
         InetSocketAddress *inet;
         saddr->type = SOCKET_ADDRESS_KIND_INET;
-        inet = saddr->u.inet = g_new0(InetSocketAddress, 1);
+        inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1);
         inet->host = g_strdup(qdict_get_str(options, "host"));
         if (!qdict_get_try_str(options, "port")) {
             inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
@@ -321,7 +321,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
             error_setg(errp, "TLS only supported over IP sockets");
             goto error;
         }
-        hostname = saddr->u.inet->host;
+        hostname = saddr->u.inet.data->host;
     }

     /* establish TCP connection, return error if it fails
diff --git a/block/qcow2.c b/block/qcow2.c
index 8babecd..31f35f4 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2809,15 +2809,15 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)

     *spec_info = (ImageInfoSpecific){
         .type  = IMAGE_INFO_SPECIFIC_KIND_QCOW2,
-        .u.qcow2 = g_new(ImageInfoSpecificQCow2, 1),
+        .u.qcow2.data = g_new(ImageInfoSpecificQCow2, 1),
     };
     if (s->qcow_version == 2) {
-        *spec_info->u.qcow2 = (ImageInfoSpecificQCow2){
+        *spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){
             .compat             = g_strdup("0.10"),
             .refcount_bits      = s->refcount_bits,
         };
     } else if (s->qcow_version == 3) {
-        *spec_info->u.qcow2 = (ImageInfoSpecificQCow2){
+        *spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){
             .compat             = g_strdup("1.1"),
             .lazy_refcounts     = s->compatible_features &
                                   QCOW2_COMPAT_LAZY_REFCOUNTS,
diff --git a/block/vmdk.c b/block/vmdk.c
index a8db5d9..0fc2ce2 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -2183,18 +2183,18 @@ static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs)

     *spec_info = (ImageInfoSpecific){
         .type = IMAGE_INFO_SPECIFIC_KIND_VMDK,
-        {
-            .vmdk = g_new0(ImageInfoSpecificVmdk, 1),
+        .u = {
+            .vmdk.data = g_new0(ImageInfoSpecificVmdk, 1),
         },
     };

-    *spec_info->u.vmdk = (ImageInfoSpecificVmdk) {
+    *spec_info->u.vmdk.data = (ImageInfoSpecificVmdk) {
         .create_type = g_strdup(s->create_type),
         .cid = s->cid,
         .parent_cid = s->parent_cid,
     };

-    next = &spec_info->u.vmdk->extents;
+    next = &spec_info->u.vmdk.data->extents;
     for (i = 0; i < s->num_extents; i++) {
         *next = g_new0(ImageInfoList, 1);
         (*next)->value = vmdk_get_extent_info(&s->extents[i]);
diff --git a/blockdev.c b/blockdev.c
index 0f20c65..0e08aea 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1234,7 +1234,7 @@ void qmp_blockdev_snapshot_sync(bool has_device, const char *device,
     };
     TransactionAction action = {
         .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC,
-        .u.blockdev_snapshot_sync = &snapshot,
+        .u.blockdev_snapshot_sync.data = &snapshot,
     };
     blockdev_do_action(&action, errp);
 }
@@ -1248,7 +1248,7 @@ void qmp_blockdev_snapshot(const char *node, const char *overlay,
     };
     TransactionAction action = {
         .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT,
-        .u.blockdev_snapshot = &snapshot_data,
+        .u.blockdev_snapshot.data = &snapshot_data,
     };
     blockdev_do_action(&action, errp);
 }
@@ -1263,7 +1263,7 @@ void qmp_blockdev_snapshot_internal_sync(const char *device,
     };
     TransactionAction action = {
         .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC,
-        .u.blockdev_snapshot_internal_sync = &snapshot,
+        .u.blockdev_snapshot_internal_sync.data = &snapshot,
     };
     blockdev_do_action(&action, errp);
 }
@@ -1502,7 +1502,7 @@ static void internal_snapshot_prepare(BlkActionState *common,

     g_assert(common->action->type ==
              TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC);
-    internal = common->action->u.blockdev_snapshot_internal_sync;
+    internal = common->action->u.blockdev_snapshot_internal_sync.data;
     state = DO_UPCAST(InternalSnapshotState, common, common);

     /* 1. parse input */
@@ -1652,7 +1652,7 @@ static void external_snapshot_prepare(BlkActionState *common,
     switch (action->type) {
     case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT:
         {
-            BlockdevSnapshot *s = action->u.blockdev_snapshot;
+            BlockdevSnapshot *s = action->u.blockdev_snapshot.data;
             device = s->node;
             node_name = s->node;
             new_image_file = NULL;
@@ -1661,7 +1661,7 @@ static void external_snapshot_prepare(BlkActionState *common,
         break;
     case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC:
         {
-            BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync;
+            BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data;
             device = s->has_device ? s->device : NULL;
             node_name = s->has_node_name ? s->node_name : NULL;
             new_image_file = s->snapshot_file;
@@ -1710,7 +1710,7 @@ static void external_snapshot_prepare(BlkActionState *common,
     }

     if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) {
-        BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync;
+        BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data;
         const char *format = s->has_format ? s->format : "qcow2";
         enum NewImageMode mode;
         const char *snapshot_node_name =
@@ -1843,7 +1843,7 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
     Error *local_err = NULL;

     assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
-    backup = common->action->u.drive_backup;
+    backup = common->action->u.drive_backup.data;

     blk = blk_by_name(backup->device);
     if (!blk) {
@@ -1925,7 +1925,7 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
     Error *local_err = NULL;

     assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
-    backup = common->action->u.blockdev_backup;
+    backup = common->action->u.blockdev_backup.data;

     blk = blk_by_name(backup->device);
     if (!blk) {
@@ -2011,7 +2011,7 @@ static void block_dirty_bitmap_add_prepare(BlkActionState *common,
         return;
     }

-    action = common->action->u.block_dirty_bitmap_add;
+    action = common->action->u.block_dirty_bitmap_add.data;
     /* AIO context taken and released within qmp_block_dirty_bitmap_add */
     qmp_block_dirty_bitmap_add(action->node, action->name,
                                action->has_granularity, action->granularity,
@@ -2030,7 +2030,7 @@ static void block_dirty_bitmap_add_abort(BlkActionState *common)
     BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
                                              common, common);

-    action = common->action->u.block_dirty_bitmap_add;
+    action = common->action->u.block_dirty_bitmap_add.data;
     /* Should not be able to fail: IF the bitmap was added via .prepare(),
      * then the node reference and bitmap name must have been valid.
      */
@@ -2050,7 +2050,7 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
         return;
     }

-    action = common->action->u.block_dirty_bitmap_clear;
+    action = common->action->u.block_dirty_bitmap_clear.data;
     state->bitmap = block_dirty_bitmap_lookup(action->node,
                                               action->name,
                                               &state->bs,
diff --git a/hmp.c b/hmp.c
index 5b6084a..6ace227 100644
--- a/hmp.c
+++ b/hmp.c
@@ -857,7 +857,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)

         switch (ti->options->type) {
         case TPM_TYPE_OPTIONS_KIND_PASSTHROUGH:
-            tpo = ti->options->u.passthrough;
+            tpo = ti->options->u.passthrough.data;
             monitor_printf(mon, "%s%s%s%s",
                            tpo->has_path ? ",path=" : "",
                            tpo->has_path ? tpo->path : "",
@@ -1753,14 +1753,14 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict)
                 goto err_out;
             }
             keylist->value->type = KEY_VALUE_KIND_NUMBER;
-            keylist->value->u.number = value;
+            keylist->value->u.number.data = value;
         } else {
             int idx = index_from_key(keys, keyname_len);
             if (idx == Q_KEY_CODE__MAX) {
                 goto err_out;
             }
             keylist->value->type = KEY_VALUE_KIND_QCODE;
-            keylist->value->u.qcode = idx;
+            keylist->value->u.qcode.data = idx;
         }

         if (!separator) {
@@ -1977,7 +1977,7 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
         if (value) {
             switch (value->type) {
             case MEMORY_DEVICE_INFO_KIND_DIMM:
-                di = value->u.dimm;
+                di = value->u.dimm.data;

                 monitor_printf(mon, "Memory device [%s]: \"%s\"\n",
                                MemoryDeviceInfoKind_lookup[value->type],
diff --git a/hw/char/escc.c b/hw/char/escc.c
index c7a24ac..7bf09a0 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -845,7 +845,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
     InputKeyEvent *key;

     assert(evt->type == INPUT_EVENT_KIND_KEY);
-    key = evt->u.key;
+    key = evt->u.key.data;
     qcode = qemu_input_key_value_to_qcode(key->key);
     trace_escc_sunkbd_event_in(qcode, QKeyCode_lookup[qcode],
                                key->down);
diff --git a/hw/input/hid.c b/hw/input/hid.c
index 5db4d68..bf8e24f 100644
--- a/hw/input/hid.c
+++ b/hw/input/hid.c
@@ -124,7 +124,7 @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src,

     switch (evt->type) {
     case INPUT_EVENT_KIND_REL:
-        move = evt->u.rel;
+        move = evt->u.rel.data;
         if (move->axis == INPUT_AXIS_X) {
             e->xdx += move->value;
         } else if (move->axis == INPUT_AXIS_Y) {
@@ -133,7 +133,7 @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
         break;

     case INPUT_EVENT_KIND_ABS:
-        move = evt->u.abs;
+        move = evt->u.abs.data;
         if (move->axis == INPUT_AXIS_X) {
             e->xdx = move->value;
         } else if (move->axis == INPUT_AXIS_Y) {
@@ -142,7 +142,7 @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
         break;

     case INPUT_EVENT_KIND_BTN:
-        btn = evt->u.btn;
+        btn = evt->u.btn.data;
         if (btn->down) {
             e->buttons_state |= bmap[btn->button];
             if (btn->button == INPUT_BUTTON_WHEELUP) {
@@ -228,7 +228,7 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
     HIDState *hs = (HIDState *)dev;
     int scancodes[3], i, count;
     int slot;
-    InputKeyEvent *key = evt->u.key;
+    InputKeyEvent *key = evt->u.key.data;

     count = qemu_input_key_value_to_scancode(key->key,
                                              key->down,
diff --git a/hw/input/ps2.c b/hw/input/ps2.c
index 7bab7c2..4edd9d2 100644
--- a/hw/input/ps2.c
+++ b/hw/input/ps2.c
@@ -182,7 +182,7 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
 {
     PS2KbdState *s = (PS2KbdState *)dev;
     int scancodes[3], i, count;
-    InputKeyEvent *key = evt->u.key;
+    InputKeyEvent *key = evt->u.key.data;

     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
     count = qemu_input_key_value_to_scancode(key->key,
@@ -399,7 +399,7 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,

     switch (evt->type) {
     case INPUT_EVENT_KIND_REL:
-        move = evt->u.rel;
+        move = evt->u.rel.data;
         if (move->axis == INPUT_AXIS_X) {
             s->mouse_dx += move->value;
         } else if (move->axis == INPUT_AXIS_Y) {
@@ -408,7 +408,7 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
         break;

     case INPUT_EVENT_KIND_BTN:
-        btn = evt->u.btn;
+        btn = evt->u.btn.data;
         if (btn->down) {
             s->mouse_buttons |= bmap[btn->button];
             if (btn->button == INPUT_BUTTON_WHEELUP) {
diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c
index 7053f75..2aec423 100644
--- a/hw/input/virtio-input-hid.c
+++ b/hw/input/virtio-input-hid.c
@@ -197,7 +197,7 @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src,

     switch (evt->type) {
     case INPUT_EVENT_KIND_KEY:
-        key = evt->u.key;
+        key = evt->u.key.data;
         qcode = qemu_input_key_value_to_qcode(key->key);
         if (qcode && keymap_qcode[qcode]) {
             event.type  = cpu_to_le16(EV_KEY);
@@ -212,7 +212,7 @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src,
         }
         break;
     case INPUT_EVENT_KIND_BTN:
-        btn = evt->u.btn;
+        btn = evt->u.btn.data;
         if (keymap_button[btn->button]) {
             event.type  = cpu_to_le16(EV_KEY);
             event.code  = cpu_to_le16(keymap_button[btn->button]);
@@ -227,14 +227,14 @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src,
         }
         break;
     case INPUT_EVENT_KIND_REL:
-        move = evt->u.rel;
+        move = evt->u.rel.data;
         event.type  = cpu_to_le16(EV_REL);
         event.code  = cpu_to_le16(axismap_rel[move->axis]);
         event.value = cpu_to_le32(move->value);
         virtio_input_send(vinput, &event);
         break;
     case INPUT_EVENT_KIND_ABS:
-        move = evt->u.abs;
+        move = evt->u.abs.data;
         event.type  = cpu_to_le16(EV_ABS);
         event.code  = cpu_to_le16(axismap_abs[move->axis]);
         event.value = cpu_to_le32(move->value);
diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c
index 650f0f8..73c2426 100644
--- a/hw/mem/pc-dimm.c
+++ b/hw/mem/pc-dimm.c
@@ -180,7 +180,7 @@ int qmp_pc_dimm_device_list(Object *obj, void *opaque)
                                                NULL);
             di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem));

-            info->u.dimm = di;
+            info->u.dimm.data = di;
             elem->value = info;
             elem->next = NULL;
             **prev = elem;
diff --git a/net/dump.c b/net/dump.c
index 61dec9d..94ac32a 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -189,7 +189,7 @@ int net_init_dump(const NetClientOptions *opts, const char *name,
     DumpNetClient *dnc;

     assert(opts->type == NET_CLIENT_OPTIONS_KIND_DUMP);
-    dump = opts->u.dump;
+    dump = opts->u.dump.data;

     assert(peer);

diff --git a/net/hub.c b/net/hub.c
index b6d44fd..6d90c6e 100644
--- a/net/hub.c
+++ b/net/hub.c
@@ -288,7 +288,7 @@ int net_init_hubport(const NetClientOptions *opts, const char *name,

     assert(opts->type == NET_CLIENT_OPTIONS_KIND_HUBPORT);
     assert(!peer);
-    hubport = opts->u.hubport;
+    hubport = opts->u.hubport.data;

     net_hub_add_port(hubport->hubid, name);
     return 0;
diff --git a/net/l2tpv3.c b/net/l2tpv3.c
index 824161c..5c668f7 100644
--- a/net/l2tpv3.c
+++ b/net/l2tpv3.c
@@ -546,7 +546,7 @@ int net_init_l2tpv3(const NetClientOptions *opts,
     s->header_mismatch = false;

     assert(opts->type == NET_CLIENT_OPTIONS_KIND_L2TPV3);
-    l2tpv3 = opts->u.l2tpv3;
+    l2tpv3 = opts->u.l2tpv3.data;

     if (l2tpv3->has_ipv6 && l2tpv3->ipv6) {
         s->ipv6 = l2tpv3->ipv6;
diff --git a/net/net.c b/net/net.c
index b0c832e..6274377 100644
--- a/net/net.c
+++ b/net/net.c
@@ -893,7 +893,7 @@ static int net_init_nic(const NetClientOptions *opts, const char *name,
     const NetLegacyNicOptions *nic;

     assert(opts->type == NET_CLIENT_OPTIONS_KIND_NIC);
-    nic = opts->u.nic;
+    nic = opts->u.nic.data;

     idx = nic_get_free_idx();
     if (idx == -1 || nb_nics >= MAX_NICS) {
@@ -1025,7 +1025,7 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)

         /* Do not add to a vlan if it's a nic with a netdev= parameter. */
         if (opts->type != NET_CLIENT_OPTIONS_KIND_NIC ||
-            !opts->u.nic->has_netdev) {
+            !opts->u.nic.data->has_netdev) {
             peer = net_hub_add_port(net->has_vlan ? net->vlan : 0, NULL);
         }
     }
diff --git a/net/netmap.c b/net/netmap.c
index 9710321..9f7dc53 100644
--- a/net/netmap.c
+++ b/net/netmap.c
@@ -403,7 +403,7 @@ static NetClientInfo net_netmap_info = {
 int net_init_netmap(const NetClientOptions *opts,
                     const char *name, NetClientState *peer, Error **errp)
 {
-    const NetdevNetmapOptions *netmap_opts = opts->u.netmap;
+    const NetdevNetmapOptions *netmap_opts = opts->u.netmap.data;
     struct nm_desc *nmd;
     NetClientState *nc;
     Error *err = NULL;
diff --git a/net/slirp.c b/net/slirp.c
index 6b51fbc..6866913 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -747,7 +747,7 @@ int net_init_slirp(const NetClientOptions *opts, const char *name,
     const char **dnssearch;

     assert(opts->type == NET_CLIENT_OPTIONS_KIND_USER);
-    user = opts->u.user;
+    user = opts->u.user.data;

     vnet = user->has_net ? g_strdup(user->net) :
            user->has_ip  ? g_strdup_printf("%s/24", user->ip) :
diff --git a/net/socket.c b/net/socket.c
index e32e3cb..d7d0dec 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -707,7 +707,7 @@ int net_init_socket(const NetClientOptions *opts, const char *name,
     const NetdevSocketOptions *sock;

     assert(opts->type == NET_CLIENT_OPTIONS_KIND_SOCKET);
-    sock = opts->u.socket;
+    sock = opts->u.socket.data;

     if (sock->has_fd + sock->has_listen + sock->has_connect + sock->has_mcast +
         sock->has_udp != 1) {
diff --git a/net/tap-win32.c b/net/tap-win32.c
index 38bbac0..f1e142a 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -795,7 +795,7 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
     const NetdevTapOptions *tap;

     assert(opts->type == NET_CLIENT_OPTIONS_KIND_TAP);
-    tap = opts->u.tap;
+    tap = opts->u.tap.data;

     if (!tap->has_ifname) {
         error_report("tap: no interface name");
diff --git a/net/tap.c b/net/tap.c
index cfb6831..b477352 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -565,7 +565,7 @@ int net_init_bridge(const NetClientOptions *opts, const char *name,
     int fd, vnet_hdr;

     assert(opts->type == NET_CLIENT_OPTIONS_KIND_BRIDGE);
-    bridge = opts->u.bridge;
+    bridge = opts->u.bridge.data;

     helper = bridge->has_helper ? bridge->helper : DEFAULT_BRIDGE_HELPER;
     br     = bridge->has_br     ? bridge->br     : DEFAULT_BRIDGE_INTERFACE;
@@ -728,7 +728,7 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
     char ifname[128];

     assert(opts->type == NET_CLIENT_OPTIONS_KIND_TAP);
-    tap = opts->u.tap;
+    tap = opts->u.tap.data;
     queues = tap->has_queues ? tap->queues : 1;
     vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL;

diff --git a/net/vde.c b/net/vde.c
index 973faf5..9427eaa 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -116,7 +116,7 @@ int net_init_vde(const NetClientOptions *opts, const char *name,
     const NetdevVdeOptions *vde;

     assert(opts->type == NET_CLIENT_OPTIONS_KIND_VDE);
-    vde = opts->u.vde;
+    vde = opts->u.vde.data;

     /* missing optional values have been initialized to "all bits zero" */
     if (net_vde_init(peer, "vde", name, vde->sock, vde->port, vde->group,
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 451dbbf..61f1cb4 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -303,7 +303,7 @@ int net_init_vhost_user(const NetClientOptions *opts, const char *name,
     CharDriverState *chr;

     assert(opts->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
-    vhost_user_opts = opts->u.vhost_user;
+    vhost_user_opts = opts->u.vhost_user.data;

     chr = net_vhost_parse_chardev(vhost_user_opts, errp);
     if (!chr) {
diff --git a/numa.c b/numa.c
index da27bf8..572712c 100644
--- a/numa.c
+++ b/numa.c
@@ -228,7 +228,7 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)

     switch (object->type) {
     case NUMA_OPTIONS_KIND_NODE:
-        numa_node_parse(object->u.node, opts, &err);
+        numa_node_parse(object->u.node.data, opts, &err);
         if (err) {
             goto error;
         }
@@ -482,7 +482,7 @@ static void numa_stat_memory_devices(uint64_t node_mem[])
         if (value) {
             switch (value->type) {
             case MEMORY_DEVICE_INFO_KIND_DIMM:
-                node_mem[value->u.dimm->node] += value->u.dimm->size;
+                node_mem[value->u.dimm.data->node] += value->u.dimm.data->size;
                 break;
             default:
                 break;
diff --git a/qemu-char.c b/qemu-char.c
index 6a0fc74..e23d64f 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -96,16 +96,18 @@ static char *SocketAddress_to_str(const char *prefix, SocketAddress *addr,
     switch (addr->type) {
     case SOCKET_ADDRESS_KIND_INET:
         return g_strdup_printf("%s%s:%s:%s%s", prefix,
-                               is_telnet ? "telnet" : "tcp", addr->u.inet->host,
-                               addr->u.inet->port, is_listen ? ",server" : "");
+                               is_telnet ? "telnet" : "tcp",
+                               addr->u.inet.data->host,
+                               addr->u.inet.data->port,
+                               is_listen ? ",server" : "");
         break;
     case SOCKET_ADDRESS_KIND_UNIX:
         return g_strdup_printf("%sunix:%s%s", prefix,
-                               addr->u.q_unix->path,
+                               addr->u.q_unix.data->path,
                                is_listen ? ",server" : "");
         break;
     case SOCKET_ADDRESS_KIND_FD:
-        return g_strdup_printf("%sfd:%s%s", prefix, addr->u.fd->str,
+        return g_strdup_printf("%sfd:%s%s", prefix, addr->u.fd.data->str,
                                is_listen ? ",server" : "");
         break;
     default:
@@ -420,7 +422,7 @@ static CharDriverState *qemu_chr_open_null(const char *id,
                                            Error **errp)
 {
     CharDriverState *chr;
-    ChardevCommon *common = backend->u.null;
+    ChardevCommon *common = backend->u.null.data;

     chr = qemu_chr_alloc(common, errp);
     if (!chr) {
@@ -721,7 +723,7 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
                                           ChardevBackend *backend,
                                           ChardevReturn *ret, Error **errp)
 {
-    ChardevMux *mux = backend->u.mux;
+    ChardevMux *mux = backend->u.mux.data;
     CharDriverState *chr, *drv;
     MuxDriver *d;
     ChardevCommon *common = qapi_ChardevMux_base(mux);
@@ -1038,7 +1040,7 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
                                            ChardevReturn *ret,
                                            Error **errp)
 {
-    ChardevHostdev *opts = backend->u.pipe;
+    ChardevHostdev *opts = backend->u.pipe.data;
     int fd_in, fd_out;
     char *filename_in;
     char *filename_out;
@@ -1120,7 +1122,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
                                             ChardevReturn *ret,
                                             Error **errp)
 {
-    ChardevStdio *opts = backend->u.stdio;
+    ChardevStdio *opts = backend->u.stdio.data;
     CharDriverState *chr;
     struct sigaction act;
     ChardevCommon *common = qapi_ChardevStdio_base(opts);
@@ -1366,7 +1368,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
     PtyCharDriver *s;
     int master_fd, slave_fd;
     char pty_name[PATH_MAX];
-    ChardevCommon *common = backend->u.pty;
+    ChardevCommon *common = backend->u.pty.data;

     master_fd = qemu_openpty_raw(&slave_fd, pty_name);
     if (master_fd < 0) {
@@ -2137,7 +2139,7 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
                                            ChardevReturn *ret,
                                            Error **errp)
 {
-    ChardevHostdev *opts = backend->u.pipe;
+    ChardevHostdev *opts = backend->u.pipe.data;
     const char *filename = opts->device;
     CharDriverState *chr;
     WinCharState *s;
@@ -2183,7 +2185,7 @@ static CharDriverState *qemu_chr_open_win_con(const char *id,
                                               ChardevReturn *ret,
                                               Error **errp)
 {
-    ChardevCommon *common = qapi_ChardevDummy_base(backend->u.console);
+    ChardevCommon *common = qapi_ChardevDummy_base(backend->u.console.data);
     return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE),
                                   common, errp);
 }
@@ -2333,7 +2335,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
     WinStdioCharState *stdio;
     DWORD              dwMode;
     int                is_console = 0;
-    ChardevCommon *common = qapi_ChardevStdio_base(backend->u.stdio);
+    ChardevCommon *common = qapi_ChardevStdio_base(backend->u.stdio.data);

     chr   = qemu_chr_alloc(common, errp);
     if (!chr) {
@@ -2968,7 +2970,7 @@ static void tcp_chr_tls_init(CharDriverState *chr)
     } else {
         tioc = qio_channel_tls_new_client(
             s->ioc, s->tls_creds,
-            s->addr->u.inet->host,
+            s->addr->u.inet.data->host,
             &err);
     }
     if (tioc == NULL) {
@@ -3215,7 +3217,7 @@ static CharDriverState *qemu_chr_open_ringbuf(const char *id,
                                               ChardevReturn *ret,
                                               Error **errp)
 {
-    ChardevRingbuf *opts = backend->u.ringbuf;
+    ChardevRingbuf *opts = backend->u.ringbuf.data;
     ChardevCommon *common = qapi_ChardevRingbuf_base(opts);
     CharDriverState *chr;
     RingBufCharDriver *d;
@@ -3512,7 +3514,7 @@ static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: file: no filename given");
         return;
     }
-    file = backend->u.file = g_new0(ChardevFile, 1);
+    file = backend->u.file.data = g_new0(ChardevFile, 1);
     qemu_chr_parse_common(opts, qapi_ChardevFile_base(file));
     file->out = g_strdup(path);

@@ -3525,7 +3527,7 @@ static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend,
 {
     ChardevStdio *stdio;

-    stdio = backend->u.stdio = g_new0(ChardevStdio, 1);
+    stdio = backend->u.stdio.data = g_new0(ChardevStdio, 1);
     qemu_chr_parse_common(opts, qapi_ChardevStdio_base(stdio));
     stdio->has_signal = true;
     stdio->signal = qemu_opt_get_bool(opts, "signal", true);
@@ -3542,7 +3544,7 @@ static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: serial/tty: no device path given");
         return;
     }
-    serial = backend->u.serial = g_new0(ChardevHostdev, 1);
+    serial = backend->u.serial.data = g_new0(ChardevHostdev, 1);
     qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(serial));
     serial->device = g_strdup(device);
 }
@@ -3559,7 +3561,7 @@ static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: parallel: no device path given");
         return;
     }
-    parallel = backend->u.parallel = g_new0(ChardevHostdev, 1);
+    parallel = backend->u.parallel.data = g_new0(ChardevHostdev, 1);
     qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(parallel));
     parallel->device = g_strdup(device);
 }
@@ -3575,7 +3577,7 @@ static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: pipe: no device path given");
         return;
     }
-    dev = backend->u.pipe = g_new0(ChardevHostdev, 1);
+    dev = backend->u.pipe.data = g_new0(ChardevHostdev, 1);
     qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(dev));
     dev->device = g_strdup(device);
 }
@@ -3586,7 +3588,7 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
     int val;
     ChardevRingbuf *ringbuf;

-    ringbuf = backend->u.ringbuf = g_new0(ChardevRingbuf, 1);
+    ringbuf = backend->u.ringbuf.data = g_new0(ChardevRingbuf, 1);
     qemu_chr_parse_common(opts, qapi_ChardevRingbuf_base(ringbuf));

     val = qemu_opt_get_size(opts, "size", 0);
@@ -3606,7 +3608,7 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: mux: no chardev given");
         return;
     }
-    mux = backend->u.mux = g_new0(ChardevMux, 1);
+    mux = backend->u.mux.data = g_new0(ChardevMux, 1);
     qemu_chr_parse_common(opts, qapi_ChardevMux_base(mux));
     mux->chardev = g_strdup(chardev);
 }
@@ -3642,7 +3644,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
         }
     }

-    sock = backend->u.socket = g_new0(ChardevSocket, 1);
+    sock = backend->u.socket.data = g_new0(ChardevSocket, 1);
     qemu_chr_parse_common(opts, qapi_ChardevSocket_base(sock));

     sock->has_nodelay = true;
@@ -3661,12 +3663,12 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
     if (path) {
         UnixSocketAddress *q_unix;
         addr->type = SOCKET_ADDRESS_KIND_UNIX;
-        q_unix = addr->u.q_unix = g_new0(UnixSocketAddress, 1);
+        q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
         q_unix->path = g_strdup(path);
     } else {
         addr->type = SOCKET_ADDRESS_KIND_INET;
-        addr->u.inet = g_new0(InetSocketAddress, 1);
-        *addr->u.inet = (InetSocketAddress) {
+        addr->u.inet.data = g_new0(InetSocketAddress, 1);
+        *addr->u.inet.data = (InetSocketAddress) {
             .host = g_strdup(host),
             .port = g_strdup(port),
             .has_to = qemu_opt_get(opts, "to"),
@@ -3709,13 +3711,13 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
         has_local = true;
     }

-    udp = backend->u.udp = g_new0(ChardevUdp, 1);
+    udp = backend->u.udp.data = g_new0(ChardevUdp, 1);
     qemu_chr_parse_common(opts, qapi_ChardevUdp_base(udp));

     addr = g_new0(SocketAddress, 1);
     addr->type = SOCKET_ADDRESS_KIND_INET;
-    addr->u.inet = g_new0(InetSocketAddress, 1);
-    *addr->u.inet = (InetSocketAddress) {
+    addr->u.inet.data = g_new0(InetSocketAddress, 1);
+    *addr->u.inet.data = (InetSocketAddress) {
         .host = g_strdup(host),
         .port = g_strdup(port),
         .has_ipv4 = qemu_opt_get(opts, "ipv4"),
@@ -3729,8 +3731,8 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
         udp->has_local = true;
         addr = g_new0(SocketAddress, 1);
         addr->type = SOCKET_ADDRESS_KIND_INET;
-        addr->u.inet = g_new0(InetSocketAddress, 1);
-        *addr->u.inet = (InetSocketAddress) {
+        addr->u.inet.data = g_new0(InetSocketAddress, 1);
+        *addr->u.inet.data = (InetSocketAddress) {
             .host = g_strdup(localaddr),
             .port = g_strdup(localport),
         };
@@ -3817,7 +3819,7 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
     } else {
         ChardevCommon *cc = g_new0(ChardevCommon, 1);
         qemu_chr_parse_common(opts, cc);
-        backend->u.null = cc; /* Any ChardevCommon member would work */
+        backend->u.null.data = cc; /* Any ChardevCommon member would work */
     }

     ret = qmp_chardev_add(bid ? bid : id, backend, errp);
@@ -3829,9 +3831,9 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
         qapi_free_ChardevBackend(backend);
         qapi_free_ChardevReturn(ret);
         backend = g_new0(ChardevBackend, 1);
-        backend->u.mux = g_new0(ChardevMux, 1);
+        backend->u.mux.data = g_new0(ChardevMux, 1);
         backend->type = CHARDEV_BACKEND_KIND_MUX;
-        backend->u.mux->chardev = g_strdup(bid);
+        backend->u.mux.data->chardev = g_strdup(bid);
         ret = qmp_chardev_add(id, backend, errp);
         if (!ret) {
             chr = qemu_chr_find(bid);
@@ -4144,7 +4146,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
                                               ChardevReturn *ret,
                                               Error **errp)
 {
-    ChardevFile *file = backend->u.file;
+    ChardevFile *file = backend->u.file.data;
     ChardevCommon *common = qapi_ChardevFile_base(file);
     HANDLE out;

@@ -4167,7 +4169,7 @@ static CharDriverState *qmp_chardev_open_serial(const char *id,
                                                 ChardevReturn *ret,
                                                 Error **errp)
 {
-    ChardevHostdev *serial = backend->u.serial;
+    ChardevHostdev *serial = backend->u.serial.data;
     ChardevCommon *common = qapi_ChardevHostdev_base(serial);
     return qemu_chr_open_win_path(serial->device, common, errp);
 }
@@ -4191,7 +4193,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
                                               ChardevReturn *ret,
                                               Error **errp)
 {
-    ChardevFile *file = backend->u.file;
+    ChardevFile *file = backend->u.file.data;
     ChardevCommon *common = qapi_ChardevFile_base(file);
     int flags, in = -1, out;

@@ -4225,7 +4227,7 @@ static CharDriverState *qmp_chardev_open_serial(const char *id,
                                                 ChardevReturn *ret,
                                                 Error **errp)
 {
-    ChardevHostdev *serial = backend->u.serial;
+    ChardevHostdev *serial = backend->u.serial.data;
     ChardevCommon *common = qapi_ChardevHostdev_base(serial);
     int fd;

@@ -4244,7 +4246,7 @@ static CharDriverState *qmp_chardev_open_parallel(const char *id,
                                                   ChardevReturn *ret,
                                                   Error **errp)
 {
-    ChardevHostdev *parallel = backend->u.parallel;
+    ChardevHostdev *parallel = backend->u.parallel.data;
     ChardevCommon *common = qapi_ChardevHostdev_base(parallel);
     int fd;

@@ -4290,7 +4292,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
 {
     CharDriverState *chr;
     TCPCharDriver *s;
-    ChardevSocket *sock = backend->u.socket;
+    ChardevSocket *sock = backend->u.socket.data;
     SocketAddress *addr = sock->addr;
     bool do_nodelay     = sock->has_nodelay ? sock->nodelay : false;
     bool is_listen      = sock->has_server  ? sock->server  : true;
@@ -4396,7 +4398,7 @@ static CharDriverState *qmp_chardev_open_udp(const char *id,
                                              ChardevReturn *ret,
                                              Error **errp)
 {
-    ChardevUdp *udp = backend->u.udp;
+    ChardevUdp *udp = backend->u.udp.data;
     ChardevCommon *common = qapi_ChardevUdp_base(udp);
     QIOChannelSocket *sioc = qio_channel_socket_new();

diff --git a/qemu-nbd.c b/qemu-nbd.c
index a5c1d95..ac0b0e3 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -377,12 +377,12 @@ static SocketAddress *nbd_build_socket_address(const char *sockpath,
     saddr = g_new0(SocketAddress, 1);
     if (sockpath) {
         saddr->type = SOCKET_ADDRESS_KIND_UNIX;
-        saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
-        saddr->u.q_unix->path = g_strdup(sockpath);
+        saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
+        saddr->u.q_unix.data->path = g_strdup(sockpath);
     } else {
         InetSocketAddress *inet;
         saddr->type = SOCKET_ADDRESS_KIND_INET;
-        inet = saddr->u.inet = g_new0(InetSocketAddress, 1);
+        inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1);
         inet->host = g_strdup(bindto);
         if (port) {
             inet->port = g_strdup(port);
diff --git a/replay/replay-input.c b/replay/replay-input.c
index c38af50..2d5d919 100644
--- a/replay/replay-input.c
+++ b/replay/replay-input.c
@@ -54,16 +54,16 @@ void replay_save_input_event(InputEvent *evt)

     switch (evt->type) {
     case INPUT_EVENT_KIND_KEY:
-        key = evt->u.key;
+        key = evt->u.key.data;
         replay_put_dword(key->key->type);

         switch (key->key->type) {
         case KEY_VALUE_KIND_NUMBER:
-            replay_put_qword(key->key->u.number);
+            replay_put_qword(key->key->u.number.data);
             replay_put_byte(key->down);
             break;
         case KEY_VALUE_KIND_QCODE:
-            replay_put_dword(key->key->u.qcode);
+            replay_put_dword(key->key->u.qcode.data);
             replay_put_byte(key->down);
             break;
         case KEY_VALUE_KIND__MAX:
@@ -72,17 +72,17 @@ void replay_save_input_event(InputEvent *evt)
         }
         break;
     case INPUT_EVENT_KIND_BTN:
-        btn = evt->u.btn;
+        btn = evt->u.btn.data;
         replay_put_dword(btn->button);
         replay_put_byte(btn->down);
         break;
     case INPUT_EVENT_KIND_REL:
-        move = evt->u.rel;
+        move = evt->u.rel.data;
         replay_put_dword(move->axis);
         replay_put_qword(move->value);
         break;
     case INPUT_EVENT_KIND_ABS:
-        move = evt->u.abs;
+        move = evt->u.abs.data;
         replay_put_dword(move->axis);
         replay_put_qword(move->value);
         break;
@@ -105,17 +105,17 @@ InputEvent *replay_read_input_event(void)
     evt.type = replay_get_dword();
     switch (evt.type) {
     case INPUT_EVENT_KIND_KEY:
-        evt.u.key = &key;
-        evt.u.key->key->type = replay_get_dword();
+        evt.u.key.data = &key;
+        evt.u.key.data->key->type = replay_get_dword();

-        switch (evt.u.key->key->type) {
+        switch (evt.u.key.data->key->type) {
         case KEY_VALUE_KIND_NUMBER:
-            evt.u.key->key->u.number = replay_get_qword();
-            evt.u.key->down = replay_get_byte();
+            evt.u.key.data->key->u.number.data = replay_get_qword();
+            evt.u.key.data->down = replay_get_byte();
             break;
         case KEY_VALUE_KIND_QCODE:
-            evt.u.key->key->u.qcode = (QKeyCode)replay_get_dword();
-            evt.u.key->down = replay_get_byte();
+            evt.u.key.data->key->u.qcode.data = (QKeyCode)replay_get_dword();
+            evt.u.key.data->down = replay_get_byte();
             break;
         case KEY_VALUE_KIND__MAX:
             /* keep gcc happy */
@@ -123,19 +123,19 @@ InputEvent *replay_read_input_event(void)
         }
         break;
     case INPUT_EVENT_KIND_BTN:
-        evt.u.btn = &btn;
-        evt.u.btn->button = (InputButton)replay_get_dword();
-        evt.u.btn->down = replay_get_byte();
+        evt.u.btn.data = &btn;
+        evt.u.btn.data->button = (InputButton)replay_get_dword();
+        evt.u.btn.data->down = replay_get_byte();
         break;
     case INPUT_EVENT_KIND_REL:
-        evt.u.rel = &rel;
-        evt.u.rel->axis = (InputAxis)replay_get_dword();
-        evt.u.rel->value = replay_get_qword();
+        evt.u.rel.data = &rel;
+        evt.u.rel.data->axis = (InputAxis)replay_get_dword();
+        evt.u.rel.data->value = replay_get_qword();
         break;
     case INPUT_EVENT_KIND_ABS:
-        evt.u.abs = &abs;
-        evt.u.abs->axis = (InputAxis)replay_get_dword();
-        evt.u.abs->value = replay_get_qword();
+        evt.u.abs.data = &abs;
+        evt.u.abs.data->axis = (InputAxis)replay_get_dword();
+        evt.u.abs.data->value = replay_get_qword();
         break;
     case INPUT_EVENT_KIND__MAX:
         /* keep gcc happy */
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 21885c5..351fcaa 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -305,9 +305,10 @@ static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
                                                 ChardevReturn *ret,
                                                 Error **errp)
 {
-    const char *type = backend->u.spicevmc->type;
+    ChardevSpiceChannel *spicevmc = backend->u.spicevmc.data;
+    const char *type = spicevmc->type;
     const char **psubtype = spice_server_char_device_recognized_subtypes();
-    ChardevCommon *common = qapi_ChardevSpiceChannel_base(backend->u.spicevmc);
+    ChardevCommon *common = qapi_ChardevSpiceChannel_base(spicevmc);

     for (; *psubtype != NULL; ++psubtype) {
         if (strcmp(type, *psubtype) == 0) {
@@ -329,8 +330,9 @@ static CharDriverState *qemu_chr_open_spice_port(const char *id,
                                                  ChardevReturn *ret,
                                                  Error **errp)
 {
-    const char *name = backend->u.spiceport->fqdn;
-    ChardevCommon *common = qapi_ChardevSpicePort_base(backend->u.spiceport);
+    ChardevSpicePort *spiceport = backend->u.spiceport.data;
+    const char *name = spiceport->fqdn;
+    ChardevCommon *common = qapi_ChardevSpicePort_base(spiceport);
     CharDriverState *chr;
     SpiceCharDriver *s;

@@ -372,7 +374,7 @@ static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: spice channel: no name given");
         return;
     }
-    spicevmc = backend->u.spicevmc = g_new0(ChardevSpiceChannel, 1);
+    spicevmc = backend->u.spicevmc.data = g_new0(ChardevSpiceChannel, 1);
     qemu_chr_parse_common(opts, qapi_ChardevSpiceChannel_base(spicevmc));
     spicevmc->type = g_strdup(name);
 }
@@ -387,7 +389,7 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: spice port: no name given");
         return;
     }
-    spiceport = backend->u.spiceport = g_new0(ChardevSpicePort, 1);
+    spiceport = backend->u.spiceport.data = g_new0(ChardevSpicePort, 1);
     qemu_chr_parse_common(opts, qapi_ChardevSpicePort_base(spiceport));
     spiceport->fqdn = g_strdup(name);
 }
diff --git a/tests/test-io-channel-socket.c b/tests/test-io-channel-socket.c
index afe46d5..494fabf 100644
--- a/tests/test-io-channel-socket.c
+++ b/tests/test-io-channel-socket.c
@@ -120,8 +120,8 @@ static void test_io_channel_setup_sync(SocketAddress *listen_addr,
         SocketAddress *laddr = qio_channel_socket_get_local_address(
             lioc, &error_abort);

-        g_free(connect_addr->u.inet->port);
-        connect_addr->u.inet->port = g_strdup(laddr->u.inet->port);
+        g_free(connect_addr->u.inet.data->port);
+        connect_addr->u.inet.data->port = g_strdup(laddr->u.inet.data->port);

         qapi_free_SocketAddress(laddr);
     }
@@ -181,8 +181,8 @@ static void test_io_channel_setup_async(SocketAddress *listen_addr,
         SocketAddress *laddr = qio_channel_socket_get_local_address(
             lioc, &error_abort);

-        g_free(connect_addr->u.inet->port);
-        connect_addr->u.inet->port = g_strdup(laddr->u.inet->port);
+        g_free(connect_addr->u.inet.data->port);
+        connect_addr->u.inet.data->port = g_strdup(laddr->u.inet.data->port);

         qapi_free_SocketAddress(laddr);
     }
@@ -283,15 +283,15 @@ static void test_io_channel_ipv4(bool async)
     SocketAddress *connect_addr = g_new0(SocketAddress, 1);

     listen_addr->type = SOCKET_ADDRESS_KIND_INET;
-    listen_addr->u.inet = g_new0(InetSocketAddress, 1);
-    *listen_addr->u.inet = (InetSocketAddress) {
+    listen_addr->u.inet.data = g_new0(InetSocketAddress, 1);
+    *listen_addr->u.inet.data = (InetSocketAddress) {
         .host = g_strdup("127.0.0.1"),
         .port = NULL, /* Auto-select */
     };

     connect_addr->type = SOCKET_ADDRESS_KIND_INET;
-    connect_addr->u.inet = g_new0(InetSocketAddress, 1);
-    *connect_addr->u.inet = (InetSocketAddress) {
+    connect_addr->u.inet.data = g_new0(InetSocketAddress, 1);
+    *connect_addr->u.inet.data = (InetSocketAddress) {
         .host = g_strdup("127.0.0.1"),
         .port = NULL, /* Filled in later */
     };
@@ -321,15 +321,15 @@ static void test_io_channel_ipv6(bool async)
     SocketAddress *connect_addr = g_new0(SocketAddress, 1);

     listen_addr->type = SOCKET_ADDRESS_KIND_INET;
-    listen_addr->u.inet = g_new0(InetSocketAddress, 1);
-    *listen_addr->u.inet = (InetSocketAddress) {
+    listen_addr->u.inet.data = g_new0(InetSocketAddress, 1);
+    *listen_addr->u.inet.data = (InetSocketAddress) {
         .host = g_strdup("::1"),
         .port = NULL, /* Auto-select */
     };

     connect_addr->type = SOCKET_ADDRESS_KIND_INET;
-    connect_addr->u.inet = g_new0(InetSocketAddress, 1);
-    *connect_addr->u.inet = (InetSocketAddress) {
+    connect_addr->u.inet.data = g_new0(InetSocketAddress, 1);
+    *connect_addr->u.inet.data = (InetSocketAddress) {
         .host = g_strdup("::1"),
         .port = NULL, /* Filled in later */
     };
@@ -361,12 +361,12 @@ static void test_io_channel_unix(bool async)

 #define TEST_SOCKET "test-io-channel-socket.sock"
     listen_addr->type = SOCKET_ADDRESS_KIND_UNIX;
-    listen_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
-    listen_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
+    listen_addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
+    listen_addr->u.q_unix.data->path = g_strdup(TEST_SOCKET);

     connect_addr->type = SOCKET_ADDRESS_KIND_UNIX;
-    connect_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
-    connect_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
+    connect_addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
+    connect_addr->u.q_unix.data->path = g_strdup(TEST_SOCKET);

     test_io_channel(async, listen_addr, connect_addr, true);

@@ -410,12 +410,12 @@ static void test_io_channel_unix_fd_pass(void)
     fdsend[2] = testfd;

     listen_addr->type = SOCKET_ADDRESS_KIND_UNIX;
-    listen_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
-    listen_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
+    listen_addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
+    listen_addr->u.q_unix.data->path = g_strdup(TEST_SOCKET);

     connect_addr->type = SOCKET_ADDRESS_KIND_UNIX;
-    connect_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
-    connect_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
+    connect_addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
+    connect_addr->u.q_unix.data->path = g_strdup(TEST_SOCKET);

     test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);

diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 650ba46..14a9ebb 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -69,7 +69,7 @@ __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a,
     __org_qemu_x_Union1 *ret = g_new0(__org_qemu_x_Union1, 1);

     ret->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH;
-    ret->u.__org_qemu_x_branch = strdup("blah1");
+    ret->u.__org_qemu_x_branch.data = strdup("blah1");

     /* Also test that 'wchar-t' was munged to 'q_wchar_t' */
     if (b && b->value && !b->value->has_q_wchar_t) {
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index b05da5b..5941e90 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -477,63 +477,64 @@ static void test_native_list_integer_helper(TestInputVisitorData *data,
     switch (kind) {
     case USER_DEF_NATIVE_LIST_UNION_KIND_INTEGER: {
         intList *elem = NULL;
-        for (i = 0, elem = cvalue->u.integer; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.integer.data;
+             elem; elem = elem->next, i++) {
             g_assert_cmpint(elem->value, ==, i);
         }
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_S8: {
         int8List *elem = NULL;
-        for (i = 0, elem = cvalue->u.s8; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.s8.data; elem; elem = elem->next, i++) {
             g_assert_cmpint(elem->value, ==, i);
         }
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_S16: {
         int16List *elem = NULL;
-        for (i = 0, elem = cvalue->u.s16; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.s16.data; elem; elem = elem->next, i++) {
             g_assert_cmpint(elem->value, ==, i);
         }
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_S32: {
         int32List *elem = NULL;
-        for (i = 0, elem = cvalue->u.s32; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.s32.data; elem; elem = elem->next, i++) {
             g_assert_cmpint(elem->value, ==, i);
         }
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_S64: {
         int64List *elem = NULL;
-        for (i = 0, elem = cvalue->u.s64; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.s64.data; elem; elem = elem->next, i++) {
             g_assert_cmpint(elem->value, ==, i);
         }
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_U8: {
         uint8List *elem = NULL;
-        for (i = 0, elem = cvalue->u.u8; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.u8.data; elem; elem = elem->next, i++) {
             g_assert_cmpint(elem->value, ==, i);
         }
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_U16: {
         uint16List *elem = NULL;
-        for (i = 0, elem = cvalue->u.u16; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.u16.data; elem; elem = elem->next, i++) {
             g_assert_cmpint(elem->value, ==, i);
         }
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_U32: {
         uint32List *elem = NULL;
-        for (i = 0, elem = cvalue->u.u32; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.u32.data; elem; elem = elem->next, i++) {
             g_assert_cmpint(elem->value, ==, i);
         }
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_U64: {
         uint64List *elem = NULL;
-        for (i = 0, elem = cvalue->u.u64; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.u64.data; elem; elem = elem->next, i++) {
             g_assert_cmpint(elem->value, ==, i);
         }
         break;
@@ -635,7 +636,7 @@ static void test_visitor_in_native_list_bool(TestInputVisitorData *data,
     g_assert(cvalue != NULL);
     g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN);

-    for (i = 0, elem = cvalue->u.boolean; elem; elem = elem->next, i++) {
+    for (i = 0, elem = cvalue->u.boolean.data; elem; elem = elem->next, i++) {
         g_assert_cmpint(elem->value, ==, (i % 3 == 0) ? 1 : 0);
     }

@@ -668,7 +669,7 @@ static void test_visitor_in_native_list_string(TestInputVisitorData *data,
     g_assert(cvalue != NULL);
     g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_STRING);

-    for (i = 0, elem = cvalue->u.string; elem; elem = elem->next, i++) {
+    for (i = 0, elem = cvalue->u.string.data; elem; elem = elem->next, i++) {
         gchar str[8];
         sprintf(str, "%d", i);
         g_assert_cmpstr(elem->value, ==, str);
@@ -705,7 +706,7 @@ static void test_visitor_in_native_list_number(TestInputVisitorData *data,
     g_assert(cvalue != NULL);
     g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER);

-    for (i = 0, elem = cvalue->u.number; elem; elem = elem->next, i++) {
+    for (i = 0, elem = cvalue->u.number.data; elem; elem = elem->next, i++) {
         GString *double_expected = g_string_new("");
         GString *double_actual = g_string_new("");

diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index a7f8b45..dc35752 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -493,7 +493,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
     int i;
     switch (cvalue->type) {
     case USER_DEF_NATIVE_LIST_UNION_KIND_INTEGER: {
-        intList **list = &cvalue->u.integer;
+        intList **list = &cvalue->u.integer.data;
         for (i = 0; i < 32; i++) {
             *list = g_new0(intList, 1);
             (*list)->value = i;
@@ -503,7 +503,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_S8: {
-        int8List **list = &cvalue->u.s8;
+        int8List **list = &cvalue->u.s8.data;
         for (i = 0; i < 32; i++) {
             *list = g_new0(int8List, 1);
             (*list)->value = i;
@@ -513,7 +513,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_S16: {
-        int16List **list = &cvalue->u.s16;
+        int16List **list = &cvalue->u.s16.data;
         for (i = 0; i < 32; i++) {
             *list = g_new0(int16List, 1);
             (*list)->value = i;
@@ -523,7 +523,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_S32: {
-        int32List **list = &cvalue->u.s32;
+        int32List **list = &cvalue->u.s32.data;
         for (i = 0; i < 32; i++) {
             *list = g_new0(int32List, 1);
             (*list)->value = i;
@@ -533,7 +533,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_S64: {
-        int64List **list = &cvalue->u.s64;
+        int64List **list = &cvalue->u.s64.data;
         for (i = 0; i < 32; i++) {
             *list = g_new0(int64List, 1);
             (*list)->value = i;
@@ -543,7 +543,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_U8: {
-        uint8List **list = &cvalue->u.u8;
+        uint8List **list = &cvalue->u.u8.data;
         for (i = 0; i < 32; i++) {
             *list = g_new0(uint8List, 1);
             (*list)->value = i;
@@ -553,7 +553,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_U16: {
-        uint16List **list = &cvalue->u.u16;
+        uint16List **list = &cvalue->u.u16.data;
         for (i = 0; i < 32; i++) {
             *list = g_new0(uint16List, 1);
             (*list)->value = i;
@@ -563,7 +563,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_U32: {
-        uint32List **list = &cvalue->u.u32;
+        uint32List **list = &cvalue->u.u32.data;
         for (i = 0; i < 32; i++) {
             *list = g_new0(uint32List, 1);
             (*list)->value = i;
@@ -573,7 +573,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_U64: {
-        uint64List **list = &cvalue->u.u64;
+        uint64List **list = &cvalue->u.u64.data;
         for (i = 0; i < 32; i++) {
             *list = g_new0(uint64List, 1);
             (*list)->value = i;
@@ -583,7 +583,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN: {
-        boolList **list = &cvalue->u.boolean;
+        boolList **list = &cvalue->u.boolean.data;
         for (i = 0; i < 32; i++) {
             *list = g_new0(boolList, 1);
             (*list)->value = (i % 3 == 0);
@@ -593,7 +593,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_STRING: {
-        strList **list = &cvalue->u.string;
+        strList **list = &cvalue->u.string.data;
         for (i = 0; i < 32; i++) {
             *list = g_new0(strList, 1);
             (*list)->value = g_strdup_printf("%d", i);
@@ -603,7 +603,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER: {
-        numberList **list = &cvalue->u.number;
+        numberList **list = &cvalue->u.number.data;
         for (i = 0; i < 32; i++) {
             *list = g_new0(numberList, 1);
             (*list)->value = (double)i / 3;
diff --git a/tpm.c b/tpm.c
index 9ed708e..9a7c711 100644
--- a/tpm.c
+++ b/tpm.c
@@ -262,7 +262,7 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
     case TPM_TYPE_PASSTHROUGH:
         res->options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
         tpo = g_new0(TPMPassthroughOptions, 1);
-        res->options->u.passthrough = tpo;
+        res->options->u.passthrough.data = tpo;
         if (drv->path) {
             tpo->path = g_strdup(drv->path);
             tpo->has_path = true;
diff --git a/ui/console.c b/ui/console.c
index 7db0fd2..76bc8e1 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -2020,7 +2020,7 @@ static VcHandler *vc_handler = text_console_init;
 static CharDriverState *vc_init(const char *id, ChardevBackend *backend,
                                 ChardevReturn *ret, Error **errp)
 {
-    return vc_handler(backend->u.vc, errp);
+    return vc_handler(backend->u.vc.data, errp);
 }

 void register_vc_handler(VcHandler *handler)
@@ -2062,7 +2062,7 @@ static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
     int val;
     ChardevVC *vc;

-    vc = backend->u.vc = g_new0(ChardevVC, 1);
+    vc = backend->u.vc.data = g_new0(ChardevVC, 1);
     qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));

     val = qemu_opt_get_number(opts, "width", 0);
diff --git a/ui/input-keymap.c b/ui/input-keymap.c
index fd2c09d..f1e700d 100644
--- a/ui/input-keymap.c
+++ b/ui/input-keymap.c
@@ -141,10 +141,10 @@ static int number_to_qcode[0x100];
 int qemu_input_key_value_to_number(const KeyValue *value)
 {
     if (value->type == KEY_VALUE_KIND_QCODE) {
-        return qcode_to_number[value->u.qcode];
+        return qcode_to_number[value->u.qcode.data];
     } else {
         assert(value->type == KEY_VALUE_KIND_NUMBER);
-        return value->u.number;
+        return value->u.number.data;
     }
 }

@@ -168,10 +168,10 @@ int qemu_input_key_number_to_qcode(uint8_t nr)
 int qemu_input_key_value_to_qcode(const KeyValue *value)
 {
     if (value->type == KEY_VALUE_KIND_QCODE) {
-        return value->u.qcode;
+        return value->u.qcode.data;
     } else {
         assert(value->type == KEY_VALUE_KIND_NUMBER);
-        return qemu_input_key_number_to_qcode(value->u.number);
+        return qemu_input_key_number_to_qcode(value->u.number.data);
     }
 }

@@ -182,7 +182,7 @@ int qemu_input_key_value_to_scancode(const KeyValue *value, bool down,
     int count = 0;

     if (value->type == KEY_VALUE_KIND_QCODE &&
-        value->u.qcode == Q_KEY_CODE_PAUSE) {
+        value->u.qcode.data == Q_KEY_CODE_PAUSE) {
         /* specific case */
         int v = down ? 0 : 0x80;
         codes[count++] = 0xe1;
diff --git a/ui/input-legacy.c b/ui/input-legacy.c
index 5119fe8..d1df7b9 100644
--- a/ui/input-legacy.c
+++ b/ui/input-legacy.c
@@ -110,7 +110,7 @@ static void legacy_kbd_event(DeviceState *dev, QemuConsole *src,
 {
     QEMUPutKbdEntry *entry = (QEMUPutKbdEntry *)dev;
     int scancodes[3], i, count;
-    InputKeyEvent *key = evt->u.key;
+    InputKeyEvent *key = evt->u.key.data;

     if (!entry || !entry->put_kbd) {
         return;
@@ -156,7 +156,7 @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src,

     switch (evt->type) {
     case INPUT_EVENT_KIND_BTN:
-        btn = evt->u.btn;
+        btn = evt->u.btn.data;
         if (btn->down) {
             s->buttons |= bmap[btn->button];
         } else {
@@ -179,11 +179,11 @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src,
         }
         break;
     case INPUT_EVENT_KIND_ABS:
-        move = evt->u.abs;
+        move = evt->u.abs.data;
         s->axis[move->axis] = move->value;
         break;
     case INPUT_EVENT_KIND_REL:
-        move = evt->u.rel;
+        move = evt->u.rel.data;
         s->axis[move->axis] += move->value;
         break;
     default:
diff --git a/ui/input.c b/ui/input.c
index 1e81c25..79b1650 100644
--- a/ui/input.c
+++ b/ui/input.c
@@ -168,7 +168,7 @@ void qmp_x_input_send_event(bool has_console, int64_t console,

 static void qemu_input_transform_abs_rotate(InputEvent *evt)
 {
-    InputMoveEvent *move = evt->u.abs;
+    InputMoveEvent *move = evt->u.abs.data;
     switch (graphic_rotate) {
     case 90:
         if (move->axis == INPUT_AXIS_X) {
@@ -205,16 +205,16 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
     }
     switch (evt->type) {
     case INPUT_EVENT_KIND_KEY:
-        key = evt->u.key;
+        key = evt->u.key.data;
         switch (key->key->type) {
         case KEY_VALUE_KIND_NUMBER:
-            qcode = qemu_input_key_number_to_qcode(key->key->u.number);
+            qcode = qemu_input_key_number_to_qcode(key->key->u.number.data);
             name = QKeyCode_lookup[qcode];
-            trace_input_event_key_number(idx, key->key->u.number,
+            trace_input_event_key_number(idx, key->key->u.number.data,
                                          name, key->down);
             break;
         case KEY_VALUE_KIND_QCODE:
-            name = QKeyCode_lookup[key->key->u.qcode];
+            name = QKeyCode_lookup[key->key->u.qcode.data];
             trace_input_event_key_qcode(idx, name, key->down);
             break;
         case KEY_VALUE_KIND__MAX:
@@ -223,17 +223,17 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
         }
         break;
     case INPUT_EVENT_KIND_BTN:
-        btn = evt->u.btn;
+        btn = evt->u.btn.data;
         name = InputButton_lookup[btn->button];
         trace_input_event_btn(idx, name, btn->down);
         break;
     case INPUT_EVENT_KIND_REL:
-        move = evt->u.rel;
+        move = evt->u.rel.data;
         name = InputAxis_lookup[move->axis];
         trace_input_event_rel(idx, name, move->value);
         break;
     case INPUT_EVENT_KIND_ABS:
-        move = evt->u.abs;
+        move = evt->u.abs.data;
         name = InputAxis_lookup[move->axis];
         trace_input_event_abs(idx, name, move->value);
         break;
@@ -368,10 +368,10 @@ void qemu_input_event_sync(void)
 InputEvent *qemu_input_event_new_key(KeyValue *key, bool down)
 {
     InputEvent *evt = g_new0(InputEvent, 1);
-    evt->u.key = g_new0(InputKeyEvent, 1);
+    evt->u.key.data = g_new0(InputKeyEvent, 1);
     evt->type = INPUT_EVENT_KIND_KEY;
-    evt->u.key->key = key;
-    evt->u.key->down = down;
+    evt->u.key.data->key = key;
+    evt->u.key.data->down = down;
     return evt;
 }

@@ -393,7 +393,7 @@ void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down)
 {
     KeyValue *key = g_new0(KeyValue, 1);
     key->type = KEY_VALUE_KIND_NUMBER;
-    key->u.number = num;
+    key->u.number.data = num;
     qemu_input_event_send_key(src, key, down);
 }

@@ -401,7 +401,7 @@ void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down)
 {
     KeyValue *key = g_new0(KeyValue, 1);
     key->type = KEY_VALUE_KIND_QCODE;
-    key->u.qcode = q;
+    key->u.qcode.data = q;
     qemu_input_event_send_key(src, key, down);
 }

@@ -418,10 +418,10 @@ void qemu_input_event_send_key_delay(uint32_t delay_ms)
 InputEvent *qemu_input_event_new_btn(InputButton btn, bool down)
 {
     InputEvent *evt = g_new0(InputEvent, 1);
-    evt->u.btn = g_new0(InputBtnEvent, 1);
+    evt->u.btn.data = g_new0(InputBtnEvent, 1);
     evt->type = INPUT_EVENT_KIND_BTN;
-    evt->u.btn->button = btn;
-    evt->u.btn->down = down;
+    evt->u.btn.data->button = btn;
+    evt->u.btn.data->down = down;
     return evt;
 }

@@ -472,7 +472,7 @@ InputEvent *qemu_input_event_new_move(InputEventKind kind,
     InputMoveEvent *move = g_new0(InputMoveEvent, 1);

     evt->type = kind;
-    evt->u.rel = move; /* also would work as evt->u.abs */
+    evt->u.rel.data = move; /* also would work as evt->u.abs.data */
     move->axis = axis;
     move->value = value;
     return evt;
diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
index 13a59f5..56e45e3 100644
--- a/ui/vnc-auth-sasl.c
+++ b/ui/vnc-auth-sasl.c
@@ -513,7 +513,8 @@ vnc_socket_ip_addr_string(QIOChannelSocket *ioc,
         error_setg(errp, "Not an inet socket type");
         return NULL;
     }
-    ret = g_strdup_printf("%s;%s", addr->u.inet->host, addr->u.inet->port);
+    ret = g_strdup_printf("%s;%s", addr->u.inet.data->host,
+                          addr->u.inet.data->port);
     qapi_free_SocketAddress(addr);
     return ret;
 }
diff --git a/ui/vnc.c b/ui/vnc.c
index b955e4e..344fc34 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -112,9 +112,9 @@ static void vnc_init_basic_info(SocketAddress *addr,
 {
     switch (addr->type) {
     case SOCKET_ADDRESS_KIND_INET:
-        info->host = g_strdup(addr->u.inet->host);
-        info->service = g_strdup(addr->u.inet->port);
-        if (addr->u.inet->ipv6) {
+        info->host = g_strdup(addr->u.inet.data->host);
+        info->service = g_strdup(addr->u.inet.data->port);
+        if (addr->u.inet.data->ipv6) {
             info->family = NETWORK_ADDRESS_FAMILY_IPV6;
         } else {
             info->family = NETWORK_ADDRESS_FAMILY_IPV4;
@@ -123,7 +123,7 @@ static void vnc_init_basic_info(SocketAddress *addr,

     case SOCKET_ADDRESS_KIND_UNIX:
         info->host = g_strdup("");
-        info->service = g_strdup(addr->u.q_unix->path);
+        info->service = g_strdup(addr->u.q_unix.data->path);
         info->family = NETWORK_ADDRESS_FAMILY_UNIX;
         break;

@@ -385,9 +385,9 @@ VncInfo *qmp_query_vnc(Error **errp)

         switch (addr->type) {
         case SOCKET_ADDRESS_KIND_INET:
-            info->host = g_strdup(addr->u.inet->host);
-            info->service = g_strdup(addr->u.inet->port);
-            if (addr->u.inet->ipv6) {
+            info->host = g_strdup(addr->u.inet.data->host);
+            info->service = g_strdup(addr->u.inet.data->port);
+            if (addr->u.inet.data->ipv6) {
                 info->family = NETWORK_ADDRESS_FAMILY_IPV6;
             } else {
                 info->family = NETWORK_ADDRESS_FAMILY_IPV4;
@@ -396,7 +396,7 @@ VncInfo *qmp_query_vnc(Error **errp)

         case SOCKET_ADDRESS_KIND_UNIX:
             info->host = g_strdup("");
-            info->service = g_strdup(addr->u.q_unix->path);
+            info->service = g_strdup(addr->u.q_unix.data->path);
             info->family = NETWORK_ADDRESS_FAMILY_UNIX;
             break;

@@ -3189,7 +3189,8 @@ char *vnc_display_local_addr(const char *id)
         qapi_free_SocketAddress(addr);
         return NULL;
     }
-    ret = g_strdup_printf("%s;%s", addr->u.inet->host, addr->u.inet->port);
+    ret = g_strdup_printf("%s;%s", addr->u.inet.data->host,
+                          addr->u.inet.data->port);
     qapi_free_SocketAddress(addr);

     return ret;
@@ -3521,8 +3522,8 @@ void vnc_display_open(const char *id, Error **errp)

         if (strncmp(vnc, "unix:", 5) == 0) {
             saddr->type = SOCKET_ADDRESS_KIND_UNIX;
-            saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
-            saddr->u.q_unix->path = g_strdup(vnc + 5);
+            saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
+            saddr->u.q_unix.data->path = g_strdup(vnc + 5);

             if (vs->ws_enabled) {
                 error_setg(errp, "UNIX sockets not supported with websock");
@@ -3532,7 +3533,7 @@ void vnc_display_open(const char *id, Error **errp)
             unsigned long long baseport;
             InetSocketAddress *inet;
             saddr->type = SOCKET_ADDRESS_KIND_INET;
-            inet = saddr->u.inet = g_new0(InetSocketAddress, 1);
+            inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1);
             if (vnc[0] == '[' && vnc[hlen - 1] == ']') {
                 inet->host = g_strndup(vnc + 1, hlen - 2);
             } else {
@@ -3561,8 +3562,8 @@ void vnc_display_open(const char *id, Error **errp)

             if (vs->ws_enabled) {
                 wsaddr->type = SOCKET_ADDRESS_KIND_INET;
-                inet = wsaddr->u.inet = g_new0(InetSocketAddress, 1);
-                inet->host = g_strdup(saddr->u.inet->host);
+                inet = wsaddr->u.inet.data = g_new0(InetSocketAddress, 1);
+                inet->host = g_strdup(saddr->u.inet.data->host);
                 inet->port = g_strdup(websocket);

                 if (to) {
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index ad7c00c..ad0d21d 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -901,8 +901,8 @@ SocketAddress *socket_parse(const char *str, Error **errp)
             goto fail;
         } else {
             addr->type = SOCKET_ADDRESS_KIND_UNIX;
-            addr->u.q_unix = g_new(UnixSocketAddress, 1);
-            addr->u.q_unix->path = g_strdup(str + 5);
+            addr->u.q_unix.data = g_new(UnixSocketAddress, 1);
+            addr->u.q_unix.data->path = g_strdup(str + 5);
         }
     } else if (strstart(str, "fd:", NULL)) {
         if (str[3] == '\0') {
@@ -910,13 +910,13 @@ SocketAddress *socket_parse(const char *str, Error **errp)
             goto fail;
         } else {
             addr->type = SOCKET_ADDRESS_KIND_FD;
-            addr->u.fd = g_new(String, 1);
-            addr->u.fd->str = g_strdup(str + 3);
+            addr->u.fd.data = g_new(String, 1);
+            addr->u.fd.data->str = g_strdup(str + 3);
         }
     } else {
         addr->type = SOCKET_ADDRESS_KIND_INET;
-        addr->u.inet = inet_parse(str, errp);
-        if (addr->u.inet == NULL) {
+        addr->u.inet.data = inet_parse(str, errp);
+        if (addr->u.inet.data == NULL) {
             goto fail;
         }
     }
@@ -934,15 +934,15 @@ int socket_connect(SocketAddress *addr, Error **errp,

     switch (addr->type) {
     case SOCKET_ADDRESS_KIND_INET:
-        fd = inet_connect_saddr(addr->u.inet, errp, callback, opaque);
+        fd = inet_connect_saddr(addr->u.inet.data, errp, callback, opaque);
         break;

     case SOCKET_ADDRESS_KIND_UNIX:
-        fd = unix_connect_saddr(addr->u.q_unix, errp, callback, opaque);
+        fd = unix_connect_saddr(addr->u.q_unix.data, errp, callback, opaque);
         break;

     case SOCKET_ADDRESS_KIND_FD:
-        fd = monitor_get_fd(cur_mon, addr->u.fd->str, errp);
+        fd = monitor_get_fd(cur_mon, addr->u.fd.data->str, errp);
         if (fd >= 0 && callback) {
             qemu_set_nonblock(fd);
             callback(fd, NULL, opaque);
@@ -961,15 +961,15 @@ int socket_listen(SocketAddress *addr, Error **errp)

     switch (addr->type) {
     case SOCKET_ADDRESS_KIND_INET:
-        fd = inet_listen_saddr(addr->u.inet, 0, false, errp);
+        fd = inet_listen_saddr(addr->u.inet.data, 0, false, errp);
         break;

     case SOCKET_ADDRESS_KIND_UNIX:
-        fd = unix_listen_saddr(addr->u.q_unix, false, errp);
+        fd = unix_listen_saddr(addr->u.q_unix.data, false, errp);
         break;

     case SOCKET_ADDRESS_KIND_FD:
-        fd = monitor_get_fd(cur_mon, addr->u.fd->str, errp);
+        fd = monitor_get_fd(cur_mon, addr->u.fd.data->str, errp);
         break;

     default:
@@ -984,7 +984,8 @@ int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp)

     switch (remote->type) {
     case SOCKET_ADDRESS_KIND_INET:
-        fd = inet_dgram_saddr(remote->u.inet, local ? local->u.inet : NULL, errp);
+        fd = inet_dgram_saddr(remote->u.inet.data,
+                              local ? local->u.inet.data : NULL, errp);
         break;

     default:
@@ -1018,7 +1019,7 @@ socket_sockaddr_to_address_inet(struct sockaddr_storage *sa,

     addr = g_new0(SocketAddress, 1);
     addr->type = SOCKET_ADDRESS_KIND_INET;
-    inet = addr->u.inet = g_new0(InetSocketAddress, 1);
+    inet = addr->u.inet.data = g_new0(InetSocketAddress, 1);
     inet->host = g_strdup(host);
     inet->port = g_strdup(serv);
     if (sa->ss_family == AF_INET) {
@@ -1042,10 +1043,10 @@ socket_sockaddr_to_address_unix(struct sockaddr_storage *sa,

     addr = g_new0(SocketAddress, 1);
     addr->type = SOCKET_ADDRESS_KIND_UNIX;
-    addr->u.q_unix = g_new0(UnixSocketAddress, 1);
+    addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
     if (su->sun_path[0]) {
-        addr->u.q_unix->path = g_strndup(su->sun_path,
-                                         sizeof(su->sun_path));
+        addr->u.q_unix.data->path = g_strndup(su->sun_path,
+                                              sizeof(su->sun_path));
     }

     return addr;
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 15/19] qapi-visit: Move error check into gen_visit_members_call()
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (13 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 14/19] qapi: Don't special-case simple union wrappers Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-03 11:56   ` Markus Armbruster
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 16/19] qapi: Allow anonymous base for flat union Eric Blake
                   ` (4 subsequent siblings)
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

When first introduced, neither branch of gen_visit_members_call()
would output a goto.  But now that the implicit struct visit
always ends with a goto, we should do the same for regular
struct visits, so that callers don't have to worry about whether
they are creating two identical goto's in a row.

Generated code gets slightly larger; if desired, we could patch
qapi.py:gen_visit_members() to have a mode where it skips the
final goto and leave it up to the callers when to use that mode,
but that adds more maintenance burden when the compiler should
be smart enough to not bloat the .o file just because the .c
file got larger.

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

---
v2: rebase onto s/fields/members/ change
---
 scripts/qapi-visit.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index e281d21..a17ecc1 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -48,6 +48,7 @@ def gen_visit_members_call(typ, direct_name, implicit_name=None):
     visit_type_%(c_type)s_members(v, %(c_name)s, &err);
 ''',
                      c_type=typ.c_name(), c_name=direct_name)
+        ret += gen_err_check()
     return ret


@@ -63,7 +64,6 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)

     if base:
         ret += gen_visit_members_call(base, '(%s *)obj' % base.c_name())
-        ret += gen_err_check()

     ret += gen_visit_members(members, prefix='obj->')

@@ -95,9 +95,9 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
     }
 ''')

-    # 'goto out' produced for base, by gen_visit_members() for each member,
-    # and if variants were present
-    if base or members or variants:
+    # 'goto out' produced for non-empty base, by gen_visit_members() for
+    # each member, and if variants were present
+    if (base and not base.is_empty()) or members or variants:
         ret += mcgen('''

 out:
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 16/19] qapi: Allow anonymous base for flat union
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (14 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 15/19] qapi-visit: Move error check into gen_visit_members_call() Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-03 13:04   ` Markus Armbruster
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 17/19] qapi: Use anonymous base in SchemaInfo Eric Blake
                   ` (3 subsequent siblings)
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Rather than requiring all flat unions to explicitly create
a separate base struct, we can allow the qapi schema to specify
the common members via an inline dictionary. This is similar to
how commands can specify an inline anonymous type for its 'data',
and matches the fact that our code base already has several
flat unions that had to create a separate base type that is used
nowhere but in the union.

The patch also has to tweak things to avoid calling base.c_name()
on an implicit type (since implicit types do not have a name);
we already have qapi_FOO_base() which does the trick nicely.

Now that anonymous bases are legal, we need to rework the
flat-union-bad-base negative test (as previously written, it
forms what is now valid QAPI; tweak it to now provide coverage
of a new error message path), and add a positive test in
qapi-schema-test to use an anonymous base.

Note that this patch only allows anonymous bases for flat
unions; simple unions are enough syntactic sugar that we do
not want to burden them further.  Meanwhile, it would be easy
to modify qapi.py to also allow an anonymous base for structs;
however, there is less of a compelling technical reason to
do so, since you can always write the struct to directly
contain any members that the anonymous base would have
mentioned.

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

---
v2: rebase onto s/fields/members/ change
v1: rebase and rework to use gen_visit_fields_call(), test new error
Previously posted as part of qapi cleanup subset F:
v6: avoid redundant error check in gen_visit_union(), rebase to
earlier gen_err_check() improvements
---
 scripts/qapi.py                            | 11 +++++++++--
 scripts/qapi-types.py                      | 12 +++++++-----
 scripts/qapi-visit.py                      |  6 +++---
 docs/qapi-code-gen.txt                     | 28 ++++++++++++++--------------
 tests/qapi-schema/flat-union-bad-base.err  |  2 +-
 tests/qapi-schema/flat-union-bad-base.json |  5 ++---
 tests/qapi-schema/qapi-schema-test.json    |  6 +-----
 tests/qapi-schema/qapi-schema-test.out     |  9 ++++-----
 8 files changed, 41 insertions(+), 38 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 38121c5..7c76442 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -329,6 +329,8 @@ class QAPISchemaParser(object):


 def find_base_members(base):
+    if isinstance(base, dict):
+        return base
     base_struct_define = find_struct(base)
     if not base_struct_define:
         return None
@@ -562,9 +564,9 @@ def check_union(expr, expr_info):

     # Else, it's a flat union.
     else:
-        # The object must have a string member 'base'.
+        # The object must have a string or dictionary 'base'.
         check_type(expr_info, "'base' for union '%s'" % name,
-                   base, allow_metas=['struct'])
+                   base, allow_dict=True, allow_metas=['struct'])
         if not base:
             raise QAPIExprError(expr_info,
                                 "Flat union '%s' must have a base"
@@ -1039,6 +1041,8 @@ class QAPISchemaMember(object):
             owner = owner[5:]
             if owner.endswith('-arg'):
                 return '(parameter of %s)' % owner[:-4]
+            elif owner.endswith('-base'):
+                return '(base of %s)' % owner[:-5]
             else:
                 assert owner.endswith('-wrapper')
                 # Unreachable and not implemented
@@ -1323,6 +1327,9 @@ class QAPISchema(object):
         base = expr.get('base')
         tag_name = expr.get('discriminator')
         tag_member = None
+        if isinstance(base, dict):
+            base = (self._make_implicit_object_type(
+                    name, info, 'base', self._make_members(base, info)))
         if tag_name:
             variants = [self._make_variant(key, value)
                         for (key, value) in data.iteritems()]
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 1f090e6..161443e 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -72,13 +72,15 @@ struct %(c_name)s {
 ''',
                  c_name=c_name(name))

-    if base:
-        ret += mcgen('''
+    if base and not base.is_empty():
+        if not base.is_implicit():
+            ret += mcgen('''
     /* Members inherited from %(c_name)s: */
 ''',
-                     c_name=base.c_name())
+                         c_name=base.c_name())
         ret += gen_struct_members(base.members)
-        ret += mcgen('''
+        if not base.is_implicit():
+            ret += mcgen('''
     /* Own members: */
 ''')
     ret += gen_struct_members(members)
@@ -238,7 +240,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
     def visit_object_type(self, name, info, base, members, variants):
         self._fwdecl += gen_fwd_object_or_array(name)
         self.decl += gen_object(name, base, members, variants)
-        if base:
+        if base and not base.is_implicit():
             self.decl += gen_upcast(name, base)
         self._gen_type_cleanup(name)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index a17ecc1..aa244cd 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -34,13 +34,12 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
                  c_name=c_name(name))


-def gen_visit_members_call(typ, direct_name, implicit_name=None):
+def gen_visit_members_call(typ, direct_name, implicit_name):
     ret = ''
     assert isinstance(typ, QAPISchemaObjectType)
     if typ.is_empty():
         pass
     elif typ.is_implicit():
-        assert implicit_name
         assert not typ.variants
         ret += gen_visit_members(typ.members, prefix=implicit_name)
     else:
@@ -63,7 +62,8 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
                 c_name=c_name(name))

     if base:
-        ret += gen_visit_members_call(base, '(%s *)obj' % base.c_name())
+        ret += gen_visit_members_call(base, 'qapi_%s_base(obj)' % c_name(name),
+                                      'obj->')

     ret += gen_visit_members(members, prefix='obj->')

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 2519c07..1c8f113 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -283,7 +283,7 @@ better than open-coding the field to be type 'str'.
 === Union types ===

 Usage: { 'union': STRING, 'data': DICT }
-or:    { 'union': STRING, 'data': DICT, 'base': STRUCT-NAME,
+or:    { 'union': STRING, 'data': DICT, 'base': STRUCT-NAME-OR-DICT,
          'discriminator': ENUM-MEMBER-OF-BASE }

 Union types are used to let the user choose between several different
@@ -319,13 +319,16 @@ an implicit C enum 'NameKind' is created, corresponding to the union
 the union can be named 'max', as this would collide with the implicit
 enum.  The value for each branch can be of any type.

-A flat union definition specifies a struct as its base, and
-avoids nesting on the wire.  All branches of the union must be
-complex types, and the top-level fields of the union dictionary on
-the wire will be combination of fields from both the base type and the
-appropriate branch type (when merging two dictionaries, there must be
-no keys in common).  The 'discriminator' field must be the name of an
-enum-typed member of the base struct.
+A flat union definition avoids nesting on the wire, and specifies a
+set of common fields that occur in all variants of the union.  The
+'base' key must specifiy either a type name (the type must be a
+struct, not a union), or a dictionary representing an anonymous type.
+All branches of the union must be complex types, and the top-level
+fields of the union dictionary on the wire will be combination of
+fields from both the base type and the appropriate branch type (when
+merging two dictionaries, there must be no keys in common).  The
+'discriminator' field must be the name of an enum-typed member of the
+base struct.

 The following example enhances the above simple union example by
 adding a common field 'readonly', renaming the discriminator to
@@ -333,10 +336,8 @@ something more applicable, and reducing the number of {} required on
 the wire:

  { 'enum': 'BlockdevDriver', 'data': [ 'file', 'qcow2' ] }
- { 'struct': 'BlockdevCommonOptions',
-   'data': { 'driver': 'BlockdevDriver', 'readonly': 'bool' } }
  { 'union': 'BlockdevOptions',
-   'base': 'BlockdevCommonOptions',
+   'base': { 'driver': 'BlockdevDriver', 'readonly': 'bool' },
    'discriminator': 'driver',
    'data': { 'file': 'FileOptions',
              'qcow2': 'Qcow2Options' } }
@@ -354,7 +355,7 @@ code generator can ensure that branches exist for all values of the
 enum (although the order of the keys need not match the declaration of
 the enum).  In the resulting generated C data types, a flat union is
 represented as a struct with the base member fields included directly,
-and then a union of structures for each branch of the struct.
+and then a union of pointers to 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
@@ -365,10 +366,9 @@ union has a struct with a single member named 'data'.  That is,
 is identical on the wire to:

  { 'enum': 'Enum', 'data': ['one', 'two'] }
- { 'struct': 'Base', 'data': { 'type': 'Enum' } }
  { 'struct': 'Branch1', 'data': { 'data': 'str' } }
  { 'struct': 'Branch2', 'data': { 'data': 'int' } }
- { 'union': 'Flat', 'base': 'Base', 'discriminator': 'type',
+ { 'union': 'Flat': 'base': { 'type': 'Enum' }, 'discriminator': 'type',
    'data': { 'one': 'Branch1', 'two': 'Branch2' } }


diff --git a/tests/qapi-schema/flat-union-bad-base.err b/tests/qapi-schema/flat-union-bad-base.err
index 79b8a71..bee24a2 100644
--- a/tests/qapi-schema/flat-union-bad-base.err
+++ b/tests/qapi-schema/flat-union-bad-base.err
@@ -1 +1 @@
-tests/qapi-schema/flat-union-bad-base.json:9: 'base' for union 'TestUnion' should be a type name
+tests/qapi-schema/flat-union-bad-base.json:8: 'string' (member of TestTypeA) collides with 'string' (base of TestUnion)
diff --git a/tests/qapi-schema/flat-union-bad-base.json b/tests/qapi-schema/flat-union-bad-base.json
index e2e622b..74dd421 100644
--- a/tests/qapi-schema/flat-union-bad-base.json
+++ b/tests/qapi-schema/flat-union-bad-base.json
@@ -1,5 +1,4 @@
-# we require the base to be an existing struct
-# TODO: should we allow an anonymous inline base type?
+# we allow anonymous base, but enforce no duplicate keys
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
 { 'struct': 'TestTypeA',
@@ -7,7 +6,7 @@
 { 'struct': 'TestTypeB',
   'data': { 'integer': 'int' } }
 { 'union': 'TestUnion',
-  'base': { 'enum1': 'TestEnum', 'kind': 'str' },
+  'base': { 'enum1': 'TestEnum', 'string': 'str' },
   'discriminator': 'enum1',
   'data': { 'value1': 'TestTypeA',
             'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 33e8517..a6989d8 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -75,14 +75,10 @@
   'base': 'UserDefZero',
   'data': { 'string': 'str', 'enum1': 'EnumOne' } }

-{ 'struct': 'UserDefUnionBase2',
-  'base': 'UserDefZero',
-  'data': { 'string': 'str', 'enum1': 'QEnumTwo' } }
-
 # this variant of UserDefFlatUnion defaults to a union that uses fields with
 # allocated types to test corner cases in the cleanup/dealloc visitor
 { 'union': 'UserDefFlatUnion2',
-  'base': 'UserDefUnionBase2',
+  'base': { 'string': 'str', 'enum1': 'QEnumTwo' },
   'discriminator': 'enum1',
   'data': { 'value1' : 'UserDefC', # intentional forward reference
             'value2' : 'UserDefB' } }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 6b97fa5..4ba347e 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -8,6 +8,9 @@ object :obj-EVENT_D-arg
     member b: str optional=False
     member c: str optional=True
     member enum3: EnumOne optional=True
+object :obj-UserDefFlatUnion2-base
+    member string: str optional=False
+    member enum1: QEnumTwo optional=False
 object :obj-__org.qemu_x-command-arg
     member a: __org.qemu_x-EnumList optional=False
     member b: __org.qemu_x-StructList optional=False
@@ -121,7 +124,7 @@ object UserDefFlatUnion
     case value2: UserDefB
     case value3: UserDefB
 object UserDefFlatUnion2
-    base UserDefUnionBase2
+    base :obj-UserDefFlatUnion2-base
     tag enum1
     case value1: UserDefC
     case value2: UserDefB
@@ -166,10 +169,6 @@ object UserDefUnionBase
     base UserDefZero
     member string: str optional=False
     member enum1: EnumOne optional=False
-object UserDefUnionBase2
-    base UserDefZero
-    member string: str optional=False
-    member enum1: QEnumTwo optional=False
 object UserDefZero
     member integer: int optional=False
 object WrapAlternate
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 17/19] qapi: Use anonymous base in SchemaInfo
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (15 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 16/19] qapi: Allow anonymous base for flat union Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-03 13:06   ` Markus Armbruster
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 18/19] qapi: Use anonymous base in CpuInfo Eric Blake
                   ` (2 subsequent siblings)
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru

Now that the generator supports it, we might as well use an
anonymous base rather than breaking out a single-use
SchemaInfoBase structure.

Oddly enough, this change does not affect the resulting
introspection output (because we already inline the members of
a base type into an object, and had no independent use of the
base type reachable from a command).

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

---
v2: no change
v1: no change
Previously posted as part of qapi cleanup subset F:
v6: new patch
---
 qapi/introspect.json | 12 +-----------
 1 file changed, 1 insertion(+), 11 deletions(-)

diff --git a/qapi/introspect.json b/qapi/introspect.json
index 9e9369e..3fd81fb 100644
--- a/qapi/introspect.json
+++ b/qapi/introspect.json
@@ -75,16 +75,6 @@
             'command', 'event' ] }

 ##
-# @SchemaInfoBase
-#
-# Members common to any @SchemaInfo.
-#
-# Since: 2.5
-##
-{ 'struct': 'SchemaInfoBase',
-  'data': { 'name': 'str', 'meta-type': 'SchemaMetaType' } }
-
-##
 # @SchemaInfo
 #
 # @name: the entity's name, inherited from @base.
@@ -103,7 +93,7 @@
 # Since: 2.5
 ##
 { 'union': 'SchemaInfo',
-  'base': 'SchemaInfoBase',
+  'base': { 'name': 'str', 'meta-type': 'SchemaMetaType' },
   'discriminator': 'meta-type',
   'data': {
       'builtin': 'SchemaInfoBuiltin',
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 18/19] qapi: Use anonymous base in CpuInfo
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (16 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 17/19] qapi: Use anonymous base in SchemaInfo Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-03 13:08   ` Markus Armbruster
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 19/19] qapi: Make c_type() more OO-like Eric Blake
  2016-03-01 15:02 ` [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Markus Armbruster
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Now that the generator supports it, we might as well use an
anonymous base rather than breaking out a single-use CpuInfoBase
structure.

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

---
v2: no change
v1: no change
Previously posted as part of qapi cleanup subset F:
v6: new patch
---
 scripts/qapi.py  |  2 +-
 qapi-schema.json | 20 ++++++--------------
 2 files changed, 7 insertions(+), 15 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 7c76442..d5f4e28 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -62,8 +62,8 @@ returns_whitelist = [
 # Whitelist of entities allowed to violate case conventions
 case_whitelist = [
     # From QMP:
+    ':obj-CpuInfo-base',    # CPU, visible through query-cpu
     'ACPISlotType',         # DIMM, visible through query-acpi-ospm-status
-    'CpuInfoBase',          # CPU, visible through query-cpu
     'CpuInfoMIPS',          # PC, visible through query-cpu
     'CpuInfoTricore',       # PC, visible through query-cpu
     'InputAxis',            # TODO: drop when x-input-send-event is fixed
diff --git a/qapi-schema.json b/qapi-schema.json
index 66cc364..967f8df 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -753,9 +753,9 @@
   'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 'other' ] }

 ##
-# @CpuInfoBase:
+# @CpuInfo:
 #
-# Common information about a virtual CPU
+# Information about a virtual CPU
 #
 # @CPU: the index of the virtual CPU
 #
@@ -776,18 +776,10 @@
 # Notes: @halted is a transient state that changes frequently.  By the time the
 #        data is sent to the client, the guest may no longer be halted.
 ##
-{ 'struct': 'CpuInfoBase',
-  'data': {'CPU': 'int', 'current': 'bool', 'halted': 'bool',
-           'qom_path': 'str', 'thread_id': 'int', 'arch': 'CpuInfoArch' } }
-
-##
-# @CpuInfo:
-#
-# Information about a virtual CPU
-#
-# Since: 0.14.0
-##
-{ 'union': 'CpuInfo', 'base': 'CpuInfoBase', 'discriminator': 'arch',
+{ 'union': 'CpuInfo',
+  'base': {'CPU': 'int', 'current': 'bool', 'halted': 'bool',
+           'qom_path': 'str', 'thread_id': 'int', 'arch': 'CpuInfoArch' },
+  'discriminator': 'arch',
   'data': { 'x86': 'CpuInfoX86',
             'sparc': 'CpuInfoSPARC',
             'ppc': 'CpuInfoPPC',
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2 19/19] qapi: Make c_type() more OO-like
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (17 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 18/19] qapi: Use anonymous base in CpuInfo Eric Blake
@ 2016-02-25 23:38 ` Eric Blake
  2016-03-03 13:29   ` Markus Armbruster
  2016-03-01 15:02 ` [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Markus Armbruster
  19 siblings, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-02-25 23:38 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

QAPISchemaType.c_type() was a bit awkward.  Rather than having two
optional orthogonal boolean flags that should never both be true,
and where all callers pass a compile-time constant, provide three
different method names that can be overridden as needed, and where
the caller just uses the right variant.  It requires slightly more
Python, but is arguably easier to read.

Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>

---
v2: no change
---
 scripts/qapi.py       | 38 +++++++++++++++++++++++++++++---------
 scripts/qapi-types.py |  2 +-
 2 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index d5f4e28..32f37d8 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -826,8 +826,17 @@ class QAPISchemaVisitor(object):


 class QAPISchemaType(QAPISchemaEntity):
-    def c_type(self, is_param=False, is_unboxed=False):
-        return c_name(self.name) + pointer_suffix
+    # Normal usage of the type, such as declaring a local variable
+    def c_type(self):
+        pass
+
+    # Use of a type in a parameter list
+    def c_param_type(self):
+        return self.c_type()
+
+    # Use of a type in a struct declaration
+    def c_unboxed_type(self):
+        return self.c_type()

     def c_null(self):
         return 'NULL'
@@ -859,8 +868,11 @@ class QAPISchemaBuiltinType(QAPISchemaType):
     def c_name(self):
         return self.name

-    def c_type(self, is_param=False, is_unboxed=False):
-        if is_param and self.name == 'str':
+    def c_type(self):
+        return self._c_type_name
+
+    def c_param_type(self):
+        if self.name == 'str':
             return 'const ' + self._c_type_name
         return self._c_type_name

@@ -893,7 +905,7 @@ class QAPISchemaEnumType(QAPISchemaType):
         # See QAPISchema._make_implicit_enum_type()
         return self.name.endswith('Kind')

-    def c_type(self, is_param=False, is_unboxed=False):
+    def c_type(self):
         return c_name(self.name)

     def member_names(self):
@@ -925,6 +937,9 @@ class QAPISchemaArrayType(QAPISchemaType):
     def is_implicit(self):
         return True

+    def c_type(self):
+        return c_name(self.name) + pointer_suffix
+
     def json_type(self):
         return 'array'

@@ -994,12 +1009,14 @@ class QAPISchemaObjectType(QAPISchemaType):
         assert not self.is_implicit()
         return QAPISchemaType.c_name(self)

-    def c_type(self, is_param=False, is_unboxed=False):
+    def c_type(self):
         assert not self.is_implicit()
-        if is_unboxed:
-            return c_name(self.name)
         return c_name(self.name) + pointer_suffix

+    def c_unboxed_type(self):
+        assert not self.is_implicit()
+        return c_name(self.name)
+
     def json_type(self):
         return 'object'

@@ -1140,6 +1157,9 @@ class QAPISchemaAlternateType(QAPISchemaType):
         for v in self.variants.variants:
             v.check_clash(self.info, seen)

+    def c_type(self):
+        return c_name(self.name) + pointer_suffix
+
     def json_type(self):
         return 'value'

@@ -1634,7 +1654,7 @@ def gen_params(arg_type, extra):
         sep = ', '
         if memb.optional:
             ret += 'bool has_%s, ' % c_name(memb.name)
-        ret += '%s %s' % (memb.type.c_type(is_param=True), c_name(memb.name))
+        ret += '%s %s' % (memb.type.c_param_type(), c_name(memb.name))
     if extra:
         ret += sep + extra
     return ret
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 161443e..ce6d6f9 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -140,7 +140,7 @@ def gen_variants(variants):
             ret += mcgen('''
         %(c_type)s %(c_name)s;
 ''',
-                         c_type=var.type.c_type(is_unboxed=True),
+                         c_type=var.type.c_unboxed_type(),
                          c_name=c_name(var.name))

     ret += mcgen('''
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types
  2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
                   ` (18 preceding siblings ...)
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 19/19] qapi: Make c_type() more OO-like Eric Blake
@ 2016-03-01 15:02 ` Markus Armbruster
  19 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-03-01 15:02 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Eduardo Habkost

Eric Blake <eblake@redhat.com> writes:

> Merge of two previous series:
> https://lists.gnu.org/archive/html/qemu-devel/2016-02/msg05492.html
> https://lists.gnu.org/archive/html/qemu-devel/2016-02/msg04703.html
>
> Depends on Markus' qapi-next branch.
>
> Also available as a tag at this location:
> git fetch git://repo.or.cz/qemu/ericb.git qapi-implicit-v2
>
> and will soon be part of my branch at:
> http://repo.or.cz/qemu/ericb.git/shortlog/refs/heads/qapi

Needs a rebase.  Shouldn't stop review, though.

[...]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v2.5 06/19] ui: Shorten references into InputEvent
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 06/19] ui: Shorten references into InputEvent Eric Blake
@ 2016-03-01 15:32   ` Eric Blake
  0 siblings, 0 replies; 48+ messages in thread
From: Eric Blake @ 2016-03-01 15:32 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, Gerd Hoffmann, armbru, Michael S. Tsirkin

An upcoming patch will alter how simple unions, like InputEvent,
are laid out, which will impact all lines of the form 'evt->u.XXX'.
To minimize the impact of that patch, use a temporary variable to
reduce the number of lines needing modification when an internal
reference within InputEvent changes layout.

There was one instance in hid.c:hid_pointer_event() where the code
was referring to evt->u.rel inside the case label where evt->u.abs
is the correct name; thankfully, both members of the union have the
same type, so it happened to work, but it is now cleaner.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-By: Daniel P. Berrange <berrange@redhat.com>

---
v2.5: rebase to qemu.git master (InputEvent enum name changes)
v2: add R-b
---
 hw/char/escc.c              | 12 +++++-----
 hw/input/hid.c              | 36 +++++++++++++++++-------------
 hw/input/ps2.c              | 27 ++++++++++++++---------
 hw/input/virtio-input-hid.c | 33 ++++++++++++++++-----------
 replay/replay-input.c       | 31 ++++++++++++++++----------
 ui/input-legacy.c           | 25 ++++++++++++---------
 ui/input.c                  | 54 ++++++++++++++++++++++++++-------------------
 7 files changed, 129 insertions(+), 89 deletions(-)

diff --git a/hw/char/escc.c b/hw/char/escc.c
index 98a1c21..c7a24ac 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -842,14 +842,16 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
 {
     ChannelState *s = (ChannelState *)dev;
     int qcode, keycode;
+    InputKeyEvent *key;

     assert(evt->type == INPUT_EVENT_KIND_KEY);
-    qcode = qemu_input_key_value_to_qcode(evt->u.key->key);
+    key = evt->u.key;
+    qcode = qemu_input_key_value_to_qcode(key->key);
     trace_escc_sunkbd_event_in(qcode, QKeyCode_lookup[qcode],
-                               evt->u.key->down);
+                               key->down);

     if (qcode == Q_KEY_CODE_CAPS_LOCK) {
-        if (evt->u.key->down) {
+        if (key->down) {
             s->caps_lock_mode ^= 1;
             if (s->caps_lock_mode == 2) {
                 return; /* Drop second press */
@@ -863,7 +865,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
     }

     if (qcode == Q_KEY_CODE_NUM_LOCK) {
-        if (evt->u.key->down) {
+        if (key->down) {
             s->num_lock_mode ^= 1;
             if (s->num_lock_mode == 2) {
                 return; /* Drop second press */
@@ -877,7 +879,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
     }

     keycode = qcode_to_keycode[qcode];
-    if (!evt->u.key->down) {
+    if (!key->down) {
         keycode |= 0x80;
     }
     trace_escc_sunkbd_event_out(keycode);
diff --git a/hw/input/hid.c b/hw/input/hid.c
index 81a85fb..41a9387 100644
--- a/hw/input/hid.c
+++ b/hw/input/hid.c
@@ -116,37 +116,42 @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
     };
     HIDState *hs = (HIDState *)dev;
     HIDPointerEvent *e;
+    InputMoveEvent *move;
+    InputBtnEvent *btn;

     assert(hs->n < QUEUE_LENGTH);
     e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];

     switch (evt->type) {
     case INPUT_EVENT_KIND_REL:
-        if (evt->u.rel->axis == INPUT_AXIS_X) {
-            e->xdx += evt->u.rel->value;
-        } else if (evt->u.rel->axis == INPUT_AXIS_Y) {
-            e->ydy += evt->u.rel->value;
+        move = evt->u.rel;
+        if (move->axis == INPUT_AXIS_X) {
+            e->xdx += move->value;
+        } else if (move->axis == INPUT_AXIS_Y) {
+            e->ydy += move->value;
         }
         break;

     case INPUT_EVENT_KIND_ABS:
-        if (evt->u.rel->axis == INPUT_AXIS_X) {
-            e->xdx = evt->u.rel->value;
-        } else if (evt->u.rel->axis == INPUT_AXIS_Y) {
-            e->ydy = evt->u.rel->value;
+        move = evt->u.abs;
+        if (move->axis == INPUT_AXIS_X) {
+            e->xdx = move->value;
+        } else if (move->axis == INPUT_AXIS_Y) {
+            e->ydy = move->value;
         }
         break;

     case INPUT_EVENT_KIND_BTN:
-        if (evt->u.btn->down) {
-            e->buttons_state |= bmap[evt->u.btn->button];
-            if (evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) {
+        btn = evt->u.btn;
+        if (btn->down) {
+            e->buttons_state |= bmap[btn->button];
+            if (btn->button == INPUT_BUTTON_WHEEL_UP) {
                 e->dz--;
-            } else if (evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+            } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
                 e->dz++;
             }
         } else {
-            e->buttons_state &= ~bmap[evt->u.btn->button];
+            e->buttons_state &= ~bmap[btn->button];
         }
         break;

@@ -223,9 +228,10 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
     HIDState *hs = (HIDState *)dev;
     int scancodes[3], i, count;
     int slot;
+    InputKeyEvent *key = evt->u.key;

-    count = qemu_input_key_value_to_scancode(evt->u.key->key,
-                                             evt->u.key->down,
+    count = qemu_input_key_value_to_scancode(key->key,
+                                             key->down,
                                              scancodes);
     if (hs->n + count > QUEUE_LENGTH) {
         fprintf(stderr, "usb-kbd: warning: key event queue full\n");
diff --git a/hw/input/ps2.c b/hw/input/ps2.c
index 1bd0dde..86df1a0 100644
--- a/hw/input/ps2.c
+++ b/hw/input/ps2.c
@@ -182,10 +182,11 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
 {
     PS2KbdState *s = (PS2KbdState *)dev;
     int scancodes[3], i, count;
+    InputKeyEvent *key = evt->u.key;

     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
-    count = qemu_input_key_value_to_scancode(evt->u.key->key,
-                                             evt->u.key->down,
+    count = qemu_input_key_value_to_scancode(key->key,
+                                             key->down,
                                              scancodes);
     for (i = 0; i < count; i++) {
         ps2_put_keycode(s, scancodes[i]);
@@ -389,6 +390,8 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
         [INPUT_BUTTON_RIGHT]  = MOUSE_EVENT_RBUTTON,
     };
     PS2MouseState *s = (PS2MouseState *)dev;
+    InputMoveEvent *move;
+    InputBtnEvent *btn;

     /* check if deltas are recorded when disabled */
     if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
@@ -396,23 +399,25 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,

     switch (evt->type) {
     case INPUT_EVENT_KIND_REL:
-        if (evt->u.rel->axis == INPUT_AXIS_X) {
-            s->mouse_dx += evt->u.rel->value;
-        } else if (evt->u.rel->axis == INPUT_AXIS_Y) {
-            s->mouse_dy -= evt->u.rel->value;
+        move = evt->u.rel;
+        if (move->axis == INPUT_AXIS_X) {
+            s->mouse_dx += move->value;
+        } else if (move->axis == INPUT_AXIS_Y) {
+            s->mouse_dy -= move->value;
         }
         break;

     case INPUT_EVENT_KIND_BTN:
-        if (evt->u.btn->down) {
-            s->mouse_buttons |= bmap[evt->u.btn->button];
-            if (evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) {
+        btn = evt->u.btn;
+        if (btn->down) {
+            s->mouse_buttons |= bmap[btn->button];
+            if (btn->button == INPUT_BUTTON_WHEEL_UP) {
                 s->mouse_dz--;
-            } else if (evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+            } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
                 s->mouse_dz++;
             }
         } else {
-            s->mouse_buttons &= ~bmap[evt->u.btn->button];
+            s->mouse_buttons &= ~bmap[btn->button];
         }
         break;

diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c
index 9ca5395..e5480c3 100644
--- a/hw/input/virtio-input-hid.c
+++ b/hw/input/virtio-input-hid.c
@@ -191,46 +191,53 @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src,
     VirtIOInput *vinput = VIRTIO_INPUT(dev);
     virtio_input_event event;
     int qcode;
+    InputKeyEvent *key;
+    InputMoveEvent *move;
+    InputBtnEvent *btn;

     switch (evt->type) {
     case INPUT_EVENT_KIND_KEY:
-        qcode = qemu_input_key_value_to_qcode(evt->u.key->key);
+        key = evt->u.key;
+        qcode = qemu_input_key_value_to_qcode(key->key);
         if (qcode && keymap_qcode[qcode]) {
             event.type  = cpu_to_le16(EV_KEY);
             event.code  = cpu_to_le16(keymap_qcode[qcode]);
-            event.value = cpu_to_le32(evt->u.key->down ? 1 : 0);
+            event.value = cpu_to_le32(key->down ? 1 : 0);
             virtio_input_send(vinput, &event);
         } else {
-            if (evt->u.key->down) {
+            if (key->down) {
                 fprintf(stderr, "%s: unmapped key: %d [%s]\n", __func__,
                         qcode, QKeyCode_lookup[qcode]);
             }
         }
         break;
     case INPUT_EVENT_KIND_BTN:
-        if (keymap_button[evt->u.btn->button]) {
+        btn = evt->u.btn;
+        if (keymap_button[btn->button]) {
             event.type  = cpu_to_le16(EV_KEY);
-            event.code  = cpu_to_le16(keymap_button[evt->u.btn->button]);
-            event.value = cpu_to_le32(evt->u.btn->down ? 1 : 0);
+            event.code  = cpu_to_le16(keymap_button[btn->button]);
+            event.value = cpu_to_le32(btn->down ? 1 : 0);
             virtio_input_send(vinput, &event);
         } else {
-            if (evt->u.btn->down) {
+            if (btn->down) {
                 fprintf(stderr, "%s: unmapped button: %d [%s]\n", __func__,
-                        evt->u.btn->button,
-                        InputButton_lookup[evt->u.btn->button]);
+                        btn->button,
+                        InputButton_lookup[btn->button]);
             }
         }
         break;
     case INPUT_EVENT_KIND_REL:
+        move = evt->u.rel;
         event.type  = cpu_to_le16(EV_REL);
-        event.code  = cpu_to_le16(axismap_rel[evt->u.rel->axis]);
-        event.value = cpu_to_le32(evt->u.rel->value);
+        event.code  = cpu_to_le16(axismap_rel[move->axis]);
+        event.value = cpu_to_le32(move->value);
         virtio_input_send(vinput, &event);
         break;
     case INPUT_EVENT_KIND_ABS:
+        move = evt->u.abs;
         event.type  = cpu_to_le16(EV_ABS);
-        event.code  = cpu_to_le16(axismap_abs[evt->u.abs->axis]);
-        event.value = cpu_to_le32(evt->u.abs->value);
+        event.code  = cpu_to_le16(axismap_abs[move->axis]);
+        event.value = cpu_to_le32(move->value);
         virtio_input_send(vinput, &event);
         break;
     default:
diff --git a/replay/replay-input.c b/replay/replay-input.c
index 93616be..c38af50 100644
--- a/replay/replay-input.c
+++ b/replay/replay-input.c
@@ -47,20 +47,24 @@ static InputEvent *qapi_clone_InputEvent(InputEvent *src)

 void replay_save_input_event(InputEvent *evt)
 {
+    InputKeyEvent *key;
+    InputBtnEvent *btn;
+    InputMoveEvent *move;
     replay_put_dword(evt->type);

     switch (evt->type) {
     case INPUT_EVENT_KIND_KEY:
-        replay_put_dword(evt->u.key->key->type);
+        key = evt->u.key;
+        replay_put_dword(key->key->type);

-        switch (evt->u.key->key->type) {
+        switch (key->key->type) {
         case KEY_VALUE_KIND_NUMBER:
-            replay_put_qword(evt->u.key->key->u.number);
-            replay_put_byte(evt->u.key->down);
+            replay_put_qword(key->key->u.number);
+            replay_put_byte(key->down);
             break;
         case KEY_VALUE_KIND_QCODE:
-            replay_put_dword(evt->u.key->key->u.qcode);
-            replay_put_byte(evt->u.key->down);
+            replay_put_dword(key->key->u.qcode);
+            replay_put_byte(key->down);
             break;
         case KEY_VALUE_KIND__MAX:
             /* keep gcc happy */
@@ -68,16 +72,19 @@ void replay_save_input_event(InputEvent *evt)
         }
         break;
     case INPUT_EVENT_KIND_BTN:
-        replay_put_dword(evt->u.btn->button);
-        replay_put_byte(evt->u.btn->down);
+        btn = evt->u.btn;
+        replay_put_dword(btn->button);
+        replay_put_byte(btn->down);
         break;
     case INPUT_EVENT_KIND_REL:
-        replay_put_dword(evt->u.rel->axis);
-        replay_put_qword(evt->u.rel->value);
+        move = evt->u.rel;
+        replay_put_dword(move->axis);
+        replay_put_qword(move->value);
         break;
     case INPUT_EVENT_KIND_ABS:
-        replay_put_dword(evt->u.abs->axis);
-        replay_put_qword(evt->u.abs->value);
+        move = evt->u.abs;
+        replay_put_dword(move->axis);
+        replay_put_qword(move->value);
         break;
     case INPUT_EVENT_KIND__MAX:
         /* keep gcc happy */
diff --git a/ui/input-legacy.c b/ui/input-legacy.c
index 703f0a6..f1c5cb4 100644
--- a/ui/input-legacy.c
+++ b/ui/input-legacy.c
@@ -110,12 +110,13 @@ static void legacy_kbd_event(DeviceState *dev, QemuConsole *src,
 {
     QEMUPutKbdEntry *entry = (QEMUPutKbdEntry *)dev;
     int scancodes[3], i, count;
+    InputKeyEvent *key = evt->u.key;

     if (!entry || !entry->put_kbd) {
         return;
     }
-    count = qemu_input_key_value_to_scancode(evt->u.key->key,
-                                             evt->u.key->down,
+    count = qemu_input_key_value_to_scancode(key->key,
+                                             key->down,
                                              scancodes);
     for (i = 0; i < count; i++) {
         entry->put_kbd(entry->opaque, scancodes[i]);
@@ -150,23 +151,25 @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src,
         [INPUT_BUTTON_RIGHT]  = MOUSE_EVENT_RBUTTON,
     };
     QEMUPutMouseEntry *s = (QEMUPutMouseEntry *)dev;
+    InputBtnEvent *btn;
+    InputMoveEvent *move;

     switch (evt->type) {
     case INPUT_EVENT_KIND_BTN:
-        if (evt->u.btn->down) {
-            s->buttons |= bmap[evt->u.btn->button];
+        btn = evt->u.btn;
+        if (btn->down) {
+            s->buttons |= bmap[btn->button];
         } else {
-            s->buttons &= ~bmap[evt->u.btn->button];
+            s->buttons &= ~bmap[btn->button];
         }
-        if (evt->u.btn->down && evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) {
+        if (btn->down && btn->button == INPUT_BUTTON_WHEEL_UP) {
             s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque,
                                     s->axis[INPUT_AXIS_X],
                                     s->axis[INPUT_AXIS_Y],
                                     -1,
                                     s->buttons);
         }
-        if (evt->u.btn->down &&
-            evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+        if (btn->down && btn->button == INPUT_BUTTON_WHEEL_DOWN) {
             s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque,
                                     s->axis[INPUT_AXIS_X],
                                     s->axis[INPUT_AXIS_Y],
@@ -175,10 +178,12 @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src,
         }
         break;
     case INPUT_EVENT_KIND_ABS:
-        s->axis[evt->u.abs->axis] = evt->u.abs->value;
+        move = evt->u.abs;
+        s->axis[move->axis] = move->value;
         break;
     case INPUT_EVENT_KIND_REL:
-        s->axis[evt->u.rel->axis] += evt->u.rel->value;
+        move = evt->u.rel;
+        s->axis[move->axis] += move->value;
         break;
     default:
         break;
diff --git a/ui/input.c b/ui/input.c
index 6fd48ef..13ee117 100644
--- a/ui/input.c
+++ b/ui/input.c
@@ -166,24 +166,25 @@ void qmp_input_send_event(bool has_device, const char *device,

 static void qemu_input_transform_abs_rotate(InputEvent *evt)
 {
+    InputMoveEvent *move = evt->u.abs;
     switch (graphic_rotate) {
     case 90:
-        if (evt->u.abs->axis == INPUT_AXIS_X) {
-            evt->u.abs->axis = INPUT_AXIS_Y;
-        } else if (evt->u.abs->axis == INPUT_AXIS_Y) {
-            evt->u.abs->axis = INPUT_AXIS_X;
-            evt->u.abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->u.abs->value;
+        if (move->axis == INPUT_AXIS_X) {
+            move->axis = INPUT_AXIS_Y;
+        } else if (move->axis == INPUT_AXIS_Y) {
+            move->axis = INPUT_AXIS_X;
+            move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value;
         }
         break;
     case 180:
-        evt->u.abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->u.abs->value;
+        move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value;
         break;
     case 270:
-        if (evt->u.abs->axis == INPUT_AXIS_X) {
-            evt->u.abs->axis = INPUT_AXIS_Y;
-            evt->u.abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->u.abs->value;
-        } else if (evt->u.abs->axis == INPUT_AXIS_Y) {
-            evt->u.abs->axis = INPUT_AXIS_X;
+        if (move->axis == INPUT_AXIS_X) {
+            move->axis = INPUT_AXIS_Y;
+            move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value;
+        } else if (move->axis == INPUT_AXIS_Y) {
+            move->axis = INPUT_AXIS_X;
         }
         break;
     }
@@ -193,22 +194,26 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
 {
     const char *name;
     int qcode, idx = -1;
+    InputKeyEvent *key;
+    InputBtnEvent *btn;
+    InputMoveEvent *move;

     if (src) {
         idx = qemu_console_get_index(src);
     }
     switch (evt->type) {
     case INPUT_EVENT_KIND_KEY:
-        switch (evt->u.key->key->type) {
+        key = evt->u.key;
+        switch (key->key->type) {
         case KEY_VALUE_KIND_NUMBER:
-            qcode = qemu_input_key_number_to_qcode(evt->u.key->key->u.number);
+            qcode = qemu_input_key_number_to_qcode(key->key->u.number);
             name = QKeyCode_lookup[qcode];
-            trace_input_event_key_number(idx, evt->u.key->key->u.number,
-                                         name, evt->u.key->down);
+            trace_input_event_key_number(idx, key->key->u.number,
+                                         name, key->down);
             break;
         case KEY_VALUE_KIND_QCODE:
-            name = QKeyCode_lookup[evt->u.key->key->u.qcode];
-            trace_input_event_key_qcode(idx, name, evt->u.key->down);
+            name = QKeyCode_lookup[key->key->u.qcode];
+            trace_input_event_key_qcode(idx, name, key->down);
             break;
         case KEY_VALUE_KIND__MAX:
             /* keep gcc happy */
@@ -216,16 +221,19 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
         }
         break;
     case INPUT_EVENT_KIND_BTN:
-        name = InputButton_lookup[evt->u.btn->button];
-        trace_input_event_btn(idx, name, evt->u.btn->down);
+        btn = evt->u.btn;
+        name = InputButton_lookup[btn->button];
+        trace_input_event_btn(idx, name, btn->down);
         break;
     case INPUT_EVENT_KIND_REL:
-        name = InputAxis_lookup[evt->u.rel->axis];
-        trace_input_event_rel(idx, name, evt->u.rel->value);
+        move = evt->u.rel;
+        name = InputAxis_lookup[move->axis];
+        trace_input_event_rel(idx, name, move->value);
         break;
     case INPUT_EVENT_KIND_ABS:
-        name = InputAxis_lookup[evt->u.abs->axis];
-        trace_input_event_abs(idx, name, evt->u.abs->value);
+        move = evt->u.abs;
+        name = InputAxis_lookup[move->axis];
+        trace_input_event_abs(idx, name, move->value);
         break;
     case INPUT_EVENT_KIND__MAX:
         /* keep gcc happy */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 01/19] qapi: Rename 'fields' to 'members' in internal interface
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 01/19] qapi: Rename 'fields' to 'members' in internal interface Eric Blake
@ 2016-03-02 17:15   ` Markus Armbruster
  2016-03-02 20:05     ` Eric Blake
  0 siblings, 1 reply; 48+ messages in thread
From: Markus Armbruster @ 2016-03-02 17:15 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> C types and JSON objects don't have fields, but members.  We
> shouldn't gratuitously invent terminology.  This patch is a
> strict renaming of generator code and static genarated functions,
> plus the naming of the dummy filler member for empty structs,
> before the next patch exposes some of that naming to the rest of
> the code base.
>
> Suggested-by: Markus Armbruster <armbru@redhat.com>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v2: new patch

Patch looks good.  You could split it into Python renames (no change to
generated code) and C renames.  Only if you like the idea.

If you want to be *really* thorough: there's a "field" left in
tests/qapi-schema/qapi-schema-test.json, and a few in
docs/qapi-code-gen.txt.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 02/19] qapi-visit: Expose visit_type_FOO_members()
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 02/19] qapi-visit: Expose visit_type_FOO_members() Eric Blake
@ 2016-03-02 17:24   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-03-02 17:24 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Dan Berrange reported a case where he needs to work with a
> QCryptoBlockOptions union type using the OptsVisitor, but only
> visit one of the branches of that type (the discriminator is not
> visited directly, but learned externally).  When things were
> boxed, it was easy: just visit the variant directly, which took
> care of both allocating the variant and visiting its members, then
> store that pointer in the union type.  But now that things are
> unboxed, we need a way to visit the members without allocation,
> done by exposing visit_type_FOO_members() to the user.
>
> Before the patch, we had quite a bit of code associated with
> object_members_seen to make sure that a declaration of the helper
> was in scope before any use of the function.  But now that the
> helper is public and declared in the header, the .c file no
> longer needs to worry about topological sorting (the helper is
> always in scope), which leads to some nice cleanups.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v2: Rebase atop s/fields/members/rename, don't provide useless
> declaration on alternates

I like this much better than v1.  Thanks!

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 04/19] chardev: Shorten references into ChardevBackend
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 04/19] chardev: Shorten references into ChardevBackend Eric Blake
@ 2016-03-02 17:55   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-03-02 17:55 UTC (permalink / raw)
  To: Eric Blake; +Cc: Paolo Bonzini, qemu-devel

Eric Blake <eblake@redhat.com> writes:

> An upcoming patch will alter how simple unions, like ChardevBackend,
> are laid out, which will impact all lines of the form 'backend->u.XXX'.
> To minimize the impact of that patch, use a temporary variable to
> reduce the number of lines needing modification when an internal
> reference within ChardevBackend changes layout.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-By: Daniel P. Berrange <berrange@redhat.com>
>
> ---
> v2: add R-b
> ---
>  qemu-char.c | 122 ++++++++++++++++++++++++++++++++----------------------------
>  1 file changed, 66 insertions(+), 56 deletions(-)
>
> diff --git a/qemu-char.c b/qemu-char.c
> index fc8ffda..5ea1d34 100644
> --- a/qemu-char.c
> +++ b/qemu-char.c
> @@ -724,7 +724,7 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
>      ChardevMux *mux = backend->u.mux;
>      CharDriverState *chr, *drv;
>      MuxDriver *d;
> -    ChardevCommon *common = qapi_ChardevMux_base(backend->u.mux);
> +    ChardevCommon *common = qapi_ChardevMux_base(mux);
>
>      drv = qemu_chr_find(mux->chardev);
>      if (drv == NULL) {

The commit message sounds like you *add* a temporary variable to reduce
churn.  You're using an existing one here.

> @@ -1043,7 +1043,7 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
>      char *filename_in;
>      char *filename_out;
>      const char *filename = opts->device;
> -    ChardevCommon *common = qapi_ChardevHostdev_base(backend->u.pipe);
> +    ChardevCommon *common = qapi_ChardevHostdev_base(opts);
>
>
>      filename_in = g_strdup_printf("%s.in", filename);
> @@ -1123,7 +1123,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
>      ChardevStdio *opts = backend->u.stdio;
>      CharDriverState *chr;
>      struct sigaction act;
> -    ChardevCommon *common = qapi_ChardevStdio_base(backend->u.stdio);
> +    ChardevCommon *common = qapi_ChardevStdio_base(opts);
>
>      if (is_daemonized()) {
>          error_setg(errp, "cannot use stdio with -daemonize");
> @@ -2141,7 +2141,7 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
>      const char *filename = opts->device;
>      CharDriverState *chr;
>      WinCharState *s;
> -    ChardevCommon *common = qapi_ChardevHostdev_base(backend->u.pipe);
> +    ChardevCommon *common = qapi_ChardevHostdev_base(opts);
>
>      chr = qemu_chr_alloc(common, errp);
>      if (!chr) {
> @@ -3216,7 +3216,7 @@ static CharDriverState *qemu_chr_open_ringbuf(const char *id,
>                                                Error **errp)
>  {
>      ChardevRingbuf *opts = backend->u.ringbuf;
> -    ChardevCommon *common = qapi_ChardevRingbuf_base(backend->u.ringbuf);
> +    ChardevCommon *common = qapi_ChardevRingbuf_base(opts);
>      CharDriverState *chr;
>      RingBufCharDriver *d;
>
> @@ -3506,26 +3506,29 @@ static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend,
>                                      Error **errp)
>  {
>      const char *path = qemu_opt_get(opts, "path");
> +    ChardevFile *file;

Ah, you do add a temporary in some places.

>
>      if (path == NULL) {
>          error_setg(errp, "chardev: file: no filename given");
>          return;
>      }
> -    backend->u.file = g_new0(ChardevFile, 1);
> -    qemu_chr_parse_common(opts, qapi_ChardevFile_base(backend->u.file));
> -    backend->u.file->out = g_strdup(path);
> +    file = backend->u.file = g_new0(ChardevFile, 1);
> +    qemu_chr_parse_common(opts, qapi_ChardevFile_base(file));
> +    file->out = g_strdup(path);
>
> -    backend->u.file->has_append = true;
> -    backend->u.file->append = qemu_opt_get_bool(opts, "append", false);
> +    file->has_append = true;
> +    file->append = qemu_opt_get_bool(opts, "append", false);
>  }


Whether you touch every line now or later is a wash as far as churn is
concerned.  I'd be willing to accept an argument that this change is
simpler than the one it avoids, or that it makes the code more
consistent, or it makes the code easier to read.  Preferably in the
commit message.

[More of the same snipped...]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 05/19] util: Shorten references into SocketAddress
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 05/19] util: Shorten references into SocketAddress Eric Blake
@ 2016-03-02 18:03   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-03-02 18:03 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, Paolo Bonzini, qemu-devel,
	open list:Block layer core, Gerd Hoffmann

Eric Blake <eblake@redhat.com> writes:

> An upcoming patch will alter how simple unions, like SocketAddress,
> are laid out, which will impact all lines of the form 'addr->u.XXX'.
> To minimize the impact of that patch, use C99 initialization or a
> temporary variable to reduce the number of lines needing modification
> when an internal reference within SocketAddress changes layout.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Daniel P. Berrange <berrange@redhat.com>

If you improve the previous commit's message to address my remarks,
you'll probably want to update this one, too.
>
> ---
> v2: add R-b
> ---
>  block/nbd.c                    | 14 ++++++++------
>  qemu-char.c                    | 43 ++++++++++++++++++++++++------------------
>  qemu-nbd.c                     |  9 +++++----
>  tests/test-io-channel-socket.c | 26 ++++++++++++++++---------
>  ui/vnc.c                       | 39 +++++++++++++++++++-------------------
>  util/qemu-sockets.c            | 11 ++++++-----
>  6 files changed, 81 insertions(+), 61 deletions(-)
>
> diff --git a/block/nbd.c b/block/nbd.c
> index db57b49..9f333c9 100644
> --- a/block/nbd.c
> +++ b/block/nbd.c
> @@ -204,18 +204,20 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, char **export,
>      saddr = g_new0(SocketAddress, 1);
>
>      if (qdict_haskey(options, "path")) {
> +        UnixSocketAddress *q_unix;
>          saddr->type = SOCKET_ADDRESS_KIND_UNIX;
> -        saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
> -        saddr->u.q_unix->path = g_strdup(qdict_get_str(options, "path"));
> +        q_unix = saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
> +        q_unix->path = g_strdup(qdict_get_str(options, "path"));
>          qdict_del(options, "path");
>      } else {
> +        InetSocketAddress *inet;
>          saddr->type = SOCKET_ADDRESS_KIND_INET;
> -        saddr->u.inet = g_new0(InetSocketAddress, 1);
> -        saddr->u.inet->host = g_strdup(qdict_get_str(options, "host"));
> +        inet = saddr->u.inet = g_new0(InetSocketAddress, 1);
> +        inet->host = g_strdup(qdict_get_str(options, "host"));
>          if (!qdict_get_try_str(options, "port")) {
> -            saddr->u.inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
> +            inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
>          } else {
> -            saddr->u.inet->port = g_strdup(qdict_get_str(options, "port"));
> +            inet->port = g_strdup(qdict_get_str(options, "port"));
>          }
>          qdict_del(options, "host");
>          qdict_del(options, "port");
> diff --git a/qemu-char.c b/qemu-char.c
> index 5ea1d34..cfc82bc 100644
> --- a/qemu-char.c
> +++ b/qemu-char.c
> @@ -3659,20 +3659,23 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
>
>      addr = g_new0(SocketAddress, 1);
>      if (path) {
> +        UnixSocketAddress *q_unix;
>          addr->type = SOCKET_ADDRESS_KIND_UNIX;
> -        addr->u.q_unix = g_new0(UnixSocketAddress, 1);
> -        addr->u.q_unix->path = g_strdup(path);
> +        q_unix = addr->u.q_unix = g_new0(UnixSocketAddress, 1);
> +        q_unix->path = g_strdup(path);
>      } else {
>          addr->type = SOCKET_ADDRESS_KIND_INET;
>          addr->u.inet = g_new0(InetSocketAddress, 1);
> -        addr->u.inet->host = g_strdup(host);
> -        addr->u.inet->port = g_strdup(port);
> -        addr->u.inet->has_to = qemu_opt_get(opts, "to");
> -        addr->u.inet->to = qemu_opt_get_number(opts, "to", 0);
> -        addr->u.inet->has_ipv4 = qemu_opt_get(opts, "ipv4");
> -        addr->u.inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0);
> -        addr->u.inet->has_ipv6 = qemu_opt_get(opts, "ipv6");
> -        addr->u.inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0);
> +        *addr->u.inet = (InetSocketAddress) {
> +            .host = g_strdup(host),
> +            .port = g_strdup(port),
> +            .has_to = qemu_opt_get(opts, "to"),
> +            .to = qemu_opt_get_number(opts, "to", 0),
> +            .has_ipv4 = qemu_opt_get(opts, "ipv4"),
> +            .ipv4 = qemu_opt_get_bool(opts, "ipv4", 0),
> +            .has_ipv6 = qemu_opt_get(opts, "ipv6"),
> +            .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
> +        };

Do you still need g_new0(), or would g_new() do?

>      }
>      sock->addr = addr;
>  }
[More of the same snipped...]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 07/19] qapi: Avoid use of 'data' member of qapi unions
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 07/19] qapi: Avoid use of 'data' member of qapi unions Eric Blake
@ 2016-03-02 18:18   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-03-02 18:18 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, qemu-devel, open list:Block layer core, Gerd Hoffmann

Eric Blake <eblake@redhat.com> writes:

> qapi code generators currently create a 'void *data' member as

QAPI

> part of the anonymous union embedded in the C struct corresponding
> to a qapi union.  However, directly assigning to this member of

QAPI

> the union feels a bit fishy, when we can directly use the rest

Suggest to drop "directly", or perhaps say "when we can assign to
another member

> of the struct instead.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
> ---
> v2: add R-b
> v1: no change
> Previously posted as part of qapi cleanup series F:
> v6: rebase to latest
> ---
>  blockdev.c | 31 +++++++++++++++++--------------
>  ui/input.c |  2 +-
>  2 files changed, 18 insertions(+), 15 deletions(-)
>
> diff --git a/blockdev.c b/blockdev.c
> index d4bc435..0f20c65 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -1202,15 +1202,11 @@ void hmp_commit(Monitor *mon, const QDict *qdict)
>      }
>  }
>
> -static void blockdev_do_action(TransactionActionKind type, void *data,
> -                               Error **errp)
> +static void blockdev_do_action(TransactionAction *action, Error **errp)
>  {
> -    TransactionAction action;
>      TransactionActionList list;
>
> -    action.type = type;
> -    action.u.data = data;
> -    list.value = &action;
> +    list.value = action;
>      list.next = NULL;
>      qmp_transaction(&list, false, NULL, errp);
>  }

Here, you avoid use of data by assigning the whole struct instead of its
members.  

> @@ -1236,8 +1232,11 @@ void qmp_blockdev_snapshot_sync(bool has_device, const char *device,
>          .has_mode = has_mode,
>          .mode = mode,
>      };
> -    blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC,
> -                       &snapshot, errp);
> +    TransactionAction action = {
> +        .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC,
> +        .u.blockdev_snapshot_sync = &snapshot,
> +    };
> +    blockdev_do_action(&action, errp);
>  }
>

However, the call sites become wordier.  I guess avoiding type-punning
is worth a bit of verbosity.

>  void qmp_blockdev_snapshot(const char *node, const char *overlay,
> @@ -1247,9 +1246,11 @@ void qmp_blockdev_snapshot(const char *node, const char *overlay,
>          .node = (char *) node,
>          .overlay = (char *) overlay
>      };
> -
> -    blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT,
> -                       &snapshot_data, errp);
> +    TransactionAction action = {
> +        .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT,
> +        .u.blockdev_snapshot = &snapshot_data,
> +    };
> +    blockdev_do_action(&action, errp);
>  }
>
>  void qmp_blockdev_snapshot_internal_sync(const char *device,
> @@ -1260,9 +1261,11 @@ void qmp_blockdev_snapshot_internal_sync(const char *device,
>          .device = (char *) device,
>          .name = (char *) name
>      };
> -
> -    blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC,
> -                       &snapshot, errp);
> +    TransactionAction action = {
> +        .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC,
> +        .u.blockdev_snapshot_internal_sync = &snapshot,
> +    };
> +    blockdev_do_action(&action, errp);
>  }
>
>  SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
> diff --git a/ui/input.c b/ui/input.c
> index e15c618..1e81c25 100644
> --- a/ui/input.c
> +++ b/ui/input.c
> @@ -472,7 +472,7 @@ InputEvent *qemu_input_event_new_move(InputEventKind kind,
>      InputMoveEvent *move = g_new0(InputMoveEvent, 1);
>
>      evt->type = kind;
> -    evt->u.data = move;
> +    evt->u.rel = move; /* also would work as evt->u.abs */
>      move->axis = axis;
>      move->value = value;
>      return evt;

Suggest to say /* evt->u.rel is the same as evt.u.abs */

Can't think of a way to build-assert that.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 09/19] qapi: Drop useless 'data' member of unions
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 09/19] qapi: Drop useless 'data' member of unions Eric Blake
@ 2016-03-02 18:30   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-03-02 18:30 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Now that we no longer have any clients of the 'void *data'
> member injected into unions, we can drop it.  Update the
> testsuite to drop the negative test union-clash-data, and
> replace it with a positive test in qapi-schema-test that
> proves that we no longer have a name collision.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
>
> ---
> v2: add R-b
> v1: drop patch that forced :empty as base to all structs
> Previously posted as part of qapi cleanup subset F:
> v6: rebase to earlier changes
> ---
>  scripts/qapi-types.py                   | 9 ---------
>  tests/Makefile                          | 1 -
>  tests/qapi-schema/qapi-schema-test.json | 2 +-
>  tests/qapi-schema/qapi-schema-test.out  | 4 ++--
>  tests/qapi-schema/union-clash-data.err  | 0
>  tests/qapi-schema/union-clash-data.exit | 1 -
>  tests/qapi-schema/union-clash-data.json | 7 -------
>  tests/qapi-schema/union-clash-data.out  | 9 ---------
>  8 files changed, 3 insertions(+), 30 deletions(-)
>  delete mode 100644 tests/qapi-schema/union-clash-data.err
>  delete mode 100644 tests/qapi-schema/union-clash-data.exit
>  delete mode 100644 tests/qapi-schema/union-clash-data.json
>  delete mode 100644 tests/qapi-schema/union-clash-data.out
>
> diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
> index 19d1fff..0306a88 100644
> --- a/scripts/qapi-types.py
> +++ b/scripts/qapi-types.py
> @@ -116,17 +116,8 @@ static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj)
>
>
>  def gen_variants(variants):
> -    # FIXME: What purpose does data serve, besides preventing a union that
> -    # has a branch named 'data'? We use it in qapi-visit.py to decide
> -    # whether to bypass the switch statement if visiting the discriminator
> -    # failed; but since we 0-initialize structs, and cannot tell what
> -    # branch of the union is in use if the discriminator is invalid, there
> -    # should not be any data leaks even without a data pointer.  Or, if
> -    # 'data' is merely added to guarantee we don't have an empty union,
> -    # shouldn't we enforce that at .json parse time?

I figure this comment became stale in commit 544a373.  Mention in commit
message?

>      ret = mcgen('''
>      union { /* union tag is @%(c_name)s */
> -        void *data;
>  ''',
>                  c_name=c_name(variants.tag_member.name))
>
> diff --git a/tests/Makefile b/tests/Makefile
> index 04e34b5..cd4bbd4 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -358,7 +358,6 @@ qapi-schema += unicode-str.json
>  qapi-schema += union-base-no-discriminator.json
>  qapi-schema += union-branch-case.json
>  qapi-schema += union-clash-branches.json
> -qapi-schema += union-clash-data.json
>  qapi-schema += union-empty.json
>  qapi-schema += union-invalid-base.json
>  qapi-schema += union-optional-branch.json
> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index 632964a..b5d0c53 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -115,7 +115,7 @@
>              'number': ['number'],
>              'boolean': ['bool'],
>              'string': ['str'],
> -            'sizes': ['size'],
> +            'data': ['size'],
>              'any': ['any'] } }
>

Replaces the natural name for the size array by an arbitrary one just to
show the patch works.  Next to no value going forward, since we're no
more likely to introduce a 'data' clash than one for any number of other
names.

In short, I wouldn't bother :)

>  # testing commands
> diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
> index f5e2a73..225e2db 100644
> --- a/tests/qapi-schema/qapi-schema-test.out
> +++ b/tests/qapi-schema/qapi-schema-test.out
> @@ -139,9 +139,9 @@ object UserDefNativeListUnion
>      case number: :obj-numberList-wrapper
>      case boolean: :obj-boolList-wrapper
>      case string: :obj-strList-wrapper
> -    case sizes: :obj-sizeList-wrapper
> +    case data: :obj-sizeList-wrapper
>      case any: :obj-anyList-wrapper
> -enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'sizes', 'any']
> +enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'data', 'any']
>  object UserDefOne
>      base UserDefZero
>      member string: str optional=False
> diff --git a/tests/qapi-schema/union-clash-data.err b/tests/qapi-schema/union-clash-data.err
> deleted file mode 100644
> index e69de29..0000000
> diff --git a/tests/qapi-schema/union-clash-data.exit b/tests/qapi-schema/union-clash-data.exit
> deleted file mode 100644
> index 573541a..0000000
> --- a/tests/qapi-schema/union-clash-data.exit
> +++ /dev/null
> @@ -1 +0,0 @@
> -0
> diff --git a/tests/qapi-schema/union-clash-data.json b/tests/qapi-schema/union-clash-data.json
> deleted file mode 100644
> index 7308e69..0000000
> --- a/tests/qapi-schema/union-clash-data.json
> +++ /dev/null
> @@ -1,7 +0,0 @@
> -# Union branch 'data'
> -# FIXME: this parses, but then fails to compile due to a duplicate 'data'
> -# (one from the branch name, another as a filler to avoid an empty union).
> -# we should either detect the collision at parse time, or change the
> -# generated struct to allow this to compile.
> -{ 'union': 'TestUnion',
> -  'data': { 'data': 'int' } }
> diff --git a/tests/qapi-schema/union-clash-data.out b/tests/qapi-schema/union-clash-data.out
> deleted file mode 100644
> index f5752f4..0000000
> --- a/tests/qapi-schema/union-clash-data.out
> +++ /dev/null
> @@ -1,9 +0,0 @@
> -object :empty
> -object :obj-int-wrapper
> -    member data: int optional=False
> -enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
> -    prefix QTYPE
> -object TestUnion
> -    member type: TestUnionKind optional=False
> -    case data: :obj-int-wrapper
> -enum TestUnionKind ['data']

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 10/19] qapi-visit: Factor out gen_visit_members_call()
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 10/19] qapi-visit: Factor out gen_visit_members_call() Eric Blake
@ 2016-03-02 18:53   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-03-02 18:53 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Upcoming patches will be adding several contexts where we want
> to handle the visit of an implicit type (an anonymous base type,
> or an anonymous branch of a flat union) by directly inlining
> the visit of each member of the implicit type. The work is made
> easier by factoring out a new helper, gen_visit_members_call(),
> so that the caller doesn't need to care whether the type it is
> visiting is implicit or normal.
>
> For now, the only implicit type we encounter are the branches
> of a simple union; the initial implementation of the helper
> method is hard-coded to that usage, but it gets us one step
> closer to completely dropping the hack of simple_union_type().
>
> Generated output is unchanged.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v2: retitle, rebase to s/fields/members/ changes
> ---
>  scripts/qapi-visit.py | 42 ++++++++++++++++++++++++------------------
>  1 file changed, 24 insertions(+), 18 deletions(-)
>
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index a712e9a..dbae00c 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -34,6 +34,25 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
>                   c_name=c_name(name))
>
>
> +def gen_visit_members_call(typ, c_name):

The actual arguments of c_name are C expressions, not names.

> +    ret = ''
> +    assert isinstance(typ, QAPISchemaObjectType)
> +    if typ.is_implicit():
> +        # TODO ugly special case for simple union
> +        assert len(typ.members) == 1
> +        assert not typ.variants

This is an inlined and simplified version of simple_union_type().
Violation of DRY, acceptable if temporary, but could use a comment.

> +        ret += mcgen('''
> +    visit_type_%(c_type)s(v, "data", %(c_name)s, &err);
> +''',
> +                     c_type=typ.members[0].type.c_name(), c_name=c_name)
> +    else:
> +        ret += mcgen('''
> +    visit_type_%(c_type)s_members(v, %(c_name)s, &err);
> +''',
> +                     c_type=typ.c_name(), c_name=c_name)
> +    return ret
> +
> +
>  def gen_visit_object_members(name, base, members, variants):
>      ret = mcgen('''
>
> @@ -45,10 +64,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
>                  c_name=c_name(name))
>
>      if base:
> -        ret += mcgen('''
> -    visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, &err);
> -''',
> -                     c_type=base.c_name())
> +        ret += gen_visit_members_call(base, '(%s *)obj' % base.c_name())
>          ret += gen_err_check()
>
>      ret += gen_visit_members(members, prefix='obj->')
> @@ -60,26 +76,16 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
>                       c_name=c_name(variants.tag_member.name))
>
>          for var in variants.variants:
> -            # TODO ugly special case for simple union
> -            simple_union_type = var.simple_union_type()
>              ret += mcgen('''
>      case %(case)s:
>  ''',
>                           case=c_enum_const(variants.tag_member.type.name,
>                                             var.name,
>                                             variants.tag_member.type.prefix))
> -            if simple_union_type:
> -                ret += mcgen('''
> -        visit_type_%(c_type)s(v, "data", &obj->u.%(c_name)s, &err);
> -''',
> -                             c_type=simple_union_type.c_name(),
> -                             c_name=c_name(var.name))
> -            else:
> -                ret += mcgen('''
> -        visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err);
> -''',
> -                             c_type=var.type.c_name(),
> -                             c_name=c_name(var.name))
> +            push_indent()
> +            ret += gen_visit_members_call(var.type,
> +                                          '&obj->u.' + c_name(var.name))
> +            pop_indent()
>              ret += mcgen('''
>          break;
>  ''')

Not an improvement on its own.  Need to review more patches before I can
more.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 11/19] qapi: Add type.is_empty() helper
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 11/19] qapi: Add type.is_empty() helper Eric Blake
@ 2016-03-02 19:04   ` Markus Armbruster
  2016-03-02 20:16     ` Eric Blake
  2016-03-02 23:04     ` Eric Blake
  0 siblings, 2 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-03-02 19:04 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> And use it in qapi-types and qapi-event.  Down the road, we may
> want to lift our artificial restriction of no variants at the
> top level of an event, at which point, inlining our check for
> whether members is empty will no longer be sufficient, but
> adding a check for variants adds verbosity; in the meantime,
> add some asserts in places where we don't handle variants.

Perhaps I'm just running out of steam for today, but I've read this
twice, and still don't get why adding these assertions goes in the same
patch as adding the helper, or what it has to do with events.

> More immediately, the new .is_empty() helper will help fix a bug
> in qapi-visit in the next patch, where the generator did not
> handle an explicit empty type in the same was as a missing type.

same way

>
> No change to generated code.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v2: no change
> v1: add some asserts
> Previously posted as part of qapi cleanup subset E:
> v9: improve commit message
> v8: no change
> v7: rebase to context change
> v6: new patch
> ---
>  scripts/qapi.py       | 5 +++++
>  scripts/qapi-event.py | 7 ++++---
>  scripts/qapi-types.py | 2 +-
>  3 files changed, 10 insertions(+), 4 deletions(-)
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 6c52fe5..83080b3 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -962,6 +962,7 @@ class QAPISchemaObjectType(QAPISchemaType):
>              assert isinstance(self.base, QAPISchemaObjectType)
>              self.base.check(schema)
>              self.base.check_clash(schema, self.info, seen)
> +            assert not self.base.variants
>          for m in self.local_members:
>              m.check(schema)
>              m.check_clash(self.info, seen)

This is the "some asserts" ;)

> @@ -983,6 +984,10 @@ class QAPISchemaObjectType(QAPISchemaType):
>          # See QAPISchema._make_implicit_object_type()
>          return self.name[0] == ':'
>
> +    def is_empty(self):
> +        assert self.members is not None
> +        return not self.members and not self.variants
> +
>      def c_name(self):
>          assert not self.is_implicit()
>          return QAPISchemaType.c_name(self)
> diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
> index fb579dd..808ed80 100644
> --- a/scripts/qapi-event.py
> +++ b/scripts/qapi-event.py
> @@ -39,7 +39,7 @@ def gen_event_send(name, arg_type):
>  ''',
>                  proto=gen_event_send_proto(name, arg_type))
>
> -    if arg_type and arg_type.members:
> +    if arg_type and not arg_type.is_empty():
>          ret += mcgen('''
>      QmpOutputVisitor *qov;
>      Visitor *v;

Oh, you don't just add a helper, you actually *change* the condition!
Perhaps the commit message would be easier to understand if it explained
that first.

> @@ -58,7 +58,8 @@ def gen_event_send(name, arg_type):
>  ''',
>                   name=name)
>
> -    if arg_type and arg_type.members:
> +    if arg_type and not arg_type.is_empty():
> +        assert not arg_type.variants
>          ret += mcgen('''
>      qov = qmp_output_visitor_new();
>      v = qmp_output_get_visitor(qov);
> @@ -88,7 +89,7 @@ out_obj:
>  ''',
>                   c_enum=c_enum_const(event_enum_name, name))
>
> -    if arg_type and arg_type.members:
> +    if arg_type and not arg_type.is_empty():
>          ret += mcgen('''
>  out:
>      qmp_output_visitor_cleanup(qov);
> diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
> index 0306a88..6c1923d 100644
> --- a/scripts/qapi-types.py
> +++ b/scripts/qapi-types.py
> @@ -90,7 +90,7 @@ struct %(c_name)s {
>      # potential issues with attempting to malloc space for zero-length
>      # structs in C, and also incompatibility with C++ (where an empty
>      # struct is size 1).
> -    if not (base and base.members) and not members and not variants:
> +    if (not base or base.is_empty()) and not members and not variants:
>          ret += mcgen('''
>      char qapi_dummy_for_empty_struct;
>  ''')

I figure the case for the helper based on this patch alone is making the
code a bit more future-proof.  Suggest you try to explain that in your
commit message, including against what future change exactly you're
proofing the code.

Haven't reviewed for completeness.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 01/19] qapi: Rename 'fields' to 'members' in internal interface
  2016-03-02 17:15   ` Markus Armbruster
@ 2016-03-02 20:05     ` Eric Blake
  0 siblings, 0 replies; 48+ messages in thread
From: Eric Blake @ 2016-03-02 20:05 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 1115 bytes --]

On 03/02/2016 10:15 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> C types and JSON objects don't have fields, but members.  We
>> shouldn't gratuitously invent terminology.  This patch is a
>> strict renaming of generator code and static genarated functions,
>> plus the naming of the dummy filler member for empty structs,
>> before the next patch exposes some of that naming to the rest of
>> the code base.
>>
>> Suggested-by: Markus Armbruster <armbru@redhat.com>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>
>> ---
>> v2: new patch
> 
> Patch looks good.  You could split it into Python renames (no change to
> generated code) and C renames.  Only if you like the idea.

Sure, not too much work.

> 
> If you want to be *really* thorough: there's a "field" left in
> tests/qapi-schema/qapi-schema-test.json, and a few in
> docs/qapi-code-gen.txt.

I'll save docs/ for 3/19, but get the .json file fixed along with the
Python renames.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 11/19] qapi: Add type.is_empty() helper
  2016-03-02 19:04   ` Markus Armbruster
@ 2016-03-02 20:16     ` Eric Blake
  2016-03-03  7:08       ` Markus Armbruster
  2016-03-02 23:04     ` Eric Blake
  1 sibling, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-03-02 20:16 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 3199 bytes --]

On 03/02/2016 12:04 PM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> And use it in qapi-types and qapi-event.  Down the road, we may
>> want to lift our artificial restriction of no variants at the
>> top level of an event, at which point, inlining our check for
>> whether members is empty will no longer be sufficient, but
>> adding a check for variants adds verbosity; in the meantime,
>> add some asserts in places where we don't handle variants.
> 
> Perhaps I'm just running out of steam for today, but I've read this
> twice, and still don't get why adding these assertions goes in the same
> patch as adding the helper, or what it has to do with events.

And yet it was the review on the earlier posting that caused me to add
asserts; maybe re-reading that thread will help refresh memory, and spur
an idea for how to better express it in the commit message:
https://lists.gnu.org/archive/html/qemu-devel/2016-01/msg04726.html

> 
>> More immediately, the new .is_empty() helper will help fix a bug
>> in qapi-visit in the next patch, where the generator did not
>> handle an explicit empty type in the same was as a missing type.
> 
> same way

[Ever wonder if I intentionally stick in a typo, just to see who will
notice? Or maybe it really was a slip of the finger...]

>> +++ b/scripts/qapi-event.py
>> @@ -39,7 +39,7 @@ def gen_event_send(name, arg_type):
>>  ''',
>>                  proto=gen_event_send_proto(name, arg_type))
>>
>> -    if arg_type and arg_type.members:
>> +    if arg_type and not arg_type.is_empty():
>>          ret += mcgen('''
>>      QmpOutputVisitor *qov;
>>      Visitor *v;
> 
> Oh, you don't just add a helper, you actually *change* the condition!
> Perhaps the commit message would be easier to understand if it explained
> that first.

The old condition:
arg_type and arg_type.members

New condition:
arg_type and (arg_type.members or arg_type.variants)

But we know there are no variants, since unions cannot (yet) be passed
as event 'data', so the condition is the same effect now, and
future-proofing for a future patch when I do allow unions in events.

>> +++ b/scripts/qapi-types.py
>> @@ -90,7 +90,7 @@ struct %(c_name)s {
>>      # potential issues with attempting to malloc space for zero-length
>>      # structs in C, and also incompatibility with C++ (where an empty
>>      # struct is size 1).
>> -    if not (base and base.members) and not members and not variants:
>> +    if (not base or base.is_empty()) and not members and not variants:
>>          ret += mcgen('''
>>      char qapi_dummy_for_empty_struct;
>>  ''')
> 
> I figure the case for the helper based on this patch alone is making the
> code a bit more future-proof.  Suggest you try to explain that in your
> commit message, including against what future change exactly you're
> proofing the code.

And here, bases cannot (yet) have variants, but that's also on my plate
of things I'd like to support in the future.

> 
> Haven't reviewed for completeness.
> 

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 11/19] qapi: Add type.is_empty() helper
  2016-03-02 19:04   ` Markus Armbruster
  2016-03-02 20:16     ` Eric Blake
@ 2016-03-02 23:04     ` Eric Blake
  2016-03-03  7:18       ` Markus Armbruster
  1 sibling, 1 reply; 48+ messages in thread
From: Eric Blake @ 2016-03-02 23:04 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 863 bytes --]

On 03/02/2016 12:04 PM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> And use it in qapi-types and qapi-event.  Down the road, we may
>> want to lift our artificial restriction of no variants at the
>> top level of an event, at which point, inlining our check for
>> whether members is empty will no longer be sufficient, but
>> adding a check for variants adds verbosity; in the meantime,
>> add some asserts in places where we don't handle variants.
> 
> Perhaps I'm just running out of steam for today, but I've read this
> twice, and still don't get why adding these assertions goes in the same
> patch as adding the helper, or what it has to do with events.

Okay, will split this into two patches for v3.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 11/19] qapi: Add type.is_empty() helper
  2016-03-02 20:16     ` Eric Blake
@ 2016-03-03  7:08       ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-03-03  7:08 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 03/02/2016 12:04 PM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> And use it in qapi-types and qapi-event.  Down the road, we may
>>> want to lift our artificial restriction of no variants at the
>>> top level of an event, at which point, inlining our check for
>>> whether members is empty will no longer be sufficient, but
>>> adding a check for variants adds verbosity; in the meantime,
>>> add some asserts in places where we don't handle variants.
>> 
>> Perhaps I'm just running out of steam for today, but I've read this
>> twice, and still don't get why adding these assertions goes in the same
>> patch as adding the helper, or what it has to do with events.
>
> And yet it was the review on the earlier posting that caused me to add
> asserts; maybe re-reading that thread will help refresh memory, and spur
> an idea for how to better express it in the commit message:
> https://lists.gnu.org/archive/html/qemu-devel/2016-01/msg04726.html

I suspect what's needed is a clearer commit message a fresher mind
reading it.

>>> More immediately, the new .is_empty() helper will help fix a bug
>>> in qapi-visit in the next patch, where the generator did not
>>> handle an explicit empty type in the same was as a missing type.
>> 
>> same way
>
> [Ever wonder if I intentionally stick in a typo, just to see who will
> notice? Or maybe it really was a slip of the finger...]
>
>>> +++ b/scripts/qapi-event.py
>>> @@ -39,7 +39,7 @@ def gen_event_send(name, arg_type):
>>>  ''',
>>>                  proto=gen_event_send_proto(name, arg_type))
>>>
>>> -    if arg_type and arg_type.members:
>>> +    if arg_type and not arg_type.is_empty():
>>>          ret += mcgen('''
>>>      QmpOutputVisitor *qov;
>>>      Visitor *v;
>> 
>> Oh, you don't just add a helper, you actually *change* the condition!
>> Perhaps the commit message would be easier to understand if it explained
>> that first.
>
> The old condition:
> arg_type and arg_type.members
>
> New condition:
> arg_type and (arg_type.members or arg_type.variants)
>
> But we know there are no variants, since unions cannot (yet) be passed
> as event 'data', so the condition is the same effect now, and
> future-proofing for a future patch when I do allow unions in events.

Unless allowing unions makes generators nicer, I'll want to see a
compelling use case.

Can you give me an idea what .is_empty() does for *this* patch series?
Wait, you did: "will help fix a bug [...] in the next patch".

Perhaps a slight rearrangement would make things easier to grok: start
with the bug fix, and introduce .is_empty() there.  In the next patch,
say "In these places, the intent is to test for empty, but the actual
code exploits that there can be no variants.  Using .is_empty() instead
is a bit clearer and a bit more robust."

>>> +++ b/scripts/qapi-types.py
>>> @@ -90,7 +90,7 @@ struct %(c_name)s {
>>>      # potential issues with attempting to malloc space for zero-length
>>>      # structs in C, and also incompatibility with C++ (where an empty
>>>      # struct is size 1).
>>> -    if not (base and base.members) and not members and not variants:
>>> +    if (not base or base.is_empty()) and not members and not variants:
>>>          ret += mcgen('''
>>>      char qapi_dummy_for_empty_struct;
>>>  ''')
>> 
>> I figure the case for the helper based on this patch alone is making the
>> code a bit more future-proof.  Suggest you try to explain that in your
>> commit message, including against what future change exactly you're
>> proofing the code.
>
> And here, bases cannot (yet) have variants, but that's also on my plate
> of things I'd like to support in the future.

Also needs a use case.

>> Haven't reviewed for completeness.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 11/19] qapi: Add type.is_empty() helper
  2016-03-02 23:04     ` Eric Blake
@ 2016-03-03  7:18       ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-03-03  7:18 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 03/02/2016 12:04 PM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> And use it in qapi-types and qapi-event.  Down the road, we may
>>> want to lift our artificial restriction of no variants at the
>>> top level of an event, at which point, inlining our check for
>>> whether members is empty will no longer be sufficient, but
>>> adding a check for variants adds verbosity; in the meantime,
>>> add some asserts in places where we don't handle variants.
>> 
>> Perhaps I'm just running out of steam for today, but I've read this
>> twice, and still don't get why adding these assertions goes in the same
>> patch as adding the helper, or what it has to do with events.
>
> Okay, will split this into two patches for v3.

A better commit message might do.  Use your judgement.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 12/19] qapi: Fix command with named empty argument type
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 12/19] qapi: Fix command with named empty argument type Eric Blake
@ 2016-03-03  8:54   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-03-03  8:54 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> The generator special-cased
>
>  { 'command':'foo', 'data': {} }
>
> to avoid emitting a visitor variable, but failed to see that
>
>  { 'struct':'NamedEmptyType, 'data': {} }
>  { 'command':'foo', 'data':'NamedEmptyType' }
>
> needs the same treatment.  There, the generator happily generates a
> visitor to get no arguments, and a visitor to destroy no arguments;
> and the compiler isn't happy with that, as demonstrated by the updated
> qapi-schema-test.json:
>
>   tests/test-qmp-marshal.c: In function ‘qmp_marshal_user_def_cmd0’:
>   tests/test-qmp-marshal.c:264:14: error: variable ‘v’ set but not used [-Werror=unused-but-set-variable]
>        Visitor *v;
>                 ^
>
> No change to generated code except for the testsuite addition.

Using a named empty type when you could just as well use {} is a rather
remote corner case, and I therefore don't care how we translate it as
long as it works.  We could make the generator suppress just the
variable, or suppress the warning, or suppress the whole "get the
arguments" thing.  Your patch shows that the latter is easy enough, so
why not.

> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v2: no change
> v1: enhance commit message
> Previously posted as part of qapi cleanup subset E:
> v9: no change
> v8: no change
> v7: no change
> v6: new patch
> ---
>  scripts/qapi-commands.py                | 6 +++---
>  tests/test-qmp-commands.c               | 5 +++++
>  tests/qapi-schema/qapi-schema-test.json | 2 ++
>  tests/qapi-schema/qapi-schema-test.out  | 2 ++
>  4 files changed, 12 insertions(+), 3 deletions(-)
>
> diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
> index f44e01f..7c9cfbf 100644
> --- a/scripts/qapi-commands.py
> +++ b/scripts/qapi-commands.py
> @@ -65,7 +65,7 @@ def gen_marshal_vars(arg_type, ret_type):
>  ''',
>                       c_type=ret_type.c_type())
>
> -    if arg_type:
> +    if arg_type and not arg_type.is_empty():

Since variants aren't possible here, arg_type and arg_type.members would
accomplish the same, wouldn't it?

Kills the idea to use the bug fix as motivation for .is_empty()...

>          ret += mcgen('''
>      QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
>      QapiDeallocVisitor *qdv;
> @@ -97,7 +97,7 @@ def gen_marshal_vars(arg_type, ret_type):
>  def gen_marshal_input_visit(arg_type, dealloc=False):
>      ret = ''
>
> -    if not arg_type:
> +    if not arg_type or arg_type.is_empty():

Likweise.

>          return ret
>
>      if dealloc:
> @@ -177,7 +177,7 @@ def gen_marshal(name, arg_type, ret_type):
>
>      # 'goto out' produced by gen_marshal_input_visit->gen_visit_members()
>      # for each arg_type member, and by gen_call() for ret_type
> -    if (arg_type and arg_type.members) or ret_type:
> +    if (arg_type and not arg_type.is_empty()) or ret_type:

This is just for consistency with the two previous hunks.

>          ret += mcgen('''
>
>  out:
> diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
> index d6171f2..650ba46 100644
> --- a/tests/test-qmp-commands.c
> +++ b/tests/test-qmp-commands.c
> @@ -13,6 +13,11 @@ void qmp_user_def_cmd(Error **errp)
>  {
>  }
>
> +Empty2 *qmp_user_def_cmd0(Error **errp)
> +{
> +    return g_new0(Empty2, 1);
> +}
> +
>  void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp)
>  {
>  }
> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index b5d0c53..33e8517 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -18,6 +18,8 @@
>  { 'struct': 'Empty1', 'data': { } }
>  { 'struct': 'Empty2', 'base': 'Empty1', 'data': { } }
>
> +{ 'command': 'user_def_cmd0', 'data': 'Empty2', 'returns': 'Empty2' }
> +
>  # for testing override of default naming heuristic
>  { 'enum': 'QEnumTwo',
>    'prefix': 'QENUM_TWO',
> diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
> index 225e2db..6b97fa5 100644
> --- a/tests/qapi-schema/qapi-schema-test.out
> +++ b/tests/qapi-schema/qapi-schema-test.out
> @@ -203,6 +203,8 @@ command guest-sync :obj-guest-sync-arg -> any
>     gen=True success_response=True
>  command user_def_cmd None -> None
>     gen=True success_response=True
> +command user_def_cmd0 Empty2 -> Empty2
> +   gen=True success_response=True
>  command user_def_cmd1 :obj-user_def_cmd1-arg -> None
>     gen=True success_response=True
>  command user_def_cmd2 :obj-user_def_cmd2-arg -> UserDefTwo

Demonstrates another aspect of the arguments/results asymmetry in C and
QAPI.

At a sufficient level of abstraction, functions take one argument and
yield one result.  Multiple arguments and results are just notational
convenience.

Our schema language embraces this: both 'data' and 'returns' are the
name of the (single) argument/return type.  See the qapi-schema-test.out
snipped visible in the patch.

The schema also provides syntactic sugar for multiple arguments, so you
don't have to define the argument type yourself.  It doesn't do that for
results.

In C, functions take multiple arguments and yield one result.  The QAPI
code generator "unwraps" its single argument type into multiple C
arguments.  That's where the "no variants in argument type" restriction
comes from.  It can't unwrap the return type.  That's where the "no
sugar for return type" comes from: we really need a C type with a proper
name, and ':obj-frobnicate-res' wouldn't be one.

Unwrapping the empty argument type results in no arguments:
qmp_user_def_cmd0() takes only the additional Error ** argument.

Not unwrapping the empty result type results in returning nothing via
g_new0().  If that mattered, we could special-case it to return void
instead, but it doesn't.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 13/19] qapi-visit: Simplify visit of empty branch in union
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 13/19] qapi-visit: Simplify visit of empty branch in union Eric Blake
@ 2016-03-03  9:14   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-03-03  9:14 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Now that we have is_empty() and gen_visit_fields_call(), it's

s/fields/members/.  Better grep all the commit messages for "fields".

> fairly easy to skip the visit of a variant type that has no
> members.

I figure that alone would've been just as easy before
gen_visit_members_call(), but see below.

>           Only one such instance exists at the moment
> (CpuInfoOther),

My testing shows qapi-schema-test.json's Empty2 is also affected:

@@ -198,7 +198,6 @@ void visit_type_Empty2_members(Visitor *
 {
     Error *err = NULL;
 
-    visit_type_Empty1_members(v, (Empty1 *)obj, &err);
     if (err) {
         goto out;
     }

This isn't "the visit of a variant type that has no members", it's a
visit of an empty base type.

If you go back to PATCH 10, you can see this patch's stated purpose is
tied to the last hunk there, which uses gen_visit_members_call() for a
variant.  Its other use is for the base.

Suggest to rephrase this patch's commit message to capture both.

>                 but the idea of a union where some branches
> add no fields beyond the base type is common enough that we
> may add syntax for other cases, as in
>   { 'union':'U', 'base':'B', 'discriminator':'D',
>     'data': { 'default': {}, 'extra':'Type' } }

I find 'default' confusing, because a variant record's default case is
the case that applies to all the tag values not explicitly listed.  What
about simply 'case1' and 'case2'?

> Note that the Abort type is another example of an empty type,
> but it is only used by a simple union rather than a flat
> union; and with simple unions, the wrapper type providing
> the 'data' QMP key is not empty.

As so often, simple unions are anything but.

> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v2: no change
> ---
>  scripts/qapi-visit.py | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index dbae00c..a9de393 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -37,7 +37,9 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
>  def gen_visit_members_call(typ, c_name):
>      ret = ''
>      assert isinstance(typ, QAPISchemaObjectType)
> -    if typ.is_implicit():
> +    if typ.is_empty():
> +        pass
> +    elif typ.is_implicit():
>          # TODO ugly special case for simple union
>          assert len(typ.members) == 1
>          assert not typ.variants

Okay because the patch is trivial.  If it wasn't, I'd say avoiding the
call isn't worthwhile; the compiler might even optimize it to nothing.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 14/19] qapi: Don't special-case simple union wrappers
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 14/19] qapi: Don't special-case simple union wrappers Eric Blake
@ 2016-03-03 10:59   ` Markus Armbruster
  2016-03-03 16:12     ` Eric Blake
  0 siblings, 1 reply; 48+ messages in thread
From: Markus Armbruster @ 2016-03-03 10:59 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, Fam Zheng, Eduardo Habkost,
	open list:Block layer core, Michael S. Tsirkin, Michael Roth,
	Jan Kiszka, Jason Wang, qemu-devel, Vincenzo Maffione,
	Luiz Capitulino, Gerd Hoffmann, Paolo Bonzini, Igor Mammedov,
	Giuseppe Lettieri, Luigi Rizzo, Samuel Thibault

Eric Blake <eblake@redhat.com> writes:

> Simple unions were carrying a special case that hid their 'data'
> QMP member from the resulting C struct, via the hack method
> QAPISchemaObjectTypeVariant.simple_union_type().  But using the
> work we started by unboxing flat union and alternate branches, we
> expose the simple union's implicit type in qapi-types.h as an
> anonymous type, and drop our last use of the hack.
>
> | struct ImageInfoSpecific {
> |     ImageInfoSpecificKind type;
> |     union { /* union tag is @type */
> |         void *data;
> |-        ImageInfoSpecificQCow2 *qcow2;
> |-        ImageInfoSpecificVmdk *vmdk;
> |+        struct {
> |+            ImageInfoSpecificQCow2 *data;
> |+        } qcow2;
> |+        struct {
> |+            ImageInfoSpecificVmdk *data;
> |+        } vmdk;
> |     } u;
> | };
>
> All clients of simple unions have to adjust from using su->u.member
> to now using su->u.member.data;

By now, a reader not familiar with the code may wonder why this is an
improvement.  It is, because

* it removes an asymmetry between QAPI's QMP side and QAPI's C side
  (both now have 'data'), and

* it hopefully turns simple unions into sugar for flat unions as
  described in qapi-code-gen.txt, where before their equivalence only
  applied to the QMP side, not to the C side.

And that's well worth having to type .data in a few places.

Can we work that into the commit message?

>                                 while this touches a number of
> files in the tree, some earlier cleanup patches helped minimize
> the change to the initialization of a temporary variable rather
> than every single member access.  The generated qapi-visit.c code
> is included in the files affected by the layout change, with a

Suggest "is also affected by the layout change".

> diff that looks like:
>
> |@@ -4510,10 +4567,16 @@ static void visit_type_ImageInfoSpecific
> |     }
> |     switch (obj->type) {
> |     case IMAGE_INFO_SPECIFIC_KIND_QCOW2:
> |-        visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err);
> |+        visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2.data, &err);
> |+        if (err) {
> |+            goto out;
> |+        }
> |         break;
> |     case IMAGE_INFO_SPECIFIC_KIND_VMDK:
> |-        visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err);
> |+        visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk.data, &err);
> |+        if (err) {
> |+            goto out;
> |+        }
> |         break;
> |     default:
> |         abort();
> |     }
> |
> | out:
> |     error_propagate(errp, err);
>
> The added error checks there are a side effect of visiting all
> members of each implicit struct (there is only one such member for
> simple unions); but do not change semantics,

I'd say something like "Because we now use the general code for visiting
struct members, which must cope with multiple members, we get additional
error checks.  They're obviously harmless, and not worth suppressing in
the generator."

>                                              and will be important
> simple unions); but do not change semantics, and will be important
> when later patches allow for flat unions with anonymous branches
> with more than one member.  That future work will look like:
> { 'union': 'Foo', 'base': 'Base', 'discriminator': 'type',
>   'data': { 'branch1': { 'anonymous': 'str', 'number': 'int' },
>             'branch2': 'Named' } }

I'd probably forgo this remark on future plans.  It's not necessary to
justify the patch, and it could distract readers.

>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v2: rebase onto s/fields/members/ change, changes in master; pick
> up missing net/ files
> ---
>  scripts/qapi.py                 | 10 -----
>  scripts/qapi-types.py           | 22 ++++++++---
>  scripts/qapi-visit.py           | 15 +++-----
>  backends/baum.c                 |  2 +-
>  backends/msmouse.c              |  2 +-
>  block/nbd.c                     |  6 +--
>  block/qcow2.c                   |  6 +--
>  block/vmdk.c                    |  8 ++--
>  blockdev.c                      | 24 ++++++------
>  hmp.c                           |  8 ++--
>  hw/char/escc.c                  |  2 +-
>  hw/input/hid.c                  |  8 ++--
>  hw/input/ps2.c                  |  6 +--
>  hw/input/virtio-input-hid.c     |  8 ++--
>  hw/mem/pc-dimm.c                |  2 +-
>  net/dump.c                      |  2 +-
>  net/hub.c                       |  2 +-
>  net/l2tpv3.c                    |  2 +-
>  net/net.c                       |  4 +-
>  net/netmap.c                    |  2 +-
>  net/slirp.c                     |  2 +-
>  net/socket.c                    |  2 +-
>  net/tap-win32.c                 |  2 +-
>  net/tap.c                       |  4 +-
>  net/vde.c                       |  2 +-
>  net/vhost-user.c                |  2 +-
>  numa.c                          |  4 +-
>  qemu-char.c                     | 82 +++++++++++++++++++++--------------------
>  qemu-nbd.c                      |  6 +--
>  replay/replay-input.c           | 44 +++++++++++-----------
>  spice-qemu-char.c               | 14 ++++---
>  tests/test-io-channel-socket.c  | 40 ++++++++++----------
>  tests/test-qmp-commands.c       |  2 +-
>  tests/test-qmp-input-visitor.c  | 25 +++++++------
>  tests/test-qmp-output-visitor.c | 24 ++++++------
>  tpm.c                           |  2 +-
>  ui/console.c                    |  4 +-
>  ui/input-keymap.c               | 10 ++---
>  ui/input-legacy.c               |  8 ++--
>  ui/input.c                      | 34 ++++++++---------
>  ui/vnc-auth-sasl.c              |  3 +-
>  ui/vnc.c                        | 29 ++++++++-------
>  util/qemu-sockets.c             | 35 +++++++++---------
>  43 files changed, 263 insertions(+), 258 deletions(-)
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 83080b3..38121c5 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -1115,16 +1115,6 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
>      def __init__(self, name, typ):
>          QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
>
> -    # This function exists to support ugly simple union special cases
> -    # TODO get rid of them, and drop the function
> -    def simple_union_type(self):
> -        if (self.type.is_implicit() and
> -                isinstance(self.type, QAPISchemaObjectType)):
> -            assert len(self.type.members) == 1
> -            assert not self.type.variants
> -            return self.type.members[0].type
> -        return None
> -
>
>  class QAPISchemaAlternateType(QAPISchemaType):
>      def __init__(self, name, info, variants):
> diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
> index 6c1923d..1f090e6 100644
> --- a/scripts/qapi-types.py
> +++ b/scripts/qapi-types.py
> @@ -122,14 +122,24 @@ def gen_variants(variants):
>                  c_name=c_name(variants.tag_member.name))
>
>      for var in variants.variants:
> -        # Ugly special case for simple union TODO get rid of it
> -        simple_union_type = var.simple_union_type()
> -        typ = simple_union_type or var.type
> -        ret += mcgen('''
> +        if (isinstance(var.type, QAPISchemaObjectType) and
> +                var.type.is_implicit()):

Uh, this condition is exactly var.type.simple_union_type() != None.  I'm
afraid we still have a special case.

> +            ret += mcgen('''
> +        struct {
> +''')
> +            push_indent(8)
> +            ret += gen_struct_members(var.type.members)
> +            pop_indent(8)
> +            ret += mcgen('''
> +        } %(c_name)s;
> +''',
> +                         c_name=c_name(var.name))
> +        else:
> +            ret += mcgen('''
>          %(c_type)s %(c_name)s;
>  ''',
> -                     c_type=typ.c_type(is_unboxed=not simple_union_type),
> -                     c_name=c_name(var.name))
> +                         c_type=var.type.c_type(is_unboxed=True),
> +                         c_name=c_name(var.name))
>
>      ret += mcgen('''
>      } u;

Before:

       for var in variants.variants:
           # Ugly special case for simple union TODO get rid of it
           simple_union_type = var.simple_union_type()
           typ = simple_union_type or var.type
           ret += mcgen('''
           %(c_type)s %(c_name)s;
   ''',
                        c_type=typ.c_type(is_unboxed=not simple_union_type),
                        c_name=c_name(var.name))

Special treatment for simple unions: don't unbox.

After:

       for var in variants.variants:
           if (isinstance(var.type, QAPISchemaObjectType) and
                   var.type.is_implicit()):
               ret += mcgen('''
           struct {
   ''')
               push_indent(8)
               ret += gen_struct_members(var.type.members)
               pop_indent(8)
               ret += mcgen('''
           } %(c_name)s;
   ''',
                            c_name=c_name(var.name))
           else:
               ret += mcgen('''
           %(c_type)s %(c_name)s;
   ''',
                            c_type=var.type.c_type(is_unboxed=True),
                            c_name=c_name(var.name))

Special treatment for simple unions: instead of a member

    TypeOfBranch name_of_branch;

we generate one

    struct {
        TypeOfBranch data;
    } name_of_branch;

Example: qapi-schema-test.json's only simple union
UserDefNativeListUnion

    { 'union': 'UserDefNativeListUnion',
      'data': { 'integer': ['int'],
                [more of the same...] } }

qapi-schema-test.out:

    object UserDefNativeListUnion
        member type: UserDefNativeListUnionKind optional=False
        case integer: :obj-intList-wrapper
        [more of the same...]

where

    object :obj-intList-wrapper
        member data: intList optional=False

qapi-types.h:

    struct UserDefNativeListUnion {
        UserDefNativeListUnionKind type;
        union { /* union tag is @type */
            struct {
                intList *data;
            } integer;
            [more of the same...]
        } u;
    }

Without the special case, we'd get

    typedef struct :obj-intList-wrapper :obj-intList-wrapper;

    struct :obj-intList-wrapper {
        intList *data;
    } :obj-intList-wrapper;

    struct UserDefNativeListUnion {
        UserDefNativeListUnionKind type;
        union { /* union tag is @type */
            :obj-intList-wrapper integer;
            [more of the same...]
        } u;
    }

except QAPISchemaObjectType.c_name() would refuse to cooperate in
creating this nonsense.

Conclusion: you replace one special case by another one.  The
improvement is in that the new special case is less special.  Instead of
"if simple union variant, do something else", we now have

    Use the C type corresponding to the type, except when it's an
    implicit object type, use an anonymous struct type, because we don't
    have a C type then.

Should we have a C type even then?  We'd need to give it a reserved
name.

On first glance, the new special case is just as special at the old one:
it applies to simple unions.  But that's not necessarily so.  We could
make use of it elsewhere if we wanted.  We'd have to factor the code out
of the "for variants" loop, of course.  In other words, it's still
special, but its specialness is less arbitrary.  That's why it's an
improvement.

Next is the visit update for this change of the type layout.

> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index a9de393..e281d21 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -34,24 +34,20 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
>                   c_name=c_name(name))
>
>
> -def gen_visit_members_call(typ, c_name):
> +def gen_visit_members_call(typ, direct_name, implicit_name=None):

As noted in my review of PATCH 10, these are C expressions, not names.

>      ret = ''
>      assert isinstance(typ, QAPISchemaObjectType)
>      if typ.is_empty():
>          pass
>      elif typ.is_implicit():
> -        # TODO ugly special case for simple union
> -        assert len(typ.members) == 1
> +        assert implicit_name
>          assert not typ.variants
> -        ret += mcgen('''
> -    visit_type_%(c_type)s(v, "data", %(c_name)s, &err);
> -''',
> -                     c_type=typ.members[0].type.c_name(), c_name=c_name)
> +        ret += gen_visit_members(typ.members, prefix=implicit_name)
>      else:
>          ret += mcgen('''
>      visit_type_%(c_type)s_members(v, %(c_name)s, &err);
>  ''',
> -                     c_type=typ.c_name(), c_name=c_name)
> +                     c_type=typ.c_name(), c_name=direct_name)
>      return ret
>
>

Note that direct_name is only used when !typ.is_implicit(), and
implicit_name is only used when typ.is_implicit().

Further note that despite its name, gen_visit_members_call() doesn't
generate a call when typ.is_implicit().

Separate function for implicit type?

Before:

       if typ.is_empty():
           pass
       elif typ.is_implicit():
           # TODO ugly special case for simple union
           assert len(typ.members) == 1
           assert not typ.variants
           ret += mcgen('''
       visit_type_%(c_type)s(v, "data", %(c_name)s, &err);
   ''',
                        c_type=typ.members[0].type.c_name(), c_name=c_name)
       else:
           ret += mcgen('''
       visit_type_%(c_type)s_members(v, %(c_name)s, &err);
   ''',
                        c_type=typ.c_name(), c_name=c_name)

Special treatment for simple unions: don't unbox.  To visit a boxed
member of type T, we call visit_type_T().  To visit an unboxed one, we
call visit_type_T_members().

After:

       if typ.is_empty():
           pass
       elif typ.is_implicit():
           assert implicit_name
           assert not typ.variants
           ret += gen_visit_members(typ.members, prefix=implicit_name)
       else:
           ret += mcgen('''
       visit_type_%(c_type)s_members(v, %(c_name)s, &err);
   ''',
                        c_type=typ.c_name(), c_name=direct_name)

Special treatment for member of implicit type: generate inline code to
visit its members, because visit_type_T_members() doesn't exist then.

Should it exist?

> @@ -86,7 +82,8 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
>                                             variants.tag_member.type.prefix))
>              push_indent()
>              ret += gen_visit_members_call(var.type,
> -                                          '&obj->u.' + c_name(var.name))
> +                                          '&obj->u.' + c_name(var.name),
> +                                          'obj->u.' + c_name(var.name) + '.')
>              pop_indent()
>              ret += mcgen('''
>          break;

On to the boring part.

> diff --git a/backends/baum.c b/backends/baum.c
> index c11320e..eef3467 100644
> --- a/backends/baum.c
> +++ b/backends/baum.c
> @@ -567,7 +567,7 @@ static CharDriverState *chr_baum_init(const char *id,
>                                        ChardevReturn *ret,
>                                        Error **errp)
>  {
> -    ChardevCommon *common = backend->u.braille;
> +    ChardevCommon *common = backend->u.braille.data;
>      BaumDriverState *baum;
>      CharDriverState *chr;
>      brlapi_handle_t *handle;

Many trivial updates like this one.  The only interesting question is
whether you got them all.  What did you do to find them?

[...]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 15/19] qapi-visit: Move error check into gen_visit_members_call()
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 15/19] qapi-visit: Move error check into gen_visit_members_call() Eric Blake
@ 2016-03-03 11:56   ` Markus Armbruster
  2016-03-04 14:27     ` Eric Blake
  0 siblings, 1 reply; 48+ messages in thread
From: Markus Armbruster @ 2016-03-03 11:56 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> When first introduced, neither branch of gen_visit_members_call()
> would output a goto.  But now that the implicit struct visit
> always ends with a goto, we should do the same for regular
> struct visits, so that callers don't have to worry about whether
> they are creating two identical goto's in a row.
>
> Generated code gets slightly larger; if desired, we could patch
> qapi.py:gen_visit_members() to have a mode where it skips the
> final goto and leave it up to the callers when to use that mode,
> but that adds more maintenance burden when the compiler should
> be smart enough to not bloat the .o file just because the .c
> file got larger.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v2: rebase onto s/fields/members/ change
> ---
>  scripts/qapi-visit.py | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index e281d21..a17ecc1 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -48,6 +48,7 @@ def gen_visit_members_call(typ, direct_name, implicit_name=None):
       assert isinstance(typ, QAPISchemaObjectType)
       if typ.is_empty():
           pass
       elif typ.is_implicit():
           assert implicit_name
           assert not typ.variants
           ret += gen_visit_members(typ.members, prefix=implicit_name)

This is the goto-generating part mentioned in the commit message.

       else:
           ret += mcgen('''
>      visit_type_%(c_type)s_members(v, %(c_name)s, &err);
>  ''',
>                       c_type=typ.c_name(), c_name=direct_name)
> +        ret += gen_err_check()
>      return ret

Emitting the gen_err_check() right after emitting the call it checks
makes for simpler and more robust code.

>
>
> @@ -63,7 +64,6 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
>
>      if base:
>          ret += gen_visit_members_call(base, '(%s *)obj' % base.c_name())
> -        ret += gen_err_check()
>
>      ret += gen_visit_members(members, prefix='obj->')
>

Adding more context to show the other use of gen_visit_members_call():

       if variants:
           ret += mcgen('''
       switch (obj->%(c_name)s) {
   ''',
                        c_name=c_name(variants.tag_member.name))

           for var in variants.variants:
               ret += mcgen('''
       case %(case)s:
   ''',
                            case=c_enum_const(variants.tag_member.type.name,
                                              var.name,
                                              variants.tag_member.type.prefix))
               push_indent()
               ret += gen_visit_members_call(var.type,
                                             '&obj->u.' + c_name(var.name),
                                             'obj->u.' + c_name(var.name) + '.')

This is where the generated code grows.  Before the patch, we sometimes
omit the goto on error, which works, because the label comes right after
the switch.

               pop_indent()
               ret += mcgen('''
           break;
   ''')

           ret += mcgen('''
       default:
           abort();
       }
   ''')

I wonder whether it would be simpler to make gen_visit_members_call()
add the goto from the start.

> @@ -95,9 +95,9 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
>      }
>  ''')
>
> -    # 'goto out' produced for base, by gen_visit_members() for each member,
> -    # and if variants were present
> -    if base or members or variants:
> +    # 'goto out' produced for non-empty base, by gen_visit_members() for
> +    # each member, and if variants were present
> +    if (base and not base.is_empty()) or members or variants:
>          ret += mcgen('''
>
>  out:

Uh, sure this hunk belongs to this patch?

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 16/19] qapi: Allow anonymous base for flat union
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 16/19] qapi: Allow anonymous base for flat union Eric Blake
@ 2016-03-03 13:04   ` Markus Armbruster
  2016-03-04 14:32     ` Eric Blake
  0 siblings, 1 reply; 48+ messages in thread
From: Markus Armbruster @ 2016-03-03 13:04 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Rather than requiring all flat unions to explicitly create
> a separate base struct, we can allow the qapi schema to specify
> the common members via an inline dictionary. This is similar to
> how commands can specify an inline anonymous type for its 'data',

Suggest to end the sentence here, and then...

> and matches the fact that our code base already has several
> flat unions that had to create a separate base type that is used
> nowhere but in the union.

"We already have several struct types that only exist to serve as a
single flat union's base.  The next commits will clean them up."

Replace "them" by "some" if you don't clean them all up.

It's a nice step towards having a variant record type in the schema
language similar to what we have in introspection.

> The patch also has to tweak things to avoid calling base.c_name()
> on an implicit type (since implicit types do not have a name);
> we already have qapi_FOO_base() which does the trick nicely.
>
> Now that anonymous bases are legal, we need to rework the
> flat-union-bad-base negative test (as previously written, it
> forms what is now valid QAPI; tweak it to now provide coverage
> of a new error message path), and add a positive test in
> qapi-schema-test to use an anonymous base.
>
> Note that this patch only allows anonymous bases for flat
> unions; simple unions are enough syntactic sugar that we do
> not want to burden them further.

Indeed.

>                                   Meanwhile, it would be easy
> to modify qapi.py to also allow an anonymous base for structs;
> however, there is less of a compelling technical reason to
> do so, since you can always write the struct to directly
> contain any members that the anonymous base would have
> mentioned.

Suggest something like

      Note that while it would be easy to also allow an anonymous base
      for structs, that would be quite redundant, jsut put the members
      right into the struct instead.

> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v2: rebase onto s/fields/members/ change
> v1: rebase and rework to use gen_visit_fields_call(), test new error
> Previously posted as part of qapi cleanup subset F:
> v6: avoid redundant error check in gen_visit_union(), rebase to
> earlier gen_err_check() improvements
> ---
>  scripts/qapi.py                            | 11 +++++++++--
>  scripts/qapi-types.py                      | 12 +++++++-----
>  scripts/qapi-visit.py                      |  6 +++---
>  docs/qapi-code-gen.txt                     | 28 ++++++++++++++--------------
>  tests/qapi-schema/flat-union-bad-base.err  |  2 +-
>  tests/qapi-schema/flat-union-bad-base.json |  5 ++---
>  tests/qapi-schema/qapi-schema-test.json    |  6 +-----
>  tests/qapi-schema/qapi-schema-test.out     |  9 ++++-----
>  8 files changed, 41 insertions(+), 38 deletions(-)
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 38121c5..7c76442 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -329,6 +329,8 @@ class QAPISchemaParser(object):
>
>
>  def find_base_members(base):
> +    if isinstance(base, dict):
> +        return base
>      base_struct_define = find_struct(base)
>      if not base_struct_define:
>          return None
> @@ -562,9 +564,9 @@ def check_union(expr, expr_info):
>
>      # Else, it's a flat union.
>      else:
> -        # The object must have a string member 'base'.
> +        # The object must have a string or dictionary 'base'.
>          check_type(expr_info, "'base' for union '%s'" % name,
> -                   base, allow_metas=['struct'])
> +                   base, allow_dict=True, allow_metas=['struct'])
>          if not base:
>              raise QAPIExprError(expr_info,
>                                  "Flat union '%s' must have a base"

We still have a lot of semantic analysis code to move into class
QAPISchema.

> @@ -1039,6 +1041,8 @@ class QAPISchemaMember(object):
>              owner = owner[5:]
>              if owner.endswith('-arg'):
>                  return '(parameter of %s)' % owner[:-4]
> +            elif owner.endswith('-base'):
> +                return '(base of %s)' % owner[:-5]
>              else:
>                  assert owner.endswith('-wrapper')
>                  # Unreachable and not implemented
> @@ -1323,6 +1327,9 @@ class QAPISchema(object):
>          base = expr.get('base')
>          tag_name = expr.get('discriminator')
>          tag_member = None
> +        if isinstance(base, dict):
> +            base = (self._make_implicit_object_type(
> +                    name, info, 'base', self._make_members(base, info)))
>          if tag_name:
>              variants = [self._make_variant(key, value)
>                          for (key, value) in data.iteritems()]
> diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
> index 1f090e6..161443e 100644
> --- a/scripts/qapi-types.py
> +++ b/scripts/qapi-types.py
> @@ -72,13 +72,15 @@ struct %(c_name)s {
>  ''',
>                   c_name=c_name(name))
>
> -    if base:
> -        ret += mcgen('''
> +    if base and not base.is_empty():

I think suppressing the comments for empty base is more closely related
to PATCH 11..13 than to this patch.

> +        if not base.is_implicit():
> +            ret += mcgen('''
>      /* Members inherited from %(c_name)s: */
>  ''',
> -                     c_name=base.c_name())
> +                         c_name=base.c_name())
>          ret += gen_struct_members(base.members)
> -        ret += mcgen('''
> +        if not base.is_implicit():
> +            ret += mcgen('''
>      /* Own members: */
>  ''')
>      ret += gen_struct_members(members)
> @@ -238,7 +240,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
>      def visit_object_type(self, name, info, base, members, variants):
>          self._fwdecl += gen_fwd_object_or_array(name)
>          self.decl += gen_object(name, base, members, variants)
> -        if base:
> +        if base and not base.is_implicit():
>              self.decl += gen_upcast(name, base)
>          self._gen_type_cleanup(name)
>
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index a17ecc1..aa244cd 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -34,13 +34,12 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
>                   c_name=c_name(name))
>
>
> -def gen_visit_members_call(typ, direct_name, implicit_name=None):
> +def gen_visit_members_call(typ, direct_name, implicit_name):

@implicit_name introduced optional in PATCH 12.  I'd make it mandatory
from the start.

>      ret = ''
>      assert isinstance(typ, QAPISchemaObjectType)
>      if typ.is_empty():
>          pass
>      elif typ.is_implicit():
> -        assert implicit_name

Also fold into PATCH 12 then.

>          assert not typ.variants
>          ret += gen_visit_members(typ.members, prefix=implicit_name)
>      else:
> @@ -63,7 +62,8 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
>                  c_name=c_name(name))
>
>      if base:
> -        ret += gen_visit_members_call(base, '(%s *)obj' % base.c_name())
> +        ret += gen_visit_members_call(base, 'qapi_%s_base(obj)' % c_name(name),

I started at this for several minutes until I could guess what's going
on here.

The old code works fine when the type isn't implicit.

When it is, it fails the assertion in base.c_name(), even though
gen_visit_members_call() is not going to use its value.

You hack around it by passing 'qapi_NAME_base(obj)' instead.

If NAME isn't implicit, the function exists, and does the same as the
expression it replaces.

If NAME is implicit, the function doesn't exist, but
gen_visit_members_call() doesn't care, because it doesn't use the
argument then.

Ugh!  More evidence that we better not munge the two cases together into
one function.

> +                                      'obj->')

Argument needed because base can be implicit now.

>
>      ret += gen_visit_members(members, prefix='obj->')
>
> diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
> index 2519c07..1c8f113 100644
> --- a/docs/qapi-code-gen.txt
> +++ b/docs/qapi-code-gen.txt
> @@ -283,7 +283,7 @@ better than open-coding the field to be type 'str'.
>  === Union types ===
>
>  Usage: { 'union': STRING, 'data': DICT }
> -or:    { 'union': STRING, 'data': DICT, 'base': STRUCT-NAME,
> +or:    { 'union': STRING, 'data': DICT, 'base': STRUCT-NAME-OR-DICT,
>           'discriminator': ENUM-MEMBER-OF-BASE }
>
>  Union types are used to let the user choose between several different
> @@ -319,13 +319,16 @@ an implicit C enum 'NameKind' is created, corresponding to the union
>  the union can be named 'max', as this would collide with the implicit
>  enum.  The value for each branch can be of any type.
>
> -A flat union definition specifies a struct as its base, and
> -avoids nesting on the wire.  All branches of the union must be
> -complex types, and the top-level fields of the union dictionary on
> -the wire will be combination of fields from both the base type and the
> -appropriate branch type (when merging two dictionaries, there must be
> -no keys in common).  The 'discriminator' field must be the name of an
> -enum-typed member of the base struct.
> +A flat union definition avoids nesting on the wire, and specifies a
> +set of common fields that occur in all variants of the union.  The
> +'base' key must specifiy either a type name (the type must be a
> +struct, not a union), or a dictionary representing an anonymous type.
> +All branches of the union must be complex types, and the top-level
> +fields of the union dictionary on the wire will be combination of
> +fields from both the base type and the appropriate branch type (when
> +merging two dictionaries, there must be no keys in common).  The
> +'discriminator' field must be the name of an enum-typed member of the
> +base struct.
>
>  The following example enhances the above simple union example by
>  adding a common field 'readonly', renaming the discriminator to
> @@ -333,10 +336,8 @@ something more applicable, and reducing the number of {} required on
>  the wire:
>
>   { 'enum': 'BlockdevDriver', 'data': [ 'file', 'qcow2' ] }
> - { 'struct': 'BlockdevCommonOptions',
> -   'data': { 'driver': 'BlockdevDriver', 'readonly': 'bool' } }
>   { 'union': 'BlockdevOptions',
> -   'base': 'BlockdevCommonOptions',
> +   'base': { 'driver': 'BlockdevDriver', 'readonly': 'bool' },
>     'discriminator': 'driver',
>     'data': { 'file': 'FileOptions',
>               'qcow2': 'Qcow2Options' } }

Problematic, because it makes the example deviate further from the real
schema.

It deviates before your patch, but that looks accidental:
BlockdevOptions and BlockdevCommonOptions come from commit
5163149..5163149 (merged in commit 405c97c, July 2013, and by the time
BlockdevOptions made it into the schema (merged in commit 33c6cae,
October 2013), its base was named BlockdevOptionsBase.

Can we change the schema's BlockdevOptions to use the anonymous base
feature?

> @@ -354,7 +355,7 @@ code generator can ensure that branches exist for all values of the
>  enum (although the order of the keys need not match the declaration of
>  the enum).  In the resulting generated C data types, a flat union is
>  represented as a struct with the base member fields included directly,
> -and then a union of structures for each branch of the struct.
> +and then a union of pointers to structures for each branch of the struct.

Uh, that became wrong in commit 544a373 already, didn't it?

Is that a bug in PATCH 3 then?

>
>  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
> @@ -365,10 +366,9 @@ union has a struct with a single member named 'data'.  That is,
>  is identical on the wire to:
>
>   { 'enum': 'Enum', 'data': ['one', 'two'] }
> - { 'struct': 'Base', 'data': { 'type': 'Enum' } }
>   { 'struct': 'Branch1', 'data': { 'data': 'str' } }
>   { 'struct': 'Branch2', 'data': { 'data': 'int' } }
> - { 'union': 'Flat', 'base': 'Base', 'discriminator': 'type',
> + { 'union': 'Flat': 'base': { 'type': 'Enum' }, 'discriminator': 'type',
>     'data': { 'one': 'Branch1', 'two': 'Branch2' } }
>
>
> diff --git a/tests/qapi-schema/flat-union-bad-base.err b/tests/qapi-schema/flat-union-bad-base.err
> index 79b8a71..bee24a2 100644
> --- a/tests/qapi-schema/flat-union-bad-base.err
> +++ b/tests/qapi-schema/flat-union-bad-base.err
> @@ -1 +1 @@
> -tests/qapi-schema/flat-union-bad-base.json:9: 'base' for union 'TestUnion' should be a type name
> +tests/qapi-schema/flat-union-bad-base.json:8: 'string' (member of TestTypeA) collides with 'string' (base of TestUnion)
> diff --git a/tests/qapi-schema/flat-union-bad-base.json b/tests/qapi-schema/flat-union-bad-base.json
> index e2e622b..74dd421 100644
> --- a/tests/qapi-schema/flat-union-bad-base.json
> +++ b/tests/qapi-schema/flat-union-bad-base.json
> @@ -1,5 +1,4 @@
> -# we require the base to be an existing struct
> -# TODO: should we allow an anonymous inline base type?
> +# we allow anonymous base, but enforce no duplicate keys
>  { 'enum': 'TestEnum',
>    'data': [ 'value1', 'value2' ] }
>  { 'struct': 'TestTypeA',
> @@ -7,7 +6,7 @@
>  { 'struct': 'TestTypeB',
>    'data': { 'integer': 'int' } }
>  { 'union': 'TestUnion',
> -  'base': { 'enum1': 'TestEnum', 'kind': 'str' },
> +  'base': { 'enum1': 'TestEnum', 'string': 'str' },
>    'discriminator': 'enum1',
>    'data': { 'value1': 'TestTypeA',
>              'value2': 'TestTypeB' } }
> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index 33e8517..a6989d8 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -75,14 +75,10 @@
>    'base': 'UserDefZero',
>    'data': { 'string': 'str', 'enum1': 'EnumOne' } }
>
> -{ 'struct': 'UserDefUnionBase2',
> -  'base': 'UserDefZero',
> -  'data': { 'string': 'str', 'enum1': 'QEnumTwo' } }
> -
>  # this variant of UserDefFlatUnion defaults to a union that uses fields with
>  # allocated types to test corner cases in the cleanup/dealloc visitor
>  { 'union': 'UserDefFlatUnion2',
> -  'base': 'UserDefUnionBase2',
> +  'base': { 'string': 'str', 'enum1': 'QEnumTwo' },

You lost member 'integer' from the base's base.  Harmless (I think), but
visible when you compare generated output.

>    'discriminator': 'enum1',
>    'data': { 'value1' : 'UserDefC', # intentional forward reference
>              'value2' : 'UserDefB' } }
> diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
> index 6b97fa5..4ba347e 100644
> --- a/tests/qapi-schema/qapi-schema-test.out
> +++ b/tests/qapi-schema/qapi-schema-test.out
> @@ -8,6 +8,9 @@ object :obj-EVENT_D-arg
>      member b: str optional=False
>      member c: str optional=True
>      member enum3: EnumOne optional=True
> +object :obj-UserDefFlatUnion2-base
> +    member string: str optional=False
> +    member enum1: QEnumTwo optional=False
>  object :obj-__org.qemu_x-command-arg
>      member a: __org.qemu_x-EnumList optional=False
>      member b: __org.qemu_x-StructList optional=False
> @@ -121,7 +124,7 @@ object UserDefFlatUnion
>      case value2: UserDefB
>      case value3: UserDefB
>  object UserDefFlatUnion2
> -    base UserDefUnionBase2
> +    base :obj-UserDefFlatUnion2-base
>      tag enum1
>      case value1: UserDefC
>      case value2: UserDefB
> @@ -166,10 +169,6 @@ object UserDefUnionBase
>      base UserDefZero
>      member string: str optional=False
>      member enum1: EnumOne optional=False
> -object UserDefUnionBase2
> -    base UserDefZero
> -    member string: str optional=False
> -    member enum1: QEnumTwo optional=False
>  object UserDefZero
>      member integer: int optional=False
>  object WrapAlternate

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 17/19] qapi: Use anonymous base in SchemaInfo
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 17/19] qapi: Use anonymous base in SchemaInfo Eric Blake
@ 2016-03-03 13:06   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-03-03 13:06 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel

Eric Blake <eblake@redhat.com> writes:

> Now that the generator supports it, we might as well use an
> anonymous base rather than breaking out a single-use
> SchemaInfoBase structure.
>
> Oddly enough, this change does not affect the resulting
> introspection output (because we already inline the members of
> a base type into an object, and had no independent use of the
> base type reachable from a command).

Design vindicated :)

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

Patch looks good.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 18/19] qapi: Use anonymous base in CpuInfo
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 18/19] qapi: Use anonymous base in CpuInfo Eric Blake
@ 2016-03-03 13:08   ` Markus Armbruster
  2016-03-04 14:35     ` Eric Blake
  0 siblings, 1 reply; 48+ messages in thread
From: Markus Armbruster @ 2016-03-03 13:08 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Now that the generator supports it, we might as well use an
> anonymous base rather than breaking out a single-use CpuInfoBase
> structure.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>

Again, introspection value remains the same.

Patch looks good.

Should we eliminate more base types?

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 19/19] qapi: Make c_type() more OO-like
  2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 19/19] qapi: Make c_type() more OO-like Eric Blake
@ 2016-03-03 13:29   ` Markus Armbruster
  2016-03-04 14:37     ` Eric Blake
  0 siblings, 1 reply; 48+ messages in thread
From: Markus Armbruster @ 2016-03-03 13:29 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> QAPISchemaType.c_type() was a bit awkward.  Rather than having two
> optional orthogonal boolean flags that should never both be true,
> and where all callers pass a compile-time constant, provide three
> different method names that can be overridden as needed, and where
> the caller just uses the right variant.  It requires slightly more
> Python, but is arguably easier to read.
>
> Suggested-by: Markus Armbruster <armbru@redhat.com>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v2: no change
> ---
>  scripts/qapi.py       | 38 +++++++++++++++++++++++++++++---------
>  scripts/qapi-types.py |  2 +-
>  2 files changed, 30 insertions(+), 10 deletions(-)
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index d5f4e28..32f37d8 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -826,8 +826,17 @@ class QAPISchemaVisitor(object):
>
>
>  class QAPISchemaType(QAPISchemaEntity):
> -    def c_type(self, is_param=False, is_unboxed=False):
> -        return c_name(self.name) + pointer_suffix
> +    # Normal usage of the type, such as declaring a local variable
> +    def c_type(self):
> +        pass
> +
> +    # Use of a type in a parameter list
> +    def c_param_type(self):
> +        return self.c_type()

These two make sense for any type.  The fact that we override
c_param_type() only for object types is detail.

> +
> +    # Use of a type in a struct declaration
> +    def c_unboxed_type(self):
> +        return self.c_type()

This one I find bit problematic.  We only ever box object types, but
those we box almost everywhere.  This method gets used in the select
places where we don't box them (currently just one).  I'm afraid "used
in a struct declaration" isn't a good definition.

I initially suggested to define c_unboxed_type() only for
QAPISchemaObjectType because there boxed vs. unboxed makes sense.
However, your code would like to call it for arbitrary types.

Perhaps we can cure my belly-ache with nothing more than carefully
drafted function contracts.  Let me try.

    # Return the C type for common use.
    # For the types we commonly box, this is a pointer type.
    def c_type(self):

    # Return the C type to be used in a parameter list.
    def c_param_type(self):

    # Return the C type to be used where we suppress boxing.
    def c_unboxed_type(self):

>
>      def c_null(self):
>          return 'NULL'
> @@ -859,8 +868,11 @@ class QAPISchemaBuiltinType(QAPISchemaType):
>      def c_name(self):
>          return self.name
>
> -    def c_type(self, is_param=False, is_unboxed=False):
> -        if is_param and self.name == 'str':
> +    def c_type(self):
> +        return self._c_type_name
> +
> +    def c_param_type(self):
> +        if self.name == 'str':
>              return 'const ' + self._c_type_name
>          return self._c_type_name
>
> @@ -893,7 +905,7 @@ class QAPISchemaEnumType(QAPISchemaType):
>          # See QAPISchema._make_implicit_enum_type()
>          return self.name.endswith('Kind')
>
> -    def c_type(self, is_param=False, is_unboxed=False):
> +    def c_type(self):
>          return c_name(self.name)
>
>      def member_names(self):
> @@ -925,6 +937,9 @@ class QAPISchemaArrayType(QAPISchemaType):
>      def is_implicit(self):
>          return True
>
> +    def c_type(self):
> +        return c_name(self.name) + pointer_suffix
> +
>      def json_type(self):
>          return 'array'
>
> @@ -994,12 +1009,14 @@ class QAPISchemaObjectType(QAPISchemaType):
>          assert not self.is_implicit()
>          return QAPISchemaType.c_name(self)
>
> -    def c_type(self, is_param=False, is_unboxed=False):
> +    def c_type(self):
>          assert not self.is_implicit()
> -        if is_unboxed:
> -            return c_name(self.name)
>          return c_name(self.name) + pointer_suffix
>
> +    def c_unboxed_type(self):
> +        assert not self.is_implicit()
> +        return c_name(self.name)
> +
>      def json_type(self):
>          return 'object'
>
> @@ -1140,6 +1157,9 @@ class QAPISchemaAlternateType(QAPISchemaType):
>          for v in self.variants.variants:
>              v.check_clash(self.info, seen)
>
> +    def c_type(self):
> +        return c_name(self.name) + pointer_suffix
> +
>      def json_type(self):
>          return 'value'
>
> @@ -1634,7 +1654,7 @@ def gen_params(arg_type, extra):
>          sep = ', '
>          if memb.optional:
>              ret += 'bool has_%s, ' % c_name(memb.name)
> -        ret += '%s %s' % (memb.type.c_type(is_param=True), c_name(memb.name))
> +        ret += '%s %s' % (memb.type.c_param_type(), c_name(memb.name))
>      if extra:
>          ret += sep + extra
>      return ret
> diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
> index 161443e..ce6d6f9 100644
> --- a/scripts/qapi-types.py
> +++ b/scripts/qapi-types.py
> @@ -140,7 +140,7 @@ def gen_variants(variants):
>              ret += mcgen('''
>          %(c_type)s %(c_name)s;
>  ''',
> -                         c_type=var.type.c_type(is_unboxed=True),
> +                         c_type=var.type.c_unboxed_type(),
>                           c_name=c_name(var.name))
>
>      ret += mcgen('''

Looks good otherwise.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 14/19] qapi: Don't special-case simple union wrappers
  2016-03-03 10:59   ` Markus Armbruster
@ 2016-03-03 16:12     ` Eric Blake
  0 siblings, 0 replies; 48+ messages in thread
From: Eric Blake @ 2016-03-03 16:12 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Fam Zheng, Eduardo Habkost,
	open list:Block layer core, Michael S. Tsirkin, Michael Roth,
	Jan Kiszka, Jason Wang, qemu-devel, Vincenzo Maffione,
	Luiz Capitulino, Gerd Hoffmann, Paolo Bonzini, Igor Mammedov,
	Giuseppe Lettieri, Luigi Rizzo, Samuel Thibault

[-- Attachment #1: Type: text/plain, Size: 7024 bytes --]

On 03/03/2016 03:59 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Simple unions were carrying a special case that hid their 'data'
>> QMP member from the resulting C struct, via the hack method
>> QAPISchemaObjectTypeVariant.simple_union_type().  But using the
>> work we started by unboxing flat union and alternate branches, we
>> expose the simple union's implicit type in qapi-types.h as an
>> anonymous type, and drop our last use of the hack.
>>

>> All clients of simple unions have to adjust from using su->u.member
>> to now using su->u.member.data;
> 
> By now, a reader not familiar with the code may wonder why this is an
> improvement.  It is, because
> 
> * it removes an asymmetry between QAPI's QMP side and QAPI's C side
>   (both now have 'data'), and
> 
> * it hopefully turns simple unions into sugar for flat unions as
>   described in qapi-code-gen.txt, where before their equivalence only
>   applied to the QMP side, not to the C side.
> 
> And that's well worth having to type .data in a few places.
> 
> Can we work that into the commit message?

Yes, definitely.


>> +++ b/scripts/qapi-types.py
>> @@ -122,14 +122,24 @@ def gen_variants(variants):
>>                  c_name=c_name(variants.tag_member.name))
>>
>>      for var in variants.variants:
>> -        # Ugly special case for simple union TODO get rid of it
>> -        simple_union_type = var.simple_union_type()
>> -        typ = simple_union_type or var.type
>> -        ret += mcgen('''
>> +        if (isinstance(var.type, QAPISchemaObjectType) and
>> +                var.type.is_implicit()):
> 
> Uh, this condition is exactly var.type.simple_union_type() != None.  I'm
> afraid we still have a special case.

The isinstance() is necessary because of alternates - a builtin type
branch to an alternate is implicit, but must be emitted directly, only
object types can be unboxed.  And, down the road, if we DO add anonymous
branches to a flat union, then this condition will also work for that
anonymous branch (in fact, I have it in my local tree, just not part of
this series).  Yes, that's the part of the commit message you said I
could drop, but I'll have to come up with some way to highlight that
potential in the commit message.

> 
> Special treatment for simple unions: instead of a member
> 

Rather, special treatment for an implicit object branch (right now, only
simple unions have implicit object branches, but an anonymous branch to
a flat union would also qualify for this treatment):

>     TypeOfBranch name_of_branch;
> 
> we generate one
> 
>     struct {
>         TypeOfBranch data;
>     } name_of_branch;
> 

> Without the special case, we'd get
> 
>     typedef struct :obj-intList-wrapper :obj-intList-wrapper;
> 
>     struct :obj-intList-wrapper {
>         intList *data;
>     } :obj-intList-wrapper;
> 
>     struct UserDefNativeListUnion {
>         UserDefNativeListUnionKind type;
>         union { /* union tag is @type */
>             :obj-intList-wrapper integer;
>             [more of the same...]
>         } u;
>     }
> 
> except QAPISchemaObjectType.c_name() would refuse to cooperate in
> creating this nonsense.
> 
> Conclusion: you replace one special case by another one.  The
> improvement is in that the new special case is less special.  Instead of
> "if simple union variant, do something else", we now have
> 
>     Use the C type corresponding to the type, except when it's an
>     implicit object type, use an anonymous struct type, because we don't
>     have a C type then.

Yes, exactly.  Words I should use in my commit message :)

> 
> Should we have a C type even then?  We'd need to give it a reserved
> name.

I found it easier to inline an anonymous struct than to think about how
to create a reserved name.  Maybe that decision of mine can be revisited.

> 
> On first glance, the new special case is just as special at the old one:
> it applies to simple unions.  But that's not necessarily so.  We could
> make use of it elsewhere if we wanted.  We'd have to factor the code out
> of the "for variants" loop, of course.  In other words, it's still
> special, but its specialness is less arbitrary.  That's why it's an
> improvement.
> 
> Next is the visit update for this change of the type layout.
> 
> Note that direct_name is only used when !typ.is_implicit(), and
> implicit_name is only used when typ.is_implicit().
> 
> Further note that despite its name, gen_visit_members_call() doesn't
> generate a call when typ.is_implicit().
> 
> Separate function for implicit type?

By the end of the series, we have two callers of the helper; if we split
to two helpers, then both callers have to test for .is_implicit() (and
it gets worse if I find a third place to use this helper in a later
patch).  My goal was to make the helper do as much as possible to
simplify the callers, but I got stuck at how to pass the difference
between a direct-use prefix vs. an implicit-use prefix.

Again, maybe the idea of creating a named C type for implicit types
would make this simpler.

> After:
> 
>        if typ.is_empty():
>            pass
>        elif typ.is_implicit():
>            assert implicit_name
>            assert not typ.variants
>            ret += gen_visit_members(typ.members, prefix=implicit_name)
>        else:
>            ret += mcgen('''
>        visit_type_%(c_type)s_members(v, %(c_name)s, &err);
>    ''',
>                         c_type=typ.c_name(), c_name=direct_name)
> 
> Special treatment for member of implicit type: generate inline code to
> visit its members, because visit_type_T_members() doesn't exist then.
> 
> Should it exist?

Only if we create a named C type for uses of implicit types.

>> +++ b/backends/baum.c
>> @@ -567,7 +567,7 @@ static CharDriverState *chr_baum_init(const char *id,
>>                                        ChardevReturn *ret,
>>                                        Error **errp)
>>  {
>> -    ChardevCommon *common = backend->u.braille;
>> +    ChardevCommon *common = backend->u.braille.data;
>>      BaumDriverState *baum;
>>      CharDriverState *chr;
>>      brlapi_handle_t *handle;
> 
> Many trivial updates like this one.  The only interesting question is
> whether you got them all.  What did you do to find them?

The compiler caught most of them.  For a few others, particularly under
net/, it was search and replace (basically, the compiler warned me about
some uses of NetClientOptions now being different, so I then grepped for
ALL uses of NetClientOptions to pick up the ones that I'm not set up to
compile).

I may have missed something, but it is a compiler error, so someone
would flag it pretty quickly if they are set up to compile code that I
am not.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 15/19] qapi-visit: Move error check into gen_visit_members_call()
  2016-03-03 11:56   ` Markus Armbruster
@ 2016-03-04 14:27     ` Eric Blake
  0 siblings, 0 replies; 48+ messages in thread
From: Eric Blake @ 2016-03-04 14:27 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 2735 bytes --]

On 03/03/2016 04:56 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> When first introduced, neither branch of gen_visit_members_call()
>> would output a goto.  But now that the implicit struct visit
>> always ends with a goto, we should do the same for regular
>> struct visits, so that callers don't have to worry about whether
>> they are creating two identical goto's in a row.
>>
>> Generated code gets slightly larger; if desired, we could patch
>> qapi.py:gen_visit_members() to have a mode where it skips the
>> final goto and leave it up to the callers when to use that mode,
>> but that adds more maintenance burden when the compiler should
>> be smart enough to not bloat the .o file just because the .c
>> file got larger.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>

>> +++ b/scripts/qapi-visit.py
>> @@ -48,6 +48,7 @@ def gen_visit_members_call(typ, direct_name, implicit_name=None):
>        assert isinstance(typ, QAPISchemaObjectType)
>        if typ.is_empty():
>            pass
>        elif typ.is_implicit():
>            assert implicit_name
>            assert not typ.variants
>            ret += gen_visit_members(typ.members, prefix=implicit_name)
> 
> This is the goto-generating part mentioned in the commit message.

> 
>> @@ -95,9 +95,9 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
>>      }
>>  ''')
>>
>> -    # 'goto out' produced for base, by gen_visit_members() for each member,
>> -    # and if variants were present
>> -    if base or members or variants:
>> +    # 'goto out' produced for non-empty base, by gen_visit_members() for
>> +    # each member, and if variants were present
>> +    if (base and not base.is_empty()) or members or variants:
>>          ret += mcgen('''
>>
>>  out:
> 
> Uh, sure this hunk belongs to this patch?

Unfortunately, yeah - because the empty base case doesn't generate a
goto.  I'm leaning more and more towards not bothering to special case
empty types on the next round.

I've already started playing with making type.c_name() work on implicit
types - it generates names like '_obj_Foo_wrapper', which won't collide
(because we reserved leading underscore for our own use), but also
doesn't quite match conventional naming conventions - but as long as it
is used only in generated code, it isn't that bad.  And with an implicit
type directly laid out, I can then just blindly call
visit_type_FOO_members(), even for implicit base or variant.  The v4
spin of the second half of this series is looking promising...

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 16/19] qapi: Allow anonymous base for flat union
  2016-03-03 13:04   ` Markus Armbruster
@ 2016-03-04 14:32     ` Eric Blake
  0 siblings, 0 replies; 48+ messages in thread
From: Eric Blake @ 2016-03-04 14:32 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 3753 bytes --]

On 03/03/2016 06:04 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Rather than requiring all flat unions to explicitly create
>> a separate base struct, we can allow the qapi schema to specify
>> the common members via an inline dictionary. This is similar to
>> how commands can specify an inline anonymous type for its 'data',
> 
> Suggest to end the sentence here, and then...
> 
>> and matches the fact that our code base already has several
>> flat unions that had to create a separate base type that is used
>> nowhere but in the union.
> 
> "We already have several struct types that only exist to serve as a
> single flat union's base.  The next commits will clean them up."
> 
> Replace "them" by "some" if you don't clean them all up.
> 
> It's a nice step towards having a variant record type in the schema
> language similar to what we have in introspection.
> 

>> @@ -63,7 +62,8 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
>>                  c_name=c_name(name))
>>
>>      if base:
>> -        ret += gen_visit_members_call(base, '(%s *)obj' % base.c_name())
>> +        ret += gen_visit_members_call(base, 'qapi_%s_base(obj)' % c_name(name),
> 
> I started at this for several minutes until I could guess what's going
> on here.
> 
> The old code works fine when the type isn't implicit.
> 
> When it is, it fails the assertion in base.c_name(), even though
> gen_visit_members_call() is not going to use its value.
> 
> You hack around it by passing 'qapi_NAME_base(obj)' instead.
> 
> If NAME isn't implicit, the function exists, and does the same as the
> expression it replaces.
> 
> If NAME is implicit, the function doesn't exist, but
> gen_visit_members_call() doesn't care, because it doesn't use the
> argument then.
> 
> Ugh!  More evidence that we better not munge the two cases together into
> one function.

Even with my v4 work towards exposing implicit types as a concrete
struct, I'm still not creating qapi_NAME_base(obj) for objects with an
implicit type.  But '(_obj_FOO_base *)FOO' works well for a base with a
concrete implicit base type.


>> @@ -354,7 +355,7 @@ code generator can ensure that branches exist for all values of the
>>  enum (although the order of the keys need not match the declaration of
>>  the enum).  In the resulting generated C data types, a flat union is
>>  represented as a struct with the base member fields included directly,
>> -and then a union of structures for each branch of the struct.
>> +and then a union of pointers to structures for each branch of the struct.
> 
> Uh, that became wrong in commit 544a373 already, didn't it?
> 
> Is that a bug in PATCH 3 then?

Yes, and fixed up accordingly in my v3 respin.  (I think it was some
rebase conflicts that I resolved incorrectly at some point).


>> +++ b/tests/qapi-schema/qapi-schema-test.json
>> @@ -75,14 +75,10 @@
>>    'base': 'UserDefZero',
>>    'data': { 'string': 'str', 'enum1': 'EnumOne' } }
>>
>> -{ 'struct': 'UserDefUnionBase2',
>> -  'base': 'UserDefZero',
>> -  'data': { 'string': 'str', 'enum1': 'QEnumTwo' } }
>> -
>>  # this variant of UserDefFlatUnion defaults to a union that uses fields with
>>  # allocated types to test corner cases in the cleanup/dealloc visitor
>>  { 'union': 'UserDefFlatUnion2',
>> -  'base': 'UserDefUnionBase2',
>> +  'base': { 'string': 'str', 'enum1': 'QEnumTwo' },
> 
> You lost member 'integer' from the base's base.  Harmless (I think), but
> visible when you compare generated output.

Easy enough to keep.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 18/19] qapi: Use anonymous base in CpuInfo
  2016-03-03 13:08   ` Markus Armbruster
@ 2016-03-04 14:35     ` Eric Blake
  0 siblings, 0 replies; 48+ messages in thread
From: Eric Blake @ 2016-03-04 14:35 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 777 bytes --]

On 03/03/2016 06:08 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Now that the generator supports it, we might as well use an
>> anonymous base rather than breaking out a single-use CpuInfoBase
>> structure.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
> 
> Again, introspection value remains the same.
> 
> Patch looks good.
> 
> Should we eliminate more base types?

At this point, we only have one other: BlockdevOptions.  Yes, I'll add
it to the list of cleanups.  I also have patches posted in the "subset
F" series that converts 'netdev_add' to be introspectible, and which
also uses the anonymous base.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v2 19/19] qapi: Make c_type() more OO-like
  2016-03-03 13:29   ` Markus Armbruster
@ 2016-03-04 14:37     ` Eric Blake
  0 siblings, 0 replies; 48+ messages in thread
From: Eric Blake @ 2016-03-04 14:37 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 1944 bytes --]

On 03/03/2016 06:29 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> QAPISchemaType.c_type() was a bit awkward.  Rather than having two
>> optional orthogonal boolean flags that should never both be true,
>> and where all callers pass a compile-time constant, provide three
>> different method names that can be overridden as needed, and where
>> the caller just uses the right variant.  It requires slightly more
>> Python, but is arguably easier to read.
>>

>> +
>> +    # Use of a type in a struct declaration
>> +    def c_unboxed_type(self):
>> +        return self.c_type()
> 
> This one I find bit problematic.  We only ever box object types, but
> those we box almost everywhere.  This method gets used in the select
> places where we don't box them (currently just one).  I'm afraid "used
> in a struct declaration" isn't a good definition.
> 
> I initially suggested to define c_unboxed_type() only for
> QAPISchemaObjectType because there boxed vs. unboxed makes sense.
> However, your code would like to call it for arbitrary types.

qapi-types.py is calling it for arbitrary types thanks to QAPI
alternates.  If I didn't put it here, then the code there needs to
special-case for isinstance(type, QAPISchemaObjectType) before calling
.c_unboxed_type(), and fall back to .c_type() otherwise.

> 
> Perhaps we can cure my belly-ache with nothing more than carefully
> drafted function contracts.  Let me try.
> 
>     # Return the C type for common use.
>     # For the types we commonly box, this is a pointer type.
>     def c_type(self):
> 
>     # Return the C type to be used in a parameter list.
>     def c_param_type(self):
> 
>     # Return the C type to be used where we suppress boxing.
>     def c_unboxed_type(self):

Sure, that helps.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

end of thread, other threads:[~2016-03-04 14:38 UTC | newest]

Thread overview: 48+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-02-25 23:38 [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Eric Blake
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 01/19] qapi: Rename 'fields' to 'members' in internal interface Eric Blake
2016-03-02 17:15   ` Markus Armbruster
2016-03-02 20:05     ` Eric Blake
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 02/19] qapi-visit: Expose visit_type_FOO_members() Eric Blake
2016-03-02 17:24   ` Markus Armbruster
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 03/19] qapi: Update docs to match recent generator changes Eric Blake
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 04/19] chardev: Shorten references into ChardevBackend Eric Blake
2016-03-02 17:55   ` Markus Armbruster
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 05/19] util: Shorten references into SocketAddress Eric Blake
2016-03-02 18:03   ` Markus Armbruster
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 06/19] ui: Shorten references into InputEvent Eric Blake
2016-03-01 15:32   ` [Qemu-devel] [PATCH v2.5 " Eric Blake
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 07/19] qapi: Avoid use of 'data' member of qapi unions Eric Blake
2016-03-02 18:18   ` Markus Armbruster
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 08/19] chardev: Drop useless ChardevDummy type Eric Blake
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 09/19] qapi: Drop useless 'data' member of unions Eric Blake
2016-03-02 18:30   ` Markus Armbruster
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 10/19] qapi-visit: Factor out gen_visit_members_call() Eric Blake
2016-03-02 18:53   ` Markus Armbruster
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 11/19] qapi: Add type.is_empty() helper Eric Blake
2016-03-02 19:04   ` Markus Armbruster
2016-03-02 20:16     ` Eric Blake
2016-03-03  7:08       ` Markus Armbruster
2016-03-02 23:04     ` Eric Blake
2016-03-03  7:18       ` Markus Armbruster
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 12/19] qapi: Fix command with named empty argument type Eric Blake
2016-03-03  8:54   ` Markus Armbruster
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 13/19] qapi-visit: Simplify visit of empty branch in union Eric Blake
2016-03-03  9:14   ` Markus Armbruster
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 14/19] qapi: Don't special-case simple union wrappers Eric Blake
2016-03-03 10:59   ` Markus Armbruster
2016-03-03 16:12     ` Eric Blake
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 15/19] qapi-visit: Move error check into gen_visit_members_call() Eric Blake
2016-03-03 11:56   ` Markus Armbruster
2016-03-04 14:27     ` Eric Blake
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 16/19] qapi: Allow anonymous base for flat union Eric Blake
2016-03-03 13:04   ` Markus Armbruster
2016-03-04 14:32     ` Eric Blake
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 17/19] qapi: Use anonymous base in SchemaInfo Eric Blake
2016-03-03 13:06   ` Markus Armbruster
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 18/19] qapi: Use anonymous base in CpuInfo Eric Blake
2016-03-03 13:08   ` Markus Armbruster
2016-03-04 14:35     ` Eric Blake
2016-02-25 23:38 ` [Qemu-devel] [PATCH v2 19/19] qapi: Make c_type() more OO-like Eric Blake
2016-03-03 13:29   ` Markus Armbruster
2016-03-04 14:37     ` Eric Blake
2016-03-01 15:02 ` [Qemu-devel] [PATCH v2 00/19] easier unboxed visits/qapi implicit types Markus Armbruster

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.