All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B')
@ 2015-10-16  4:15 Eric Blake
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 01/17] qapi: Add tests for reserved name abuse Eric Blake
                   ` (17 more replies)
  0 siblings, 18 replies; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Eduardo Habkost

Pending prerequisite: Markus' qapi-next branch (which has the first
half of my subset B patches):
git://repo.or.cz/qemu/armbru.git pull-qapi-2015-10-15
https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg03321.html

Pending prerequisite: Paolo's rewrite of qemu-char:
https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg03238.html

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

and I plan to eventually forcefully update my branch with the rest
of the v5 series, at:
http://repo.or.cz/qemu/ericb.git/shortlog/refs/heads/qapi

v9 notes:
Calling this subset B', because it is mostly new content, but
based heavily on review comments on subset B.  Also, I need to
rebase pending subset C on top of this.  A couple patches
from v5 have been hoisted earlier into the series; while v8
11/18 (Simplify gen_struct_field()) makes an appearance at the
end, and v8 10/18 (Move union tag quirks into subclass) is all
but eliminated.  Since the concepts of these patches has been
on the list for a while, it should be safe to take even during
soft freeze, even if the actual versions of these patches have
lots of new content.

The general goal of this batch of patches is to rework the C
layout of qapi unions so that we get rid of the type/kind
mismatch and isolate tag values from colliding with QMP names.
The tail end of v8 (detecting collisions in check()) will have
to wait a bit longer, but it should be a lot easier to reason
about now that there are fewer collisions possible.

Not worth a backport diff, since most of it is new or previously
unreviewed.

v8 notes:
https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02879.html
Address review comments, including fixing a bug with tracking
branch name collisions. Include a few more simple test cleanups
(4-6), and add another patch (11) that will make converting from
kind=>type easier in a later subset.  More details in per-patch
changelogs.

v7 notes:
https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg01387.html
Address comments, including a couple of new commits that made
later patches a bit cleaner.  Backport diff gets a bit confused
by a couple of patch titles changing.

v6 notes:
https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg00562.html
This is patches 11-16 of my v5 series; it has grown a bit with
splitting some patches and adding some others.  I suspect that
12/12 on this series will be discarded, but am including it because
it was split from v5 content.

Not much review comments other than on the original 11/46, but there
is enough churn due to rebasing that it's now easier to review this
version than plowing through v5.

Subset C (and more?) will come later.

In v5:
https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg05410.html
I _did_ rearrange patches to try and group related features:

1-2: Groundwork cleanups
3-5: Add more test cases
6-16: Front-end cleanups
17-18: Introspection output cleanups
19-20: 'alternate' type cleanups
21-29: qapi visitor cleanups
30-45: qapi-ify netdev_add
46: add qapi shorthand for flat unions

Lots of fixes based on additional testing, and rebased to
track other changes that happened in the meantime.  The series
is huge; I can split off smaller portions as requested.

In v4:
https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg02580.html
add some more clean up patches
rebase to Markus' recent work
pull in part of Zoltán's work to make netdev_add a flat union,
further enhancing it to be introspectible

I might be able to rearrange some of these patches, or separate
it into smaller independent series, if requested; but I'm
posting now to get review started.

In v3:
https://lists.gnu.org/archive/html/qemu-devel/2015-08/msg02059.html
redo cleanup of dealloc of partial struct
add patches to make all visit_type_*() avoid leaks on failure
add patches to allow boxed command arguments and events

In v2:
https://lists.gnu.org/archive/html/qemu-devel/2015-08/msg00900.html
rebase to Markus' v3 series
rework how comments are emitted for fields inherited from base
additional patches added for deleting colliding 'void *data'
documentation updates to match code changes

v1 was here:
https://lists.gnu.org/archive/html/qemu-devel/2015-07/msg05266.html
https://lists.gnu.org/archive/html/qemu-devel/2015-07/msg05325.html

Eric Blake (17):
  qapi: Add tests for reserved name abuse
  qapi: Reserve '*List' type names for arrays
  qapi: Reserve 'u' and 'has[-_]*' member names
  vnc: hoist allocation of VncBasicInfo to callers
  qapi: Unbox base members
  qapi-visit: Remove redundant functions for flat union base
  qapi: Start converting to new qapi union layout
  tests: Convert to new qapi union layout
  block: Convert to new qapi union layout
  nbd: Convert to new qapi union layout
  net: Convert to new qapi union layout
  char: Convert to new qapi union layout
  input: Convert to new qapi union layout
  memory: Convert to new qapi union layout
  tpm: Convert to new qapi union layout
  qapi: Finish converting to new qapi union layout
  qapi: Simplify gen_struct_field()

 block/nbd.c                                        |  18 +--
 block/qcow2.c                                      |  10 +-
 block/vmdk.c                                       |   6 +-
 blockdev.c                                         |  47 +++---
 docs/qapi-code-gen.txt                             |   8 +-
 hmp.c                                              |  26 ++--
 hw/char/escc.c                                     |  12 +-
 hw/input/hid.c                                     |  32 ++---
 hw/input/ps2.c                                     |  24 ++--
 hw/input/virtio-input-hid.c                        |  27 ++--
 hw/mem/pc-dimm.c                                   |   6 +-
 net/dump.c                                         |   4 +-
 net/hub.c                                          |   4 +-
 net/l2tpv3.c                                       |   4 +-
 net/net.c                                          |  24 ++--
 net/slirp.c                                        |   4 +-
 net/socket.c                                       |   4 +-
 net/tap-win32.c                                    |   4 +-
 net/tap.c                                          |   8 +-
 net/vde.c                                          |   4 +-
 net/vhost-user.c                                   |   4 +-
 numa.c                                             |   8 +-
 qemu-char.c                                        | 160 ++++++++++-----------
 qemu-nbd.c                                         |  16 +--
 scripts/qapi-types.py                              |  51 +++----
 scripts/qapi-visit.py                              |  58 ++++----
 scripts/qapi.py                                    |  25 +++-
 spice-qemu-char.c                                  |  12 +-
 tests/Makefile                                     |   4 +-
 tests/qapi-schema/args-name-has.err                |   1 +
 tests/qapi-schema/args-name-has.exit               |   1 +
 tests/qapi-schema/args-name-has.json               |   5 +
 ...truct-base-clash-base.err => args-name-has.out} |   0
 tests/qapi-schema/flat-union-clash-branch.err      |   1 +
 tests/qapi-schema/flat-union-clash-branch.exit     |   2 +-
 tests/qapi-schema/flat-union-clash-branch.json     |  18 ++-
 tests/qapi-schema/flat-union-clash-branch.out      |  14 --
 tests/qapi-schema/qapi-schema-test.json            |  12 ++
 tests/qapi-schema/qapi-schema-test.out             |  15 ++
 tests/qapi-schema/struct-base-clash-base.exit      |   1 -
 tests/qapi-schema/struct-base-clash-base.json      |   9 --
 tests/qapi-schema/struct-base-clash-base.out       |   5 -
 tests/qapi-schema/struct-member-u.err              |   1 +
 tests/qapi-schema/struct-member-u.exit             |   1 +
 tests/qapi-schema/struct-member-u.json             |   6 +
 tests/qapi-schema/struct-member-u.out              |   0
 tests/qapi-schema/struct-name-list.err             |   1 +
 tests/qapi-schema/struct-name-list.exit            |   1 +
 tests/qapi-schema/struct-name-list.json            |   5 +
 tests/qapi-schema/struct-name-list.out             |   0
 tests/qapi-schema/union-clash-type.err             |   2 +-
 tests/qapi-schema/union-clash-type.json            |   6 +-
 tests/test-qmp-commands.c                          |  19 +--
 tests/test-qmp-event.c                             |   8 +-
 tests/test-qmp-input-visitor.c                     |  82 +++++------
 tests/test-qmp-output-visitor.c                    |  54 ++++---
 tests/test-visitor-serialization.c                 |  14 +-
 tpm.c                                              |   4 +-
 ui/console.c                                       |  20 +--
 ui/input-keymap.c                                  |  20 +--
 ui/input-legacy.c                                  |  21 +--
 ui/input.c                                         |  84 +++++------
 ui/spice-core.c                                    |  23 +--
 ui/vnc.c                                           |  64 +++++----
 util/qemu-sockets.c                                |  42 +++---
 65 files changed, 596 insertions(+), 580 deletions(-)
 create mode 100644 tests/qapi-schema/args-name-has.err
 create mode 100644 tests/qapi-schema/args-name-has.exit
 create mode 100644 tests/qapi-schema/args-name-has.json
 rename tests/qapi-schema/{struct-base-clash-base.err => args-name-has.out} (100%)
 delete mode 100644 tests/qapi-schema/struct-base-clash-base.exit
 delete mode 100644 tests/qapi-schema/struct-base-clash-base.json
 delete mode 100644 tests/qapi-schema/struct-base-clash-base.out
 create mode 100644 tests/qapi-schema/struct-member-u.err
 create mode 100644 tests/qapi-schema/struct-member-u.exit
 create mode 100644 tests/qapi-schema/struct-member-u.json
 create mode 100644 tests/qapi-schema/struct-member-u.out
 create mode 100644 tests/qapi-schema/struct-name-list.err
 create mode 100644 tests/qapi-schema/struct-name-list.exit
 create mode 100644 tests/qapi-schema/struct-name-list.json
 create mode 100644 tests/qapi-schema/struct-name-list.out

-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 01/17] qapi: Add tests for reserved name abuse
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-19 16:05   ` Markus Armbruster
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 02/17] qapi: Reserve '*List' type names for arrays Eric Blake
                   ` (16 subsequent siblings)
  17 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

A future patch wants to change qapi union representation from
an anonymous C union over to a named C union 'u', so that the
C names of tag values are in a separate namespace and thus
cannot collide with the C names of non-variant QMP members.
But for that to not cause any problems with future extensions
to existing qapi, it would be best if we prohibit 'u' as a
member name everywhere, to reserve it for our internal use.
(Remember that although 'u' would only actually collide in
flat unions, we must also worry about the fact that it is
possible to convert from a struct to a flat union in a future
qemu version while remaining backwards-compatible in QMP.)

We are failing to detect a collision between a QMP member and
the implicit 'has_*' flag for another optional QMP member. The
easiest fix would be for a future patch to reserve the entire
"has[-_]" namespace for member names (also for branch names,
but only as long as branch names can collide with QMP names).

Our current representation of qapi arrays is done by appending
'List' to the element name; but we are not preventing the
creation of a non-array type with the same name.  It is also
possible to abuse the internal naming by writing such things
as ['intList'] for a 2-dimensional array of integers; however,
we may want to later add support for explicit 2D arrays such
as [['int']], so it is better to defer writing tests for what
we will permit and reject when it comes to multi-dimensioned
arrays until that later design is complete; for now, we are
only testing type collision.

On the other hand, there is no reason to forbid type name
patterns from member names, or member name patterns from types,
since the two are not in the same namespace in C and won't
collide.

Modify and add tests to cover these issues.

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

---
v9: new patch
---
 tests/Makefile                                 |  3 +++
 tests/qapi-schema/args-name-has.err            |  0
 tests/qapi-schema/args-name-has.exit           |  1 +
 tests/qapi-schema/args-name-has.json           |  6 ++++++
 tests/qapi-schema/args-name-has.out            |  6 ++++++
 tests/qapi-schema/flat-union-clash-branch.json | 17 +++++++----------
 tests/qapi-schema/flat-union-clash-branch.out  |  9 +++------
 tests/qapi-schema/qapi-schema-test.json        |  9 +++++++++
 tests/qapi-schema/qapi-schema-test.out         | 12 ++++++++++++
 tests/qapi-schema/struct-member-u.err          |  0
 tests/qapi-schema/struct-member-u.exit         |  1 +
 tests/qapi-schema/struct-member-u.json         |  6 ++++++
 tests/qapi-schema/struct-member-u.out          |  3 +++
 tests/qapi-schema/struct-name-list.err         |  0
 tests/qapi-schema/struct-name-list.exit        |  1 +
 tests/qapi-schema/struct-name-list.json        |  5 +++++
 tests/qapi-schema/struct-name-list.out         |  3 +++
 17 files changed, 66 insertions(+), 16 deletions(-)
 create mode 100644 tests/qapi-schema/args-name-has.err
 create mode 100644 tests/qapi-schema/args-name-has.exit
 create mode 100644 tests/qapi-schema/args-name-has.json
 create mode 100644 tests/qapi-schema/args-name-has.out
 create mode 100644 tests/qapi-schema/struct-member-u.err
 create mode 100644 tests/qapi-schema/struct-member-u.exit
 create mode 100644 tests/qapi-schema/struct-member-u.json
 create mode 100644 tests/qapi-schema/struct-member-u.out
 create mode 100644 tests/qapi-schema/struct-name-list.err
 create mode 100644 tests/qapi-schema/struct-name-list.exit
 create mode 100644 tests/qapi-schema/struct-name-list.json
 create mode 100644 tests/qapi-schema/struct-name-list.out

diff --git a/tests/Makefile b/tests/Makefile
index cb221de..ef2a19f 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -241,6 +241,7 @@ qapi-schema += args-invalid.json
 qapi-schema += args-member-array-bad.json
 qapi-schema += args-member-unknown.json
 qapi-schema += args-name-clash.json
+qapi-schema += args-name-has.json
 qapi-schema += args-union.json
 qapi-schema += args-unknown.json
 qapi-schema += bad-base.json
@@ -323,6 +324,8 @@ qapi-schema += struct-base-clash-deep.json
 qapi-schema += struct-base-clash.json
 qapi-schema += struct-data-invalid.json
 qapi-schema += struct-member-invalid.json
+qapi-schema += struct-member-u.json
+qapi-schema += struct-name-list.json
 qapi-schema += trailing-comma-list.json
 qapi-schema += trailing-comma-object.json
 qapi-schema += type-bypass-bad-gen.json
diff --git a/tests/qapi-schema/args-name-has.err b/tests/qapi-schema/args-name-has.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/args-name-has.exit b/tests/qapi-schema/args-name-has.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/args-name-has.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/args-name-has.json b/tests/qapi-schema/args-name-has.json
new file mode 100644
index 0000000..a2197de
--- /dev/null
+++ b/tests/qapi-schema/args-name-has.json
@@ -0,0 +1,6 @@
+# C member name collision
+# FIXME - This parses, but fails to compile, because the C struct is given
+# two 'has_a' members, one from the flag for optional 'a', and the other
+# from member 'has-a'.  Either reject this at parse time, or munge the C
+# names to avoid the collision.
+{ 'command': 'oops', 'data': { '*a': 'str', 'has-a': 'str' } }
diff --git a/tests/qapi-schema/args-name-has.out b/tests/qapi-schema/args-name-has.out
new file mode 100644
index 0000000..5a18b6b
--- /dev/null
+++ b/tests/qapi-schema/args-name-has.out
@@ -0,0 +1,6 @@
+object :empty
+object :obj-oops-arg
+    member a: str optional=True
+    member has-a: str optional=False
+command oops :obj-oops-arg -> None
+   gen=True success_response=True
diff --git a/tests/qapi-schema/flat-union-clash-branch.json b/tests/qapi-schema/flat-union-clash-branch.json
index e593336..c9f08e3 100644
--- a/tests/qapi-schema/flat-union-clash-branch.json
+++ b/tests/qapi-schema/flat-union-clash-branch.json
@@ -1,18 +1,15 @@
 # Flat union branch name collision
-# FIXME: this parses, but then fails to compile due to a duplicate 'c_d'
-# (one from the base member, the other from the branch name).  We should
-# either reject the collision at parse time, or munge the generated branch
-# name to allow this to compile.
+# FIXME: this parses, but then fails to compile due to a duplicate 'has_a'
+# (one as the implicit flag for the optional base member, the other from
+# the C member for the branch name).  We should either reject the collision
+# at parse time, or munge the generated branch name to allow this to compile.
 { 'enum': 'TestEnum',
-  'data': [ 'base', 'c-d' ] }
+  'data': [ 'has-a' ] }
 { 'struct': 'Base',
-  'data': { 'enum1': 'TestEnum', '*c_d': 'str' } }
+  'data': { 'enum1': 'TestEnum', '*a': 'str' } }
 { 'struct': 'Branch1',
   'data': { 'string': 'str' } }
-{ 'struct': 'Branch2',
-  'data': { 'value': 'int' } }
 { 'union': 'TestUnion',
   'base': 'Base',
   'discriminator': 'enum1',
-  'data': { 'base': 'Branch1',
-            'c-d': 'Branch2' } }
+  'data': { 'has-a': 'Branch1' } }
diff --git a/tests/qapi-schema/flat-union-clash-branch.out b/tests/qapi-schema/flat-union-clash-branch.out
index 8e0da73..1491081 100644
--- a/tests/qapi-schema/flat-union-clash-branch.out
+++ b/tests/qapi-schema/flat-union-clash-branch.out
@@ -1,14 +1,11 @@
 object :empty
 object Base
     member enum1: TestEnum optional=False
-    member c_d: str optional=True
+    member a: str optional=True
 object Branch1
     member string: str optional=False
-object Branch2
-    member value: int optional=False
-enum TestEnum ['base', 'c-d']
+enum TestEnum ['has-a']
 object TestUnion
     base Base
     tag enum1
-    case base: Branch1
-    case c-d: Branch2
+    case has-a: Branch1
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 4e2d7c2..c842e22 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -105,6 +105,15 @@
             'sizes': ['size'],
             'any': ['any'] } }

+# Even if 'u' is forbidden as a struct member name, it should still be
+# valid as a type or union branch name. And although '*Kind' is forbidden
+# as a type name, it should not be forbidden as a member or branch name.
+# TODO - '*List' should also be forbidden as a type name, and 'has_*' as
+# a member name.
+{ 'struct': 'has_a', 'data': { 'MyKind': 'int', 'MyList': ['int'] } }
+{ 'union': 'u', 'data': { 'u': 'uint8', 'myKind': 'has_a',
+                          'myList': 'has_a' } }
+
 # testing commands
 { 'command': 'user_def_cmd', 'data': {} }
 { 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index a6c80e0..30c3ff0 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -22,6 +22,8 @@ object :obj-guest-get-time-arg
     member b: int optional=True
 object :obj-guest-sync-arg
     member arg: any optional=False
+object :obj-has_a-wrapper
+    member data: has_a optional=False
 object :obj-int16List-wrapper
     member data: int16List optional=False
 object :obj-int32List-wrapper
@@ -46,6 +48,8 @@ object :obj-uint32List-wrapper
     member data: uint32List optional=False
 object :obj-uint64List-wrapper
     member data: uint64List optional=False
+object :obj-uint8-wrapper
+    member data: uint8 optional=False
 object :obj-uint8List-wrapper
     member data: uint8List optional=False
 object :obj-user_def_cmd1-arg
@@ -191,6 +195,14 @@ command guest-get-time :obj-guest-get-time-arg -> int
    gen=True success_response=True
 command guest-sync :obj-guest-sync-arg -> any
    gen=True success_response=True
+object has_a
+    member MyKind: int optional=False
+    member MyList: intList optional=False
+object u
+    case u: :obj-uint8-wrapper
+    case myKind: :obj-has_a-wrapper
+    case myList: :obj-has_a-wrapper
+enum uKind ['u', 'myKind', 'myList']
 command user_def_cmd None -> None
    gen=True success_response=True
 command user_def_cmd1 :obj-user_def_cmd1-arg -> None
diff --git a/tests/qapi-schema/struct-member-u.err b/tests/qapi-schema/struct-member-u.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/struct-member-u.exit b/tests/qapi-schema/struct-member-u.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/struct-member-u.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/struct-member-u.json b/tests/qapi-schema/struct-member-u.json
new file mode 100644
index 0000000..d72023d
--- /dev/null
+++ b/tests/qapi-schema/struct-member-u.json
@@ -0,0 +1,6 @@
+# Potential C member name collision
+# FIXME - This parses and compiles, but would cause a collision if this
+# struct is later reworked to be part of a flat union, once unions hide
+# their tag values under a C union named 'u'. We should reject the use
+# of this identifier to reserve it for internal use.
+{ 'struct': 'Oops', 'data': { 'u': 'str' } }
diff --git a/tests/qapi-schema/struct-member-u.out b/tests/qapi-schema/struct-member-u.out
new file mode 100644
index 0000000..aa53e7f
--- /dev/null
+++ b/tests/qapi-schema/struct-member-u.out
@@ -0,0 +1,3 @@
+object :empty
+object Oops
+    member u: str optional=False
diff --git a/tests/qapi-schema/struct-name-list.err b/tests/qapi-schema/struct-name-list.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/struct-name-list.exit b/tests/qapi-schema/struct-name-list.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/struct-name-list.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/struct-name-list.json b/tests/qapi-schema/struct-name-list.json
new file mode 100644
index 0000000..8ad38e6
--- /dev/null
+++ b/tests/qapi-schema/struct-name-list.json
@@ -0,0 +1,5 @@
+# Potential C name collision
+# FIXME - This parses and compiles on its own, but prevents the user from
+# creating a type named 'Foo' and using ['Foo'] for an array.  We should
+# reject the use of any non-array type names ending in 'List'.
+{ 'struct': 'FooList', 'data': { 's': 'str' } }
diff --git a/tests/qapi-schema/struct-name-list.out b/tests/qapi-schema/struct-name-list.out
new file mode 100644
index 0000000..0406bfe
--- /dev/null
+++ b/tests/qapi-schema/struct-name-list.out
@@ -0,0 +1,3 @@
+object :empty
+object FooList
+    member s: str optional=False
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 02/17] qapi: Reserve '*List' type names for arrays
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 01/17] qapi: Add tests for reserved name abuse Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-19 16:14   ` Markus Armbruster
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 03/17] qapi: Reserve 'u' and 'has[-_]*' member names Eric Blake
                   ` (15 subsequent siblings)
  17 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

We already reserved '*Kind' for implicit enums (since commit
4dc2e69), but failed to reserve '*List' for array types.  Since
no QMP was yet using it, we might as well make the reservation
official.

Note that this forbids creation of new types or commands ending
in 'List', but does NOT forbid abuse of attempts like ['intList']
for creating a 2-dimensional array of 'int'.  The whole idea of
multi-dimensional arrays needs more design thought, best left for
another day.

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

---
v9: new patch
---
 docs/qapi-code-gen.txt                  |  3 ++-
 scripts/qapi.py                         | 11 +++++------
 tests/qapi-schema/qapi-schema-test.json |  8 ++++----
 tests/qapi-schema/struct-name-list.err  |  1 +
 tests/qapi-schema/struct-name-list.exit |  2 +-
 tests/qapi-schema/struct-name-list.json |  6 +++---
 tests/qapi-schema/struct-name-list.out  |  3 ---
 7 files changed, 16 insertions(+), 18 deletions(-)

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 2afab20..c4264a8 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -106,7 +106,8 @@ Types, commands, and events share a common namespace.  Therefore,
 generally speaking, type definitions should always use CamelCase for
 user-defined type names, while built-in types are lowercase. Type
 definitions should not end in 'Kind', as this namespace is used for
-creating implicit C enums for visiting union types.  Command names,
+creating implicit C enums for visiting union types, or in 'List', as
+this namespace is used for creating array types.  Command names,
 and field names within a type, should be all lower case with words
 separated by a hyphen.  However, some existing older commands and
 complex types use underscore; when extending such expressions,
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 9d53255..09ba44b 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -390,10 +390,10 @@ def add_name(name, info, meta, implicit=False):
         raise QAPIExprError(info,
                             "%s '%s' is already defined"
                             % (all_names[name], name))
-    if not implicit and name[-4:] == 'Kind':
+    if not implicit and (name[-4:] == 'Kind' or name[-4:] == 'List'):
         raise QAPIExprError(info,
-                            "%s '%s' should not end in 'Kind'"
-                            % (meta, name))
+                            "%s '%s' should not end in '%s'"
+                            % (meta, name, name[-4:]))
     all_names[name] = meta


@@ -1196,9 +1196,8 @@ class QAPISchema(object):
         return name

     def _make_array_type(self, element_type, info):
-        # TODO fooList namespace is not reserved; user can create collisions,
-        # or abuse our type system with ['fooList'] for 2D array
-        name = element_type + 'List'
+        # TODO Should we worry about users abusing ['intList'] for 2-D array?
+        name = element_type + 'List'   # Use namespace reserved by add_name()
         if not self.lookup_type(name):
             self._def_entity(QAPISchemaArrayType(name, info, element_type))
         return name
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index c842e22..81d19d0 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -106,10 +106,10 @@
             'any': ['any'] } }

 # Even if 'u' is forbidden as a struct member name, it should still be
-# valid as a type or union branch name. And although '*Kind' is forbidden
-# as a type name, it should not be forbidden as a member or branch name.
-# TODO - '*List' should also be forbidden as a type name, and 'has_*' as
-# a member name.
+# valid as a type or union branch name. And although '*Kind' and '*List'
+# are forbidden as type names, they should not be forbidden as a member
+# or branch name.
+# TODO - 'has_*' should also be forbidden as a member name.
 { 'struct': 'has_a', 'data': { 'MyKind': 'int', 'MyList': ['int'] } }
 { 'union': 'u', 'data': { 'u': 'uint8', 'myKind': 'has_a',
                           'myList': 'has_a' } }
diff --git a/tests/qapi-schema/struct-name-list.err b/tests/qapi-schema/struct-name-list.err
index e69de29..ee5b09b 100644
--- a/tests/qapi-schema/struct-name-list.err
+++ b/tests/qapi-schema/struct-name-list.err
@@ -0,0 +1 @@
+tests/qapi-schema/struct-name-list.json:5: struct 'FooList' should not end in 'List'
diff --git a/tests/qapi-schema/struct-name-list.exit b/tests/qapi-schema/struct-name-list.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/struct-name-list.exit
+++ b/tests/qapi-schema/struct-name-list.exit
@@ -1 +1 @@
-0
+1
diff --git a/tests/qapi-schema/struct-name-list.json b/tests/qapi-schema/struct-name-list.json
index 8ad38e6..98d53bf 100644
--- a/tests/qapi-schema/struct-name-list.json
+++ b/tests/qapi-schema/struct-name-list.json
@@ -1,5 +1,5 @@
 # Potential C name collision
-# FIXME - This parses and compiles on its own, but prevents the user from
-# creating a type named 'Foo' and using ['Foo'] for an array.  We should
-# reject the use of any non-array type names ending in 'List'.
+# We reserve names ending in 'List' for use by array types.
+# TODO - we could choose array names to avoid collision with user types,
+# in order to let this compile
 { 'struct': 'FooList', 'data': { 's': 'str' } }
diff --git a/tests/qapi-schema/struct-name-list.out b/tests/qapi-schema/struct-name-list.out
index 0406bfe..e69de29 100644
--- a/tests/qapi-schema/struct-name-list.out
+++ b/tests/qapi-schema/struct-name-list.out
@@ -1,3 +0,0 @@
-object :empty
-object FooList
-    member s: str optional=False
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 03/17] qapi: Reserve 'u' and 'has[-_]*' member names
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 01/17] qapi: Add tests for reserved name abuse Eric Blake
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 02/17] qapi: Reserve '*List' type names for arrays Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-19 17:19   ` Markus Armbruster
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 04/17] vnc: hoist allocation of VncBasicInfo to callers Eric Blake
                   ` (14 subsequent siblings)
  17 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

To make collision detection between member names easier, we
might as well reject all attempts to use anything that would
collide with our use of 'has_' as a flag for optional members.
Also, a later patch will introduce a named union 'u' for
holding the branch names of a qapi union in a separate
namespace from non-variant QMP members, at which point it
would collide with a 'u' member.  Fortunately, none of our
qapi files were using these names, so we can reserve them now.

Note that we cannot forbid 'u' everywhere (by adding the
rejection code to check_name()), because the existing QKeyCode
enum already uses it, and because qapi-schema-test.json
intentionally uses it as a branch name (we could feasibly have
a union with one branch per QKeyCode).  Also, forbidding 'has_'
in branch names will eventually disappear, once branch names
cannot collide with other names.  So instead, this code does
separate checks for check_type() (when iterating over a dict,
common to struct, command args, and event data) and for
unions.  We do NOT need to reserve the names for alternates,
because alternates do not have any QMP members alongside its
branch names.

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

---
v9: new patch
---
 docs/qapi-code-gen.txt                         |  5 ++++-
 scripts/qapi.py                                | 12 ++++++++++++
 tests/qapi-schema/args-name-has.err            |  1 +
 tests/qapi-schema/args-name-has.exit           |  2 +-
 tests/qapi-schema/args-name-has.json           |  7 +++----
 tests/qapi-schema/args-name-has.out            |  6 ------
 tests/qapi-schema/flat-union-clash-branch.err  |  1 +
 tests/qapi-schema/flat-union-clash-branch.exit |  2 +-
 tests/qapi-schema/flat-union-clash-branch.json |  7 ++++---
 tests/qapi-schema/flat-union-clash-branch.out  | 11 -----------
 tests/qapi-schema/qapi-schema-test.json        |  9 ++++-----
 tests/qapi-schema/struct-member-u.err          |  1 +
 tests/qapi-schema/struct-member-u.exit         |  2 +-
 tests/qapi-schema/struct-member-u.json         |  8 ++++----
 tests/qapi-schema/struct-member-u.out          |  3 ---
 15 files changed, 37 insertions(+), 40 deletions(-)

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index c4264a8..df2d198 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -112,7 +112,10 @@ and field names within a type, should be all lower case with words
 separated by a hyphen.  However, some existing older commands and
 complex types use underscore; when extending such expressions,
 consistency is preferred over blindly avoiding underscore.  Event
-names should be ALL_CAPS with words separated by underscore.
+names should be ALL_CAPS with words separated by underscore.  Field
+names cannot be 'u', as this is reserved for generated code for
+unions, nor can they start with 'has-' or 'has_', as this is
+reserved for tracking optional fields.

 Any name (command, event, type, field, or enum value) beginning with
 "x-" is marked experimental, and may be withdrawn or changed
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 09ba44b..1e7e08b 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -488,6 +488,10 @@ def check_type(expr_info, source, value, allow_array=False,
     for (key, arg) in value.items():
         check_name(expr_info, "Member of %s" % source, key,
                    allow_optional=allow_optional)
+        if key == 'u' or key.startswith('has-') or key.startswith('has_'):
+            raise QAPIExprError(expr_info,
+                                "Member of %s uses reserved name '%s'"
+                                % (source, key))
         # Todo: allow dictionaries to represent default values of
         # an optional argument.
         check_type(expr_info, "Member '%s' of %s" % (key, source), arg,
@@ -588,6 +592,14 @@ def check_union(expr, expr_info):
     # Check every branch
     for (key, value) in members.items():
         check_name(expr_info, "Member of union '%s'" % name, key)
+        # TODO: As long as branch names can collide with QMP names, we
+        # must prevent branches starting with 'has_'. However, we do not
+        # need to reject 'u', because that is reserved for when we start
+        # sticking branch names in a C union named 'u'.
+        if key.startswith('has-') or key.startswith('has_'):
+            raise QAPIExprError(expr_info,
+                                "Branch of union '%s' uses reserved name '%s'"
+                                % (name, key))

         # Each value must name a known type; furthermore, in flat unions,
         # branches must be a struct with no overlapping member names
diff --git a/tests/qapi-schema/args-name-has.err b/tests/qapi-schema/args-name-has.err
index e69de29..cb57552 100644
--- a/tests/qapi-schema/args-name-has.err
+++ b/tests/qapi-schema/args-name-has.err
@@ -0,0 +1 @@
+tests/qapi-schema/args-name-has.json:5: Member of 'data' for command 'oops' uses reserved name 'has-a'
diff --git a/tests/qapi-schema/args-name-has.exit b/tests/qapi-schema/args-name-has.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/args-name-has.exit
+++ b/tests/qapi-schema/args-name-has.exit
@@ -1 +1 @@
-0
+1
diff --git a/tests/qapi-schema/args-name-has.json b/tests/qapi-schema/args-name-has.json
index a2197de..4297549 100644
--- a/tests/qapi-schema/args-name-has.json
+++ b/tests/qapi-schema/args-name-has.json
@@ -1,6 +1,5 @@
 # C member name collision
-# FIXME - This parses, but fails to compile, because the C struct is given
-# two 'has_a' members, one from the flag for optional 'a', and the other
-# from member 'has-a'.  Either reject this at parse time, or munge the C
-# names to avoid the collision.
+# This would attempt to create two 'has_a' members of the C struct, one
+# from the flag for optional 'a', and the other from member 'has-a'.
+# TODO we could munge the optional flag name to avoid the collision.
 { 'command': 'oops', 'data': { '*a': 'str', 'has-a': 'str' } }
diff --git a/tests/qapi-schema/args-name-has.out b/tests/qapi-schema/args-name-has.out
index 5a18b6b..e69de29 100644
--- a/tests/qapi-schema/args-name-has.out
+++ b/tests/qapi-schema/args-name-has.out
@@ -1,6 +0,0 @@
-object :empty
-object :obj-oops-arg
-    member a: str optional=True
-    member has-a: str optional=False
-command oops :obj-oops-arg -> None
-   gen=True success_response=True
diff --git a/tests/qapi-schema/flat-union-clash-branch.err b/tests/qapi-schema/flat-union-clash-branch.err
index e69de29..e6b6294 100644
--- a/tests/qapi-schema/flat-union-clash-branch.err
+++ b/tests/qapi-schema/flat-union-clash-branch.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-clash-branch.json:13: Branch of union 'TestUnion' uses reserved name 'has-a'
diff --git a/tests/qapi-schema/flat-union-clash-branch.exit b/tests/qapi-schema/flat-union-clash-branch.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/flat-union-clash-branch.exit
+++ b/tests/qapi-schema/flat-union-clash-branch.exit
@@ -1 +1 @@
-0
+1
diff --git a/tests/qapi-schema/flat-union-clash-branch.json b/tests/qapi-schema/flat-union-clash-branch.json
index c9f08e3..8efbcfd 100644
--- a/tests/qapi-schema/flat-union-clash-branch.json
+++ b/tests/qapi-schema/flat-union-clash-branch.json
@@ -1,8 +1,9 @@
 # Flat union branch name collision
-# FIXME: this parses, but then fails to compile due to a duplicate 'has_a'
+# This is rejected because the C struct would have duplicate 'has_a'
 # (one as the implicit flag for the optional base member, the other from
-# the C member for the branch name).  We should either reject the collision
-# at parse time, or munge the generated branch name to allow this to compile.
+# the C member for the branch name).
+# TODO: we should munge generated branch names to not collide with the
+# non-variant struct members.
 { 'enum': 'TestEnum',
   'data': [ 'has-a' ] }
 { 'struct': 'Base',
diff --git a/tests/qapi-schema/flat-union-clash-branch.out b/tests/qapi-schema/flat-union-clash-branch.out
index 1491081..e69de29 100644
--- a/tests/qapi-schema/flat-union-clash-branch.out
+++ b/tests/qapi-schema/flat-union-clash-branch.out
@@ -1,11 +0,0 @@
-object :empty
-object Base
-    member enum1: TestEnum optional=False
-    member a: str optional=True
-object Branch1
-    member string: str optional=False
-enum TestEnum ['has-a']
-object TestUnion
-    base Base
-    tag enum1
-    case has-a: Branch1
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 81d19d0..1ca7e4f 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -105,11 +105,10 @@
             'sizes': ['size'],
             'any': ['any'] } }

-# Even if 'u' is forbidden as a struct member name, it should still be
-# valid as a type or union branch name. And although '*Kind' and '*List'
-# are forbidden as type names, they should not be forbidden as a member
-# or branch name.
-# TODO - 'has_*' should also be forbidden as a member name.
+# Even though 'u' and 'has_*' are forbidden as struct member names, they
+# should still be valid as a type or union branch name. And although
+# '*Kind' and '*List' are forbidden as type names, they should not be
+# forbidden as a member or branch name.
 { 'struct': 'has_a', 'data': { 'MyKind': 'int', 'MyList': ['int'] } }
 { 'union': 'u', 'data': { 'u': 'uint8', 'myKind': 'has_a',
                           'myList': 'has_a' } }
diff --git a/tests/qapi-schema/struct-member-u.err b/tests/qapi-schema/struct-member-u.err
index e69de29..5610310 100644
--- a/tests/qapi-schema/struct-member-u.err
+++ b/tests/qapi-schema/struct-member-u.err
@@ -0,0 +1 @@
+tests/qapi-schema/struct-member-u.json:6: Member of 'data' for struct 'Oops' uses reserved name 'u'
diff --git a/tests/qapi-schema/struct-member-u.exit b/tests/qapi-schema/struct-member-u.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/struct-member-u.exit
+++ b/tests/qapi-schema/struct-member-u.exit
@@ -1 +1 @@
-0
+1
diff --git a/tests/qapi-schema/struct-member-u.json b/tests/qapi-schema/struct-member-u.json
index d72023d..a0e05b5 100644
--- a/tests/qapi-schema/struct-member-u.json
+++ b/tests/qapi-schema/struct-member-u.json
@@ -1,6 +1,6 @@
 # Potential C member name collision
-# FIXME - This parses and compiles, but would cause a collision if this
-# struct is later reworked to be part of a flat union, once unions hide
-# their tag values under a C union named 'u'. We should reject the use
-# of this identifier to reserve it for internal use.
+# We reject use of 'u' as a member name, to allow it for internal use in
+# putting union branch members in a separate namespace from QMP members.
+# This is true even for non-unions, because it is possible to convert a
+# struct to flat union while remaining backwards compatible in QMP.
 { 'struct': 'Oops', 'data': { 'u': 'str' } }
diff --git a/tests/qapi-schema/struct-member-u.out b/tests/qapi-schema/struct-member-u.out
index aa53e7f..e69de29 100644
--- a/tests/qapi-schema/struct-member-u.out
+++ b/tests/qapi-schema/struct-member-u.out
@@ -1,3 +0,0 @@
-object :empty
-object Oops
-    member u: str optional=False
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 04/17] vnc: hoist allocation of VncBasicInfo to callers
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
                   ` (2 preceding siblings ...)
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 03/17] qapi: Reserve 'u' and 'has[-_]*' member names Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-20  7:38   ` Markus Armbruster
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 05/17] qapi: Unbox base members Eric Blake
                   ` (13 subsequent siblings)
  17 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Gerd Hoffmann

A future qapi patch will rework generated structs with a base
class to be unboxed.  In preparation for that, change the code
that allocates then populates an info struct to instead merely
populate the fields of an info field passed in as a parameter.
Add rudimentary Error handling for cases where the old code
returned NULL; but as before, callers merely ignore errors for
now.

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

---
v9: (no v6-8): hoist from v5 33/46
---
 ui/vnc.c | 52 ++++++++++++++++++++++++++++------------------------
 1 file changed, 28 insertions(+), 24 deletions(-)

diff --git a/ui/vnc.c b/ui/vnc.c
index d73966a..61af4ba 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -156,10 +156,11 @@ char *vnc_socket_remote_addr(const char *format, int fd) {
     return addr_to_string(format, &sa, salen);
 }

-static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa,
-                                        socklen_t salen)
+static void vnc_basic_info_get(struct sockaddr_storage *sa,
+                               socklen_t salen,
+                               VncBasicInfo *info,
+                               Error **errp)
 {
-    VncBasicInfo *info;
     char host[NI_MAXHOST];
     char serv[NI_MAXSERV];
     int err;
@@ -168,42 +169,44 @@ static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa,
                            host, sizeof(host),
                            serv, sizeof(serv),
                            NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
-        VNC_DEBUG("Cannot resolve address %d: %s\n",
-                  err, gai_strerror(err));
-        return NULL;
+        error_setg(errp, "Cannot resolve address %d: %s",
+                   err, gai_strerror(err));
+        return;
     }

-    info = g_malloc0(sizeof(VncBasicInfo));
     info->host = g_strdup(host);
     info->service = g_strdup(serv);
     info->family = inet_netfamily(sa->ss_family);
-    return info;
 }

-static VncBasicInfo *vnc_basic_info_get_from_server_addr(int fd)
+static void vnc_basic_info_get_from_server_addr(int fd, VncBasicInfo *info,
+                                                Error **errp)
 {
     struct sockaddr_storage sa;
     socklen_t salen;

     salen = sizeof(sa);
     if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) {
-        return NULL;
+        error_setg_errno(errp, errno, "getsockname failed");
+        return;
     }

-    return vnc_basic_info_get(&sa, salen);
+    vnc_basic_info_get(&sa, salen, info, errp);
 }

-static VncBasicInfo *vnc_basic_info_get_from_remote_addr(int fd)
+static void vnc_basic_info_get_from_remote_addr(int fd, VncBasicInfo *info,
+                                                Error **errp)
 {
     struct sockaddr_storage sa;
     socklen_t salen;

     salen = sizeof(sa);
     if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) {
-        return NULL;
+        error_setg_errno(errp, errno, "getpeername failed");
+        return;
     }

-    return vnc_basic_info_get(&sa, salen);
+    vnc_basic_info_get(&sa, salen, info, errp);
 }

 static const char *vnc_auth_name(VncDisplay *vd) {
@@ -256,13 +259,10 @@ static const char *vnc_auth_name(VncDisplay *vd) {
 static VncServerInfo *vnc_server_info_get(VncDisplay *vd)
 {
     VncServerInfo *info;
-    VncBasicInfo *bi = vnc_basic_info_get_from_server_addr(vd->lsock);
-    if (!bi) {
-        return NULL;
-    }

     info = g_malloc(sizeof(*info));
-    info->base = bi;
+    info->base = g_malloc(sizeof(*info->base));
+    vnc_basic_info_get_from_server_addr(vd->lsock, info->base, NULL);
     info->has_auth = true;
     info->auth = g_strdup(vnc_auth_name(vd));
     return info;
@@ -291,11 +291,15 @@ static void vnc_client_cache_auth(VncState *client)

 static void vnc_client_cache_addr(VncState *client)
 {
-    VncBasicInfo *bi = vnc_basic_info_get_from_remote_addr(client->csock);
-
-    if (bi) {
-        client->info = g_malloc0(sizeof(*client->info));
-        client->info->base = bi;
+    Error *err = NULL;
+    client->info = g_malloc0(sizeof(*client->info));
+    client->info->base = g_malloc0(sizeof(*client->info->base));
+    vnc_basic_info_get_from_remote_addr(client->csock, client->info->base,
+                                        &err);
+    if (err) {
+        qapi_free_VncClientInfo(client->info);
+        client->info = NULL;
+        error_free(err);
     }
 }

-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 05/17] qapi: Unbox base members
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
                   ` (3 preceding siblings ...)
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 04/17] vnc: hoist allocation of VncBasicInfo to callers Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-16 19:12   ` [Qemu-devel] [PATCH v9 05.5/17] fixup to " Eric Blake
  2015-10-20 12:09   ` [Qemu-devel] [PATCH v9 05/17] " Markus Armbruster
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 06/17] qapi-visit: Remove redundant functions for flat union base Eric Blake
                   ` (12 subsequent siblings)
  17 siblings, 2 replies; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Michael Roth, Gerd Hoffmann, armbru, Luiz Capitulino

Rather than storing a base class as a pointer to a box, just
store the fields of that base class in the same order, so that
a child struct can be safely cast to its parent.  This gives
less malloc overhead, less pointer dereferencing, and even less
generated code.

Without boxing, the corner case of one empty struct having
another empty struct as its base type now requires inserting a
dummy member (previously, the pointer to the base would have
sufficed).

And now that we no longer consume a 'base' member in the generated
C struct, we can delete the former negative struct-base-clash-base
test and replace it with a positive test in qapi-schema-test that
ensures we don't reintroduce the naming collision.

Compare to the earlier commit 1e6c1616a "qapi: Generate a nicer
struct for flat unions".

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

---
v9: (no v6-8): hoist from v5 34/46, rebase to master
---
 hmp.c                                         |  6 +++---
 scripts/qapi-types.py                         | 29 ++++++++++++++-------------
 scripts/qapi-visit.py                         |  9 ++++++---
 tests/Makefile                                |  1 -
 tests/qapi-schema/qapi-schema-test.json       |  4 ++++
 tests/qapi-schema/qapi-schema-test.out        |  3 +++
 tests/qapi-schema/struct-base-clash-base.err  |  0
 tests/qapi-schema/struct-base-clash-base.exit |  1 -
 tests/qapi-schema/struct-base-clash-base.json |  9 ---------
 tests/qapi-schema/struct-base-clash-base.out  |  5 -----
 tests/test-qmp-commands.c                     | 15 +++++---------
 tests/test-qmp-event.c                        |  8 ++------
 tests/test-qmp-input-visitor.c                |  4 ++--
 tests/test-qmp-output-visitor.c               | 12 ++++-------
 tests/test-visitor-serialization.c            | 14 ++++++-------
 ui/spice-core.c                               | 23 ++++++++++++---------
 ui/vnc.c                                      | 20 +++++++++---------
 17 files changed, 72 insertions(+), 91 deletions(-)
 delete mode 100644 tests/qapi-schema/struct-base-clash-base.err
 delete mode 100644 tests/qapi-schema/struct-base-clash-base.exit
 delete mode 100644 tests/qapi-schema/struct-base-clash-base.json
 delete mode 100644 tests/qapi-schema/struct-base-clash-base.out

diff --git a/hmp.c b/hmp.c
index 5048eee..88fd804 100644
--- a/hmp.c
+++ b/hmp.c
@@ -569,8 +569,8 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict)
         for (client = info->clients; client; client = client->next) {
             monitor_printf(mon, "Client:\n");
             monitor_printf(mon, "     address: %s:%s\n",
-                           client->value->base->host,
-                           client->value->base->service);
+                           client->value->host,
+                           client->value->service);
             monitor_printf(mon, "  x509_dname: %s\n",
                            client->value->x509_dname ?
                            client->value->x509_dname : "none");
@@ -638,7 +638,7 @@ void hmp_info_spice(Monitor *mon, const QDict *qdict)
         for (chan = info->channels; chan; chan = chan->next) {
             monitor_printf(mon, "Channel:\n");
             monitor_printf(mon, "     address: %s:%s%s\n",
-                           chan->value->base->host, chan->value->base->port,
+                           chan->value->host, chan->value->port,
                            chan->value->tls ? " [tls]" : "");
             monitor_printf(mon, "     session: %" PRId64 "\n",
                            chan->value->connection_id);
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 4fe618e..bcef39d 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -51,8 +51,19 @@ def gen_struct_field(name, typ, optional):
     return ret


-def gen_struct_fields(members):
+def gen_struct_fields(members, base, nested=False):
     ret = ''
+    if base:
+        if not nested:
+            ret += mcgen('''
+    /* Members inherited from %(c_name)s: */
+''',
+                         c_name=base.c_name())
+        ret += gen_struct_fields(base.local_members, base.base, True)
+        if not nested:
+            ret += mcgen('''
+    /* Own members: */
+''')

     for memb in members:
         ret += gen_struct_field(memb.name, memb.type, memb.optional)
@@ -66,16 +77,13 @@ struct %(c_name)s {
 ''',
                 c_name=c_name(name))

-    if base:
-        ret += gen_struct_field('base', base, False)
-
-    ret += gen_struct_fields(members)
+    ret += gen_struct_fields(members, base)

     # Make sure that all structs have at least one field; 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 not members:
+    if not (base and base.members) and not members:
         ret += mcgen('''
     char qapi_dummy_field_for_empty_struct;
 ''')
@@ -126,14 +134,7 @@ struct %(c_name)s {
 ''',
                 c_name=c_name(name))
     if base:
-        ret += mcgen('''
-    /* Members inherited from %(c_name)s: */
-''',
-                     c_name=c_name(base.name))
-        ret += gen_struct_fields(base.members)
-        ret += mcgen('''
-    /* Own members: */
-''')
+        ret += gen_struct_fields([], base)
     else:
         ret += mcgen('''
     %(c_type)s kind;
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index d0759d7..8aae8da 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -62,12 +62,15 @@ static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error *


 def gen_visit_struct_fields(name, base, members):
+    if name in struct_fields_seen:
+        return ''
     struct_fields_seen.add(name)

     ret = ''

     if base:
-        ret += gen_visit_implicit_struct(base)
+        ret += gen_visit_struct_fields(base.name, base.base,
+                                       base.local_members)

     ret += mcgen('''

@@ -80,9 +83,9 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e

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

     ret += gen_visit_fields(members, prefix='(*obj)->')
diff --git a/tests/Makefile b/tests/Makefile
index ef2a19f..b3516ad 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -319,7 +319,6 @@ qapi-schema += returns-array-bad.json
 qapi-schema += returns-dict.json
 qapi-schema += returns-unknown.json
 qapi-schema += returns-whitelist.json
-qapi-schema += struct-base-clash-base.json
 qapi-schema += struct-base-clash-deep.json
 qapi-schema += struct-base-clash.json
 qapi-schema += struct-data-invalid.json
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 1ca7e4f..22e15eb 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -40,6 +40,10 @@
   'data': { 'string0': 'str',
             'dict1': 'UserDefTwoDict' } }

+# ensure that we don't have an artificial collision on 'base'
+{ 'struct': 'UserDefThree',
+  'base': 'UserDefOne', 'data': { 'base': 'str' } }
+
 # dummy struct to force generation of array types not otherwise mentioned
 { 'struct': 'ForceArrays',
   'data': { 'unused1':['UserDefOne'], 'unused2':['UserDefTwo'] } }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 30c3ff0..feaf20d 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -153,6 +153,9 @@ object UserDefOptions
     member u16: uint16List optional=True
     member i64x: int optional=True
     member u64x: uint64 optional=True
+object UserDefThree
+    base UserDefOne
+    member base: str optional=False
 object UserDefTwo
     member string0: str optional=False
     member dict1: UserDefTwoDict optional=False
diff --git a/tests/qapi-schema/struct-base-clash-base.err b/tests/qapi-schema/struct-base-clash-base.err
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/qapi-schema/struct-base-clash-base.exit b/tests/qapi-schema/struct-base-clash-base.exit
deleted file mode 100644
index 573541a..0000000
--- a/tests/qapi-schema/struct-base-clash-base.exit
+++ /dev/null
@@ -1 +0,0 @@
-0
diff --git a/tests/qapi-schema/struct-base-clash-base.json b/tests/qapi-schema/struct-base-clash-base.json
deleted file mode 100644
index 0c84025..0000000
--- a/tests/qapi-schema/struct-base-clash-base.json
+++ /dev/null
@@ -1,9 +0,0 @@
-# Struct member 'base'
-# FIXME: this parses, but then fails to compile due to a duplicate 'base'
-# (one explicit in QMP, the other used to box the base class members).
-# We should either reject the collision at parse time, or change the
-# generated struct to allow this to compile.
-{ 'struct': 'Base', 'data': {} }
-{ 'struct': 'Sub',
-  'base': 'Base',
-  'data': { 'base': 'str' } }
diff --git a/tests/qapi-schema/struct-base-clash-base.out b/tests/qapi-schema/struct-base-clash-base.out
deleted file mode 100644
index e69a416..0000000
--- a/tests/qapi-schema/struct-base-clash-base.out
+++ /dev/null
@@ -1,5 +0,0 @@
-object :empty
-object Base
-object Sub
-    base Base
-    member base: str optional=False
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index bc59835..ea700d8 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -25,11 +25,9 @@ UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a,
     UserDefOne *ud1d = g_malloc0(sizeof(UserDefOne));

     ud1c->string = strdup(ud1a->string);
-    ud1c->base = g_new0(UserDefZero, 1);
-    ud1c->base->integer = ud1a->base->integer;
+    ud1c->integer = ud1a->integer;
     ud1d->string = strdup(has_udb1 ? ud1b->string : "blah0");
-    ud1d->base = g_new0(UserDefZero, 1);
-    ud1d->base->integer = has_udb1 ? ud1b->base->integer : 0;
+    ud1d->integer = has_udb1 ? ud1b->integer : 0;

     ret = g_new0(UserDefTwo, 1);
     ret->string0 = strdup("blah1");
@@ -176,20 +174,17 @@ static void test_dealloc_types(void)
     UserDefOneList *ud1list;

     ud1test = g_malloc0(sizeof(UserDefOne));
-    ud1test->base = g_new0(UserDefZero, 1);
-    ud1test->base->integer = 42;
+    ud1test->integer = 42;
     ud1test->string = g_strdup("hi there 42");

     qapi_free_UserDefOne(ud1test);

     ud1a = g_malloc0(sizeof(UserDefOne));
-    ud1a->base = g_new0(UserDefZero, 1);
-    ud1a->base->integer = 43;
+    ud1a->integer = 43;
     ud1a->string = g_strdup("hi there 43");

     ud1b = g_malloc0(sizeof(UserDefOne));
-    ud1b->base = g_new0(UserDefZero, 1);
-    ud1b->base->integer = 44;
+    ud1b->integer = 44;
     ud1b->string = g_strdup("hi there 44");

     ud1list = g_malloc0(sizeof(UserDefOneList));
diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c
index 28f146d..035c65c 100644
--- a/tests/test-qmp-event.c
+++ b/tests/test-qmp-event.c
@@ -179,9 +179,7 @@ static void test_event_c(TestEventData *data,
     QDict *d, *d_data, *d_b;

     UserDefOne b;
-    UserDefZero z;
-    z.integer = 2;
-    b.base = &z;
+    b.integer = 2;
     b.string = g_strdup("test1");
     b.has_enum1 = false;

@@ -209,11 +207,9 @@ static void test_event_d(TestEventData *data,
 {
     UserDefOne struct1;
     EventStructOne a;
-    UserDefZero z;
     QDict *d, *d_data, *d_a, *d_struct1;

-    z.integer = 2;
-    struct1.base = &z;
+    struct1.integer = 2;
     struct1.string = g_strdup("test1");
     struct1.has_enum1 = true;
     struct1.enum1 = ENUM_ONE_VALUE1;
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index 8941963..514cfd0 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -262,7 +262,7 @@ static void test_visitor_in_struct_nested(TestInputVisitorData *data,

     check_and_free_str(udp->string0, "string0");
     check_and_free_str(udp->dict1->string1, "string1");
-    g_assert_cmpint(udp->dict1->dict2->userdef->base->integer, ==, 42);
+    g_assert_cmpint(udp->dict1->dict2->userdef->integer, ==, 42);
     check_and_free_str(udp->dict1->dict2->userdef->string, "string");
     check_and_free_str(udp->dict1->dict2->string, "string2");
     g_assert(udp->dict1->has_dict3 == false);
@@ -292,7 +292,7 @@ static void test_visitor_in_list(TestInputVisitorData *data,

         snprintf(string, sizeof(string), "string%d", i);
         g_assert_cmpstr(item->value->string, ==, string);
-        g_assert_cmpint(item->value->base->integer, ==, 42 + i);
+        g_assert_cmpint(item->value->integer, ==, 42 + i);
     }

     qapi_free_UserDefOneList(head);
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index c84002e..88e01ea 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -250,16 +250,14 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
     ud2->dict1->dict2 = g_malloc0(sizeof(*ud2->dict1->dict2));
     ud2->dict1->dict2->userdef = g_new0(UserDefOne, 1);
     ud2->dict1->dict2->userdef->string = g_strdup(string);
-    ud2->dict1->dict2->userdef->base = g_new0(UserDefZero, 1);
-    ud2->dict1->dict2->userdef->base->integer = value;
+    ud2->dict1->dict2->userdef->integer = value;
     ud2->dict1->dict2->string = g_strdup(strings[2]);

     ud2->dict1->dict3 = g_malloc0(sizeof(*ud2->dict1->dict3));
     ud2->dict1->has_dict3 = true;
     ud2->dict1->dict3->userdef = g_new0(UserDefOne, 1);
     ud2->dict1->dict3->userdef->string = g_strdup(string);
-    ud2->dict1->dict3->userdef->base = g_new0(UserDefZero, 1);
-    ud2->dict1->dict3->userdef->base->integer = value;
+    ud2->dict1->dict3->userdef->integer = value;
     ud2->dict1->dict3->string = g_strdup(strings[3]);

     visit_type_UserDefTwo(data->ov, &ud2, "unused", &err);
@@ -301,8 +299,7 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
                                            const void *unused)
 {
     EnumOne bad_values[] = { ENUM_ONE_MAX, -1 };
-    UserDefZero b;
-    UserDefOne u = { .base = &b }, *pu = &u;
+    UserDefOne u, *pu = &u;
     Error *err;
     int i;

@@ -416,8 +413,7 @@ static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data,
         p->value->dict1->dict2 = g_new0(UserDefTwoDictDict, 1);
         p->value->dict1->dict2->userdef = g_new0(UserDefOne, 1);
         p->value->dict1->dict2->userdef->string = g_strdup(string);
-        p->value->dict1->dict2->userdef->base = g_new0(UserDefZero, 1);
-        p->value->dict1->dict2->userdef->base->integer = 42;
+        p->value->dict1->dict2->userdef->integer = 42;
         p->value->dict1->dict2->string = g_strdup(string);
         p->value->dict1->has_dict3 = false;

diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index fa86cae..634563b 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -258,15 +258,13 @@ static UserDefTwo *nested_struct_create(void)
     udnp->dict1->string1 = strdup("test_string1");
     udnp->dict1->dict2 = g_malloc0(sizeof(*udnp->dict1->dict2));
     udnp->dict1->dict2->userdef = g_new0(UserDefOne, 1);
-    udnp->dict1->dict2->userdef->base = g_new0(UserDefZero, 1);
-    udnp->dict1->dict2->userdef->base->integer = 42;
+    udnp->dict1->dict2->userdef->integer = 42;
     udnp->dict1->dict2->userdef->string = strdup("test_string");
     udnp->dict1->dict2->string = strdup("test_string2");
     udnp->dict1->dict3 = g_malloc0(sizeof(*udnp->dict1->dict3));
     udnp->dict1->has_dict3 = true;
     udnp->dict1->dict3->userdef = g_new0(UserDefOne, 1);
-    udnp->dict1->dict3->userdef->base = g_new0(UserDefZero, 1);
-    udnp->dict1->dict3->userdef->base->integer = 43;
+    udnp->dict1->dict3->userdef->integer = 43;
     udnp->dict1->dict3->userdef->string = strdup("test_string");
     udnp->dict1->dict3->string = strdup("test_string3");
     return udnp;
@@ -278,15 +276,15 @@ static void nested_struct_compare(UserDefTwo *udnp1, UserDefTwo *udnp2)
     g_assert(udnp2);
     g_assert_cmpstr(udnp1->string0, ==, udnp2->string0);
     g_assert_cmpstr(udnp1->dict1->string1, ==, udnp2->dict1->string1);
-    g_assert_cmpint(udnp1->dict1->dict2->userdef->base->integer, ==,
-                    udnp2->dict1->dict2->userdef->base->integer);
+    g_assert_cmpint(udnp1->dict1->dict2->userdef->integer, ==,
+                    udnp2->dict1->dict2->userdef->integer);
     g_assert_cmpstr(udnp1->dict1->dict2->userdef->string, ==,
                     udnp2->dict1->dict2->userdef->string);
     g_assert_cmpstr(udnp1->dict1->dict2->string, ==,
                     udnp2->dict1->dict2->string);
     g_assert(udnp1->dict1->has_dict3 == udnp2->dict1->has_dict3);
-    g_assert_cmpint(udnp1->dict1->dict3->userdef->base->integer, ==,
-                    udnp2->dict1->dict3->userdef->base->integer);
+    g_assert_cmpint(udnp1->dict1->dict3->userdef->integer, ==,
+                    udnp2->dict1->dict3->userdef->integer);
     g_assert_cmpstr(udnp1->dict1->dict3->userdef->string, ==,
                     udnp2->dict1->dict3->userdef->string);
     g_assert_cmpstr(udnp1->dict1->dict3->string, ==,
diff --git a/ui/spice-core.c b/ui/spice-core.c
index bf4fd07..86f4441 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -200,8 +200,6 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
 {
     SpiceServerInfo *server = g_malloc0(sizeof(*server));
     SpiceChannel *client = g_malloc0(sizeof(*client));
-    server->base = g_malloc0(sizeof(*server->base));
-    client->base = g_malloc0(sizeof(*client->base));

     /*
      * Spice server might have called us from spice worker thread
@@ -218,9 +216,11 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
     }

     if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
-        add_addr_info(client->base, (struct sockaddr *)&info->paddr_ext,
+        add_addr_info((SpiceBasicInfo *)client,
+                      (struct sockaddr *)&info->paddr_ext,
                       info->plen_ext);
-        add_addr_info(server->base, (struct sockaddr *)&info->laddr_ext,
+        add_addr_info((SpiceBasicInfo *)server,
+                      (struct sockaddr *)&info->laddr_ext,
                       info->llen_ext);
     } else {
         error_report("spice: %s, extended address is expected",
@@ -229,7 +229,9 @@ static void channel_event(int event, SpiceChannelEventInfo *info)

     switch (event) {
     case SPICE_CHANNEL_EVENT_CONNECTED:
-        qapi_event_send_spice_connected(server->base, client->base, &error_abort);
+        qapi_event_send_spice_connected((SpiceBasicInfo *)server,
+                                        (SpiceBasicInfo *)client,
+                                        &error_abort);
         break;
     case SPICE_CHANNEL_EVENT_INITIALIZED:
         if (auth) {
@@ -242,7 +244,9 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
         break;
     case SPICE_CHANNEL_EVENT_DISCONNECTED:
         channel_list_del(info);
-        qapi_event_send_spice_disconnected(server->base, client->base, &error_abort);
+        qapi_event_send_spice_disconnected((SpiceBasicInfo *)server,
+                                           (SpiceBasicInfo *)client,
+                                           &error_abort);
         break;
     default:
         break;
@@ -378,16 +382,15 @@ static SpiceChannelList *qmp_query_spice_channels(void)

         chan = g_malloc0(sizeof(*chan));
         chan->value = g_malloc0(sizeof(*chan->value));
-        chan->value->base = g_malloc0(sizeof(*chan->value->base));

         paddr = (struct sockaddr *)&item->info->paddr_ext;
         plen = item->info->plen_ext;
         getnameinfo(paddr, plen,
                     host, sizeof(host), port, sizeof(port),
                     NI_NUMERICHOST | NI_NUMERICSERV);
-        chan->value->base->host = g_strdup(host);
-        chan->value->base->port = g_strdup(port);
-        chan->value->base->family = inet_netfamily(paddr->sa_family);
+        chan->value->host = g_strdup(host);
+        chan->value->port = g_strdup(port);
+        chan->value->family = inet_netfamily(paddr->sa_family);

         chan->value->connection_id = item->info->connection_id;
         chan->value->channel_type = item->info->type;
diff --git a/ui/vnc.c b/ui/vnc.c
index 61af4ba..e7c29fe 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -261,8 +261,7 @@ static VncServerInfo *vnc_server_info_get(VncDisplay *vd)
     VncServerInfo *info;

     info = g_malloc(sizeof(*info));
-    info->base = g_malloc(sizeof(*info->base));
-    vnc_basic_info_get_from_server_addr(vd->lsock, info->base, NULL);
+    vnc_basic_info_get_from_server_addr(vd->lsock, (VncBasicInfo *)info, NULL);
     info->has_auth = true;
     info->auth = g_strdup(vnc_auth_name(vd));
     return info;
@@ -293,8 +292,8 @@ static void vnc_client_cache_addr(VncState *client)
 {
     Error *err = NULL;
     client->info = g_malloc0(sizeof(*client->info));
-    client->info->base = g_malloc0(sizeof(*client->info->base));
-    vnc_basic_info_get_from_remote_addr(client->csock, client->info->base,
+    vnc_basic_info_get_from_remote_addr(client->csock,
+                                        (VncBasicInfo *)client->info,
                                         &err);
     if (err) {
         qapi_free_VncClientInfo(client->info);
@@ -310,7 +309,6 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event)
     if (!vs->info) {
         return;
     }
-    g_assert(vs->info->base);

     si = vnc_server_info_get(vs->vd);
     if (!si) {
@@ -319,7 +317,8 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event)

     switch (event) {
     case QAPI_EVENT_VNC_CONNECTED:
-        qapi_event_send_vnc_connected(si, vs->info->base, &error_abort);
+        qapi_event_send_vnc_connected(si, (VncBasicInfo *)vs->info,
+                                      &error_abort);
         break;
     case QAPI_EVENT_VNC_INITIALIZED:
         qapi_event_send_vnc_initialized(si, vs->info, &error_abort);
@@ -354,11 +353,10 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client)
     }

     info = g_malloc0(sizeof(*info));
-    info->base = g_malloc0(sizeof(*info->base));
-    info->base->host = g_strdup(host);
-    info->base->service = g_strdup(serv);
-    info->base->family = inet_netfamily(sa.ss_family);
-    info->base->websocket = client->websocket;
+    info->host = g_strdup(host);
+    info->service = g_strdup(serv);
+    info->family = inet_netfamily(sa.ss_family);
+    info->websocket = client->websocket;

     if (client->tls) {
         info->x509_dname = qcrypto_tls_session_get_peer_name(client->tls);
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 06/17] qapi-visit: Remove redundant functions for flat union base
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
                   ` (4 preceding siblings ...)
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 05/17] qapi: Unbox base members Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-21 17:36   ` Markus Armbruster
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 07/17] qapi: Start converting to new qapi union layout Eric Blake
                   ` (11 subsequent siblings)
  17 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

The code for visiting the base class of a child struct created
visit_type_Base_fields() which covers all fields of Base; while
the code for visiting the base class of a flat union created
visit_type_Union_fields() covering all fields of the base
except the discriminator.  But if the base class were to always
visit all its fields, then we wouldn't need a separate visit of
the discriminator for a union.  Not only is consistently
visiting all fields easier to understand, it lets us share code.

Now that gen_visit_struct_fields() can potentially collect more
than one function into 'ret', a regular expression searching for
whether a label was used may hit a false positive within the
body of the first function.  But using a regex was overkill,
since we can easily determine when we jumped to a label.

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

---
v9: (no v6-8): hoist from v5 35/46; rebase to master; fix indentation
botch in gen_visit_union(); polish commit message
---
 scripts/qapi-visit.py | 35 +++++++++++++++++------------------
 1 file changed, 17 insertions(+), 18 deletions(-)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 8aae8da..91bf350 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -90,7 +90,7 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e

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

-    if re.search('^ *goto out;', ret, re.MULTILINE):
+    if base or members:
         ret += mcgen('''

 out:
@@ -221,8 +221,8 @@ def gen_visit_union(name, base, variants):
     ret = ''

     if base:
-        members = [m for m in base.members if m != variants.tag_member]
-        ret += gen_visit_struct_fields(name, None, members)
+        ret += gen_visit_struct_fields(base.name, base.base,
+                                       base.local_members)

     for var in variants.variants:
         # Ugly special case for simple union TODO get rid of it
@@ -247,31 +247,30 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error

     if base:
         ret += mcgen('''
-    visit_type_%(c_name)s_fields(v, obj, &err);
+    visit_type_%(c_name)s_fields(v, (%(c_name)s **)obj, &err);
 ''',
-                     c_name=c_name(name))
-        ret += gen_err_check(label='out_obj')
-
-    tag_key = variants.tag_member.name
-    if not variants.tag_name:
-        # we pointlessly use a different key for simple unions
-        tag_key = 'type'
-    ret += mcgen('''
+                     c_name=base.c_name())
+    else:
+        ret += mcgen('''
     visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "%(name)s", &err);
-    if (err) {
-        goto out_obj;
-    }
+''',
+                     c_type=variants.tag_member.type.c_name(),
+                     # TODO ugly special case for simple union
+                     # Use same tag name in C as on the wire to get rid of
+                     # it, then: c_name=c_name(variants.tag_member.name)
+                     c_name='kind',
+                     name=variants.tag_member.name)
+    ret += gen_err_check(label='out_obj')
+    ret += mcgen('''
     if (!visit_start_union(v, !!(*obj)->data, &err) || err) {
         goto out_obj;
     }
     switch ((*obj)->%(c_name)s) {
 ''',
-                 c_type=variants.tag_member.type.c_name(),
                  # TODO ugly special case for simple union
                  # Use same tag name in C as on the wire to get rid of
                  # it, then: c_name=c_name(variants.tag_member.name)
-                 c_name=c_name(variants.tag_name or 'kind'),
-                 name=tag_key)
+                 c_name=c_name(variants.tag_name or 'kind'))

     for var in variants.variants:
         # TODO ugly special case for simple union
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 07/17] qapi: Start converting to new qapi union layout
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
                   ` (5 preceding siblings ...)
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 06/17] qapi-visit: Remove redundant functions for flat union base Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-22 13:54   ` Markus Armbruster
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 08/17] tests: Convert " Eric Blake
                   ` (10 subsequent siblings)
  17 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

We have two issues with our qapi union layout:
1) Even though the QMP wire format spells the tag 'type', the
C code spells it 'kind', requiring some hacks in the generator.
2) The C struct uses an anonymous union, which places all tag
values in the same namespace as all non-variant members. This
leads to spurious collisions if a tag value matches a QMP name.

This patch is the front end for a series that converts to a
saner qapi union layout.  By the end of the series, we will no
longer have the type/kind mismatch, and all tag values will be
under a named union, which requires clients to access
'obj->u.value' instead of 'obj->value'.  But since the
conversion touches a number of files, it is easiest if we
temporarily support BOTH layouts simultaneously.

Given a simple union qapi type:

{ 'union':'Foo', 'data': { 'a':'int', 'b':'bool' } }

we make the following changes in generated qapi-types.h:

| struct Foo {
|-    FooKind kind;
|-    union { /* union tag is @kind */
|+    union {
|+        FooKind kind;
|+        FooKind type;
|+    };
|+    union { /* union tag is @type */
|         void *data;
|         int64_t a;
|         bool b;
|+        union { /* union tag is @type */
|+            void *data;
|+            int64_t a;
|+            bool b;
|+        } u;
|     };
| };

Flat unions do not need the anonymous union for the tag member,
as we already fixed that to use the member name instead of 'kind'
back in commit 0f61af3e.  On the other hand, the duplication
means that we temporarily cannot support 'u' as a branch name.

Later, when the conversions are complete, we will remove the
duplication hacks and restore support for 'u' as a branch name.

Note, however, that we do not rename the generated enum, which
is still 'FooKind'.  A further patch could generate implicit
enums as 'FooType', but that causes more churn to C code, and
gets harder since the generator already reserved the '*Kind'
namespace, but there are already QMP constructs with '*Type'
naming which means we cannot easily reserve it for qapi.

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

---
v9: new patch, but incorporates parts of v5 31/46 and Markus' RFC:
http://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02236.html
---
 scripts/qapi-types.py                   | 26 +++++++++++++++++++-------
 scripts/qapi-visit.py                   | 24 +++++++++---------------
 tests/qapi-schema/qapi-schema-test.json |  4 +++-
 tests/qapi-schema/qapi-schema-test.out  |  4 ++--
 4 files changed, 33 insertions(+), 25 deletions(-)

diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index bcef39d..0a14451 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -136,11 +136,23 @@ struct %(c_name)s {
     if base:
         ret += gen_struct_fields([], base)
     else:
+        # TODO As a hack, we emit both 'kind' and 'type'. Ultimately, we
+        # want to use only 'type', but the conversion is large enough to
+        # require staging over several commits.
         ret += mcgen('''
-    %(c_type)s kind;
+    union {
+        %(c_type)s kind;
+        %(c_type)s type;
+    };
 ''',
                      c_type=c_name(variants.tag_member.type.name))

+    # TODO As a hack, we emit the union twice, once as an anonymous union
+    # and once as a named union.  Ultimately, we want to use only the
+    # named union version (as it avoids conflicts between tag values as
+    # branch names competing with non-variant QMP names), but the conversion
+    # is large enough to require staging over several commits.
+    tmp = ''
     # 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
@@ -149,25 +161,25 @@ struct %(c_name)s {
     # 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('''
+    tmp += mcgen('''
     union { /* union tag is @%(c_name)s */
         void *data;
 ''',
-                 # TODO ugly special case for simple union
-                 # Use same tag name in C as on the wire to get rid of
-                 # it, then: c_name=c_name(variants.tag_member.name)
-                 c_name=c_name(variants.tag_name or 'kind'))
+                 c_name=c_name(variants.tag_member.name))

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

+    ret += tmp
+    ret += '    ' + '\n    '.join(tmp.split('\n'))
     ret += mcgen('''
+    } u;
     };
 };
 ''')
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 91bf350..2afe811 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -182,18 +182,18 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
     if (err) {
         goto out;
     }
-    visit_get_next_type(v, (int*) &(*obj)->kind, %(c_name)s_qtypes, name, &err);
+    visit_get_next_type(v, (int*) &(*obj)->type, %(c_name)s_qtypes, name, &err);
     if (err) {
         goto out_obj;
     }
-    switch ((*obj)->kind) {
+    switch ((*obj)->type) {
 ''',
                 c_name=c_name(name))

     for var in variants.variants:
         ret += mcgen('''
     case %(case)s:
-        visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, name, &err);
+        visit_type_%(c_type)s(v, &(*obj)->u.%(c_name)s, name, &err);
         break;
 ''',
                      case=c_enum_const(variants.tag_member.type.name,
@@ -255,22 +255,16 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
     visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "%(name)s", &err);
 ''',
                      c_type=variants.tag_member.type.c_name(),
-                     # TODO ugly special case for simple union
-                     # Use same tag name in C as on the wire to get rid of
-                     # it, then: c_name=c_name(variants.tag_member.name)
-                     c_name='kind',
+                     c_name=c_name(variants.tag_member.name),
                      name=variants.tag_member.name)
     ret += gen_err_check(label='out_obj')
     ret += mcgen('''
-    if (!visit_start_union(v, !!(*obj)->data, &err) || err) {
+    if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
         goto out_obj;
     }
     switch ((*obj)->%(c_name)s) {
 ''',
-                 # TODO ugly special case for simple union
-                 # Use same tag name in C as on the wire to get rid of
-                 # it, then: c_name=c_name(variants.tag_member.name)
-                 c_name=c_name(variants.tag_name or 'kind'))
+                 c_name=c_name(variants.tag_member.name))

     for var in variants.variants:
         # TODO ugly special case for simple union
@@ -282,13 +276,13 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
                                        var.name))
         if simple_union_type:
             ret += mcgen('''
-        visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "data", &err);
+        visit_type_%(c_type)s(v, &(*obj)->u.%(c_name)s, "data", &err);
 ''',
                          c_type=simple_union_type.c_name(),
                          c_name=c_name(var.name))
         else:
             ret += mcgen('''
-        visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err);
+        visit_type_implicit_%(c_type)s(v, &(*obj)->u.%(c_name)s, &err);
 ''',
                          c_type=var.type.c_name(),
                          c_name=c_name(var.name))
@@ -304,7 +298,7 @@ out_obj:
     error_propagate(errp, err);
     err = NULL;
     if (*obj) {
-        visit_end_union(v, !!(*obj)->data, &err);
+        visit_end_union(v, !!(*obj)->u.data, &err);
     }
     error_propagate(errp, err);
     err = NULL;
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 22e15eb..876ce18 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -113,8 +113,10 @@
 # should still be valid as a type or union branch name. And although
 # '*Kind' and '*List' are forbidden as type names, they should not be
 # forbidden as a member or branch name.
+# TODO - we temporarily do not support 'u' as branch name, while converting
+# code to use the new union layout
 { 'struct': 'has_a', 'data': { 'MyKind': 'int', 'MyList': ['int'] } }
-{ 'union': 'u', 'data': { 'u': 'uint8', 'myKind': 'has_a',
+{ 'union': 'u', 'data': { 'u8': 'uint8', 'myKind': 'has_a',
                           'myList': 'has_a' } }

 # testing commands
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index feaf20d..cb12435 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -202,10 +202,10 @@ object has_a
     member MyKind: int optional=False
     member MyList: intList optional=False
 object u
-    case u: :obj-uint8-wrapper
+    case u8: :obj-uint8-wrapper
     case myKind: :obj-has_a-wrapper
     case myList: :obj-has_a-wrapper
-enum uKind ['u', 'myKind', 'myList']
+enum uKind ['u8', 'myKind', 'myList']
 command user_def_cmd None -> None
    gen=True success_response=True
 command user_def_cmd1 :obj-user_def_cmd1-arg -> None
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 08/17] tests: Convert to new qapi union layout
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
                   ` (6 preceding siblings ...)
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 07/17] qapi: Start converting to new qapi union layout Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-22 14:01   ` Markus Armbruster
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 09/17] block: " Eric Blake
                   ` (9 subsequent siblings)
  17 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

We have two issues with our qapi union layout:
1) Even though the QMP wire format spells the tag 'type', the
C code spells it 'kind', requiring some hacks in the generator.
2) The C struct uses an anonymous union, which places all tag
values in the same namespace as all non-variant members. This
leads to spurious collisions if a tag value matches a QMP name.

Make the conversion to the new layout for testsuite code.

Note that this includes updating an error message regarding a
collision.  After the conversion to the new union qapi layout
is complete, there will still be further patches for cleaning
up collision detection, since the use of a named union can
completely eliminate the collision wording changed here.

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

---
v9: new patch, but incorporates parts of v5 31/46 and Markus' RFC:
http://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02236.html
---
 scripts/qapi.py                         |  2 +-
 tests/qapi-schema/union-clash-type.err  |  2 +-
 tests/qapi-schema/union-clash-type.json |  6 +--
 tests/test-qmp-commands.c               |  4 +-
 tests/test-qmp-input-visitor.c          | 78 ++++++++++++++++-----------------
 tests/test-qmp-output-visitor.c         | 42 +++++++++---------
 6 files changed, 66 insertions(+), 68 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 1e7e08b..aab2b46 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -546,7 +546,7 @@ def check_union(expr, expr_info):
     base = expr.get('base')
     discriminator = expr.get('discriminator')
     members = expr['data']
-    values = {'MAX': '(automatic)', 'KIND': '(automatic)'}
+    values = {'MAX': '(automatic)', 'TYPE': '(automatic tag)'}

     # Two types of unions, determined by discriminator.

diff --git a/tests/qapi-schema/union-clash-type.err b/tests/qapi-schema/union-clash-type.err
index a5dead1..c14bbdd 100644
--- a/tests/qapi-schema/union-clash-type.err
+++ b/tests/qapi-schema/union-clash-type.err
@@ -1 +1 @@
-tests/qapi-schema/union-clash-type.json:8: Union 'TestUnion' member 'kind' clashes with '(automatic)'
+tests/qapi-schema/union-clash-type.json:6: Union 'TestUnion' member 'type' clashes with '(automatic tag)'
diff --git a/tests/qapi-schema/union-clash-type.json b/tests/qapi-schema/union-clash-type.json
index cfc256b..641b2d5 100644
--- a/tests/qapi-schema/union-clash-type.json
+++ b/tests/qapi-schema/union-clash-type.json
@@ -1,9 +1,7 @@
 # Union branch 'type'
 # Reject this, because we would have a clash in generated C, between the
-# simple union's implicit tag member 'kind' and the branch name 'kind'
+# simple union's implicit tag member 'type' and the branch name 'type'
 # within the union.
-# TODO: Even when the generated C is switched to use 'type' rather than
-# 'kind', to match the QMP spelling, the collision should still be detected.
-# Or, we could munge the branch name to allow compilation.
+# TODO: If desired, we could munge the branch name to allow compilation.
 { 'union': 'TestUnion',
   'data': { 'kind': 'int', 'type': 'str' } }
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index ea700d8..f23d8ea 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -62,8 +62,8 @@ __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->kind = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH;
-    ret->__org_qemu_x_branch = strdup("blah1");
+    ret->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH;
+    ret->u.__org_qemu_x_branch = strdup("blah1");

     return ret;
 }
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index 514cfd0..617eada 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -359,7 +359,7 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data,
     g_assert_cmpint(tmp->enum1, ==, ENUM_ONE_VALUE1);
     g_assert_cmpstr(tmp->string, ==, "str");
     g_assert_cmpint(tmp->integer, ==, 41);
-    g_assert_cmpint(tmp->value1->boolean, ==, true);
+    g_assert_cmpint(tmp->u.value1->boolean, ==, true);
     qapi_free_UserDefFlatUnion(tmp);
 }

@@ -372,15 +372,15 @@ static void test_visitor_in_alternate(TestInputVisitorData *data,

     v = visitor_input_test_init(data, "42");
     visit_type_UserDefAlternate(v, &tmp, NULL, &error_abort);
-    g_assert_cmpint(tmp->kind, ==, USER_DEF_ALTERNATE_KIND_I);
-    g_assert_cmpint(tmp->i, ==, 42);
+    g_assert_cmpint(tmp->type, ==, USER_DEF_ALTERNATE_KIND_I);
+    g_assert_cmpint(tmp->u.i, ==, 42);
     qapi_free_UserDefAlternate(tmp);
     visitor_input_teardown(data, NULL);

     v = visitor_input_test_init(data, "'string'");
     visit_type_UserDefAlternate(v, &tmp, NULL, &error_abort);
-    g_assert_cmpint(tmp->kind, ==, USER_DEF_ALTERNATE_KIND_S);
-    g_assert_cmpstr(tmp->s, ==, "string");
+    g_assert_cmpint(tmp->type, ==, USER_DEF_ALTERNATE_KIND_S);
+    g_assert_cmpstr(tmp->u.s, ==, "string");
     qapi_free_UserDefAlternate(tmp);
     visitor_input_teardown(data, NULL);

@@ -419,8 +419,8 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data,
      * parse the same as ans */
     v = visitor_input_test_init(data, "42");
     visit_type_AltStrNum(v, &asn, NULL, &err);
-    /* FIXME g_assert_cmpint(asn->kind, == ALT_STR_NUM_KIND_N); */
-    /* FIXME g_assert_cmpfloat(asn->n, ==, 42); */
+    /* FIXME g_assert_cmpint(asn->type, == ALT_STR_NUM_KIND_N); */
+    /* FIXME g_assert_cmpfloat(asn->u.n, ==, 42); */
     g_assert(err);
     error_free(err);
     err = NULL;
@@ -429,29 +429,29 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data,

     v = visitor_input_test_init(data, "42");
     visit_type_AltNumStr(v, &ans, NULL, &error_abort);
-    g_assert_cmpint(ans->kind, ==, ALT_NUM_STR_KIND_N);
-    g_assert_cmpfloat(ans->n, ==, 42);
+    g_assert_cmpint(ans->type, ==, ALT_NUM_STR_KIND_N);
+    g_assert_cmpfloat(ans->u.n, ==, 42);
     qapi_free_AltNumStr(ans);
     visitor_input_teardown(data, NULL);

     v = visitor_input_test_init(data, "42");
     visit_type_AltStrInt(v, &asi, NULL, &error_abort);
-    g_assert_cmpint(asi->kind, ==, ALT_STR_INT_KIND_I);
-    g_assert_cmpint(asi->i, ==, 42);
+    g_assert_cmpint(asi->type, ==, ALT_STR_INT_KIND_I);
+    g_assert_cmpint(asi->u.i, ==, 42);
     qapi_free_AltStrInt(asi);
     visitor_input_teardown(data, NULL);

     v = visitor_input_test_init(data, "42");
     visit_type_AltIntNum(v, &ain, NULL, &error_abort);
-    g_assert_cmpint(ain->kind, ==, ALT_INT_NUM_KIND_I);
-    g_assert_cmpint(ain->i, ==, 42);
+    g_assert_cmpint(ain->type, ==, ALT_INT_NUM_KIND_I);
+    g_assert_cmpint(ain->u.i, ==, 42);
     qapi_free_AltIntNum(ain);
     visitor_input_teardown(data, NULL);

     v = visitor_input_test_init(data, "42");
     visit_type_AltNumInt(v, &ani, NULL, &error_abort);
-    g_assert_cmpint(ani->kind, ==, ALT_NUM_INT_KIND_I);
-    g_assert_cmpint(ani->i, ==, 42);
+    g_assert_cmpint(ani->type, ==, ALT_NUM_INT_KIND_I);
+    g_assert_cmpint(ani->u.i, ==, 42);
     qapi_free_AltNumInt(ani);
     visitor_input_teardown(data, NULL);

@@ -467,15 +467,15 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data,

     v = visitor_input_test_init(data, "42.5");
     visit_type_AltStrNum(v, &asn, NULL, &error_abort);
-    g_assert_cmpint(asn->kind, ==, ALT_STR_NUM_KIND_N);
-    g_assert_cmpfloat(asn->n, ==, 42.5);
+    g_assert_cmpint(asn->type, ==, ALT_STR_NUM_KIND_N);
+    g_assert_cmpfloat(asn->u.n, ==, 42.5);
     qapi_free_AltStrNum(asn);
     visitor_input_teardown(data, NULL);

     v = visitor_input_test_init(data, "42.5");
     visit_type_AltNumStr(v, &ans, NULL, &error_abort);
-    g_assert_cmpint(ans->kind, ==, ALT_NUM_STR_KIND_N);
-    g_assert_cmpfloat(ans->n, ==, 42.5);
+    g_assert_cmpint(ans->type, ==, ALT_NUM_STR_KIND_N);
+    g_assert_cmpfloat(ans->u.n, ==, 42.5);
     qapi_free_AltNumStr(ans);
     visitor_input_teardown(data, NULL);

@@ -489,15 +489,15 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data,

     v = visitor_input_test_init(data, "42.5");
     visit_type_AltIntNum(v, &ain, NULL, &error_abort);
-    g_assert_cmpint(ain->kind, ==, ALT_INT_NUM_KIND_N);
-    g_assert_cmpfloat(ain->n, ==, 42.5);
+    g_assert_cmpint(ain->type, ==, ALT_INT_NUM_KIND_N);
+    g_assert_cmpfloat(ain->u.n, ==, 42.5);
     qapi_free_AltIntNum(ain);
     visitor_input_teardown(data, NULL);

     v = visitor_input_test_init(data, "42.5");
     visit_type_AltNumInt(v, &ani, NULL, &error_abort);
-    g_assert_cmpint(ani->kind, ==, ALT_NUM_INT_KIND_N);
-    g_assert_cmpfloat(ani->n, ==, 42.5);
+    g_assert_cmpint(ani->type, ==, ALT_NUM_INT_KIND_N);
+    g_assert_cmpfloat(ani->u.n, ==, 42.5);
     qapi_free_AltNumInt(ani);
     visitor_input_teardown(data, NULL);
 }
@@ -527,68 +527,68 @@ static void test_native_list_integer_helper(TestInputVisitorData *data,
     visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &err);
     g_assert(err == NULL);
     g_assert(cvalue != NULL);
-    g_assert_cmpint(cvalue->kind, ==, kind);
+    g_assert_cmpint(cvalue->type, ==, kind);

     switch (kind) {
     case USER_DEF_NATIVE_LIST_UNION_KIND_INTEGER: {
         intList *elem = NULL;
-        for (i = 0, elem = cvalue->integer; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.integer; 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->s8; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.s8; 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->s16; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.s16; 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->s32; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.s32; 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->s64; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.s64; 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->u8; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.u8; 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->u16; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.u16; 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->u32; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.u32; 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->u64; elem; elem = elem->next, i++) {
+        for (i = 0, elem = cvalue->u.u64; elem; elem = elem->next, i++) {
             g_assert_cmpint(elem->value, ==, i);
         }
         break;
@@ -690,9 +690,9 @@ static void test_visitor_in_native_list_bool(TestInputVisitorData *data,
     visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &err);
     g_assert(err == NULL);
     g_assert(cvalue != NULL);
-    g_assert_cmpint(cvalue->kind, ==, USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN);
+    g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN);

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

@@ -725,9 +725,9 @@ static void test_visitor_in_native_list_string(TestInputVisitorData *data,
     visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &err);
     g_assert(err == NULL);
     g_assert(cvalue != NULL);
-    g_assert_cmpint(cvalue->kind, ==, USER_DEF_NATIVE_LIST_UNION_KIND_STRING);
+    g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_STRING);

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

-    for (i = 0, elem = cvalue->number; elem; elem = elem->next, i++) {
+    for (i = 0, elem = cvalue->u.number; 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 88e01ea..25c5e25 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -486,9 +486,9 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
     UserDefFlatUnion *tmp = g_malloc0(sizeof(UserDefFlatUnion));
     tmp->enum1 = ENUM_ONE_VALUE1;
     tmp->string = g_strdup("str");
-    tmp->value1 = g_malloc0(sizeof(UserDefA));
-    /* TODO when generator bug is fixed: tmp->integer = 41; */
-    tmp->value1->boolean = true;
+    tmp->u.value1 = g_malloc0(sizeof(UserDefA));
+    tmp->integer = 41;
+    tmp->u.value1->boolean = true;

     visit_type_UserDefFlatUnion(data->ov, &tmp, NULL, &err);
     g_assert(err == NULL);
@@ -499,7 +499,7 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,

     g_assert_cmpstr(qdict_get_str(qdict, "enum1"), ==, "value1");
     g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "str");
-    /* TODO g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 41); */
+    g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 41);
     g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true);

     qapi_free_UserDefFlatUnion(tmp);
@@ -513,8 +513,8 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     Error *err = NULL;

     UserDefAlternate *tmp = g_malloc0(sizeof(UserDefAlternate));
-    tmp->kind = USER_DEF_ALTERNATE_KIND_I;
-    tmp->i = 42;
+    tmp->type = USER_DEF_ALTERNATE_KIND_I;
+    tmp->u.i = 42;

     visit_type_UserDefAlternate(data->ov, &tmp, NULL, &err);
     g_assert(err == NULL);
@@ -539,9 +539,9 @@ static void test_visitor_out_empty(TestOutputVisitorData *data,
 static void init_native_list(UserDefNativeListUnion *cvalue)
 {
     int i;
-    switch (cvalue->kind) {
+    switch (cvalue->type) {
     case USER_DEF_NATIVE_LIST_UNION_KIND_INTEGER: {
-        intList **list = &cvalue->integer;
+        intList **list = &cvalue->u.integer;
         for (i = 0; i < 32; i++) {
             *list = g_new0(intList, 1);
             (*list)->value = i;
@@ -551,7 +551,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_S8: {
-        int8List **list = &cvalue->s8;
+        int8List **list = &cvalue->u.s8;
         for (i = 0; i < 32; i++) {
             *list = g_new0(int8List, 1);
             (*list)->value = i;
@@ -561,7 +561,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_S16: {
-        int16List **list = &cvalue->s16;
+        int16List **list = &cvalue->u.s16;
         for (i = 0; i < 32; i++) {
             *list = g_new0(int16List, 1);
             (*list)->value = i;
@@ -571,7 +571,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_S32: {
-        int32List **list = &cvalue->s32;
+        int32List **list = &cvalue->u.s32;
         for (i = 0; i < 32; i++) {
             *list = g_new0(int32List, 1);
             (*list)->value = i;
@@ -581,7 +581,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_S64: {
-        int64List **list = &cvalue->s64;
+        int64List **list = &cvalue->u.s64;
         for (i = 0; i < 32; i++) {
             *list = g_new0(int64List, 1);
             (*list)->value = i;
@@ -591,7 +591,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_U8: {
-        uint8List **list = &cvalue->u8;
+        uint8List **list = &cvalue->u.u8;
         for (i = 0; i < 32; i++) {
             *list = g_new0(uint8List, 1);
             (*list)->value = i;
@@ -601,7 +601,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_U16: {
-        uint16List **list = &cvalue->u16;
+        uint16List **list = &cvalue->u.u16;
         for (i = 0; i < 32; i++) {
             *list = g_new0(uint16List, 1);
             (*list)->value = i;
@@ -611,7 +611,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_U32: {
-        uint32List **list = &cvalue->u32;
+        uint32List **list = &cvalue->u.u32;
         for (i = 0; i < 32; i++) {
             *list = g_new0(uint32List, 1);
             (*list)->value = i;
@@ -621,7 +621,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_U64: {
-        uint64List **list = &cvalue->u64;
+        uint64List **list = &cvalue->u.u64;
         for (i = 0; i < 32; i++) {
             *list = g_new0(uint64List, 1);
             (*list)->value = i;
@@ -631,7 +631,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN: {
-        boolList **list = &cvalue->boolean;
+        boolList **list = &cvalue->u.boolean;
         for (i = 0; i < 32; i++) {
             *list = g_new0(boolList, 1);
             (*list)->value = (i % 3 == 0);
@@ -641,7 +641,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_STRING: {
-        strList **list = &cvalue->string;
+        strList **list = &cvalue->u.string;
         for (i = 0; i < 32; i++) {
             *list = g_new0(strList, 1);
             (*list)->value = g_strdup_printf("%d", i);
@@ -651,7 +651,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue)
         break;
     }
     case USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER: {
-        numberList **list = &cvalue->number;
+        numberList **list = &cvalue->u.number;
         for (i = 0; i < 32; i++) {
             *list = g_new0(numberList, 1);
             (*list)->value = (double)i / 3;
@@ -760,14 +760,14 @@ static void test_native_list(TestOutputVisitorData *data,
     Error *err = NULL;
     QObject *obj;

-    cvalue->kind = kind;
+    cvalue->type = kind;
     init_native_list(cvalue);

     visit_type_UserDefNativeListUnion(data->ov, &cvalue, NULL, &err);
     g_assert(err == NULL);

     obj = qmp_output_get_qobject(data->qov);
-    check_native_list(obj, cvalue->kind);
+    check_native_list(obj, cvalue->type);
     qapi_free_UserDefNativeListUnion(cvalue);
     qobject_decref(obj);
 }
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 09/17] block: Convert to new qapi union layout
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
                   ` (7 preceding siblings ...)
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 08/17] tests: Convert " Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 10/17] nbd: " Eric Blake
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Paolo Bonzini, Fam Zheng, armbru, open list:Block layer core

We have two issues with our qapi union layout:
1) Even though the QMP wire format spells the tag 'type', the
C code spells it 'kind', requiring some hacks in the generator.
2) The C struct uses an anonymous union, which places all tag
values in the same namespace as all non-variant members. This
leads to spurious collisions if a tag value matches a QMP name.

Make the conversion to the new layout for block-related code.

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

---
v9: new patch, but incorporates parts of v5 31/46 and Markus' RFC:
http://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02236.html
---
 block/nbd.c   | 18 +++++++++---------
 block/qcow2.c | 10 ++++------
 block/vmdk.c  |  6 +++---
 blockdev.c    | 47 ++++++++++++++++++++++++-----------------------
 4 files changed, 40 insertions(+), 41 deletions(-)

diff --git a/block/nbd.c b/block/nbd.c
index c2a87e9..cd6a587 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -206,24 +206,24 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, char **export,
     saddr = g_new0(SocketAddress, 1);

     if (qdict_haskey(options, "path")) {
-        saddr->kind = SOCKET_ADDRESS_KIND_UNIX;
-        saddr->q_unix = g_new0(UnixSocketAddress, 1);
-        saddr->q_unix->path = g_strdup(qdict_get_str(options, "path"));
+        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"));
         qdict_del(options, "path");
     } else {
-        saddr->kind = SOCKET_ADDRESS_KIND_INET;
-        saddr->inet = g_new0(InetSocketAddress, 1);
-        saddr->inet->host = g_strdup(qdict_get_str(options, "host"));
+        saddr->type = SOCKET_ADDRESS_KIND_INET;
+        saddr->u.inet = g_new0(InetSocketAddress, 1);
+        saddr->u.inet->host = g_strdup(qdict_get_str(options, "host"));
         if (!qdict_get_try_str(options, "port")) {
-            saddr->inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
+            saddr->u.inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
         } else {
-            saddr->inet->port = g_strdup(qdict_get_str(options, "port"));
+            saddr->u.inet->port = g_strdup(qdict_get_str(options, "port"));
         }
         qdict_del(options, "host");
         qdict_del(options, "port");
     }

-    s->client.is_unix = saddr->kind == SOCKET_ADDRESS_KIND_UNIX;
+    s->client.is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;

     *export = g_strdup(qdict_get_try_str(options, "export"));
     if (*export) {
diff --git a/block/qcow2.c b/block/qcow2.c
index 56ad808..bdb3eca 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2736,18 +2736,16 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
     ImageInfoSpecific *spec_info = g_new(ImageInfoSpecific, 1);

     *spec_info = (ImageInfoSpecific){
-        .kind  = IMAGE_INFO_SPECIFIC_KIND_QCOW2,
-        {
-            .qcow2 = g_new(ImageInfoSpecificQCow2, 1),
-        },
+        .type  = IMAGE_INFO_SPECIFIC_KIND_QCOW2,
+        .u.qcow2 = g_new(ImageInfoSpecificQCow2, 1),
     };
     if (s->qcow_version == 2) {
-        *spec_info->qcow2 = (ImageInfoSpecificQCow2){
+        *spec_info->u.qcow2 = (ImageInfoSpecificQCow2){
             .compat             = g_strdup("0.10"),
             .refcount_bits      = s->refcount_bits,
         };
     } else if (s->qcow_version == 3) {
-        *spec_info->qcow2 = (ImageInfoSpecificQCow2){
+        *spec_info->u.qcow2 = (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 be0d640..88b7750 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -2156,19 +2156,19 @@ static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs)
     ImageInfoList **next;

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

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

-    next = &spec_info->vmdk->extents;
+    next = &spec_info->u.vmdk->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 32b04b4..d77650b 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1052,13 +1052,14 @@ void hmp_commit(Monitor *mon, const QDict *qdict)
     }
 }

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

-    action.kind = kind;
-    action.data = data;
+    action.type = type;
+    action.u.data = data;
     list.value = &action;
     list.next = NULL;
     qmp_transaction(&list, errp);
@@ -1297,9 +1298,9 @@ static void internal_snapshot_prepare(BlkTransactionState *common,
     InternalSnapshotState *state;
     int ret1;

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

     /* 1. parse input */
@@ -1439,22 +1440,22 @@ static void external_snapshot_prepare(BlkTransactionState *common,
     TransactionAction *action = common->action;

     /* get parameters */
-    g_assert(action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC);
+    g_assert(action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC);

-    has_device = action->blockdev_snapshot_sync->has_device;
-    device = action->blockdev_snapshot_sync->device;
-    has_node_name = action->blockdev_snapshot_sync->has_node_name;
-    node_name = action->blockdev_snapshot_sync->node_name;
+    has_device = action->u.blockdev_snapshot_sync->has_device;
+    device = action->u.blockdev_snapshot_sync->device;
+    has_node_name = action->u.blockdev_snapshot_sync->has_node_name;
+    node_name = action->u.blockdev_snapshot_sync->node_name;
     has_snapshot_node_name =
-        action->blockdev_snapshot_sync->has_snapshot_node_name;
-    snapshot_node_name = action->blockdev_snapshot_sync->snapshot_node_name;
+        action->u.blockdev_snapshot_sync->has_snapshot_node_name;
+    snapshot_node_name = action->u.blockdev_snapshot_sync->snapshot_node_name;

-    new_image_file = action->blockdev_snapshot_sync->snapshot_file;
-    if (action->blockdev_snapshot_sync->has_format) {
-        format = action->blockdev_snapshot_sync->format;
+    new_image_file = action->u.blockdev_snapshot_sync->snapshot_file;
+    if (action->u.blockdev_snapshot_sync->has_format) {
+        format = action->u.blockdev_snapshot_sync->format;
     }
-    if (action->blockdev_snapshot_sync->has_mode) {
-        mode = action->blockdev_snapshot_sync->mode;
+    if (action->u.blockdev_snapshot_sync->has_mode) {
+        mode = action->u.blockdev_snapshot_sync->mode;
     }

     /* start processing */
@@ -1579,8 +1580,8 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp)
     DriveBackup *backup;
     Error *local_err = NULL;

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

     blk = blk_by_name(backup->device);
     if (!blk) {
@@ -1647,8 +1648,8 @@ static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp)
     BlockBackend *blk;
     Error *local_err = NULL;

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

     blk = blk_by_name(backup->device);
     if (!blk) {
@@ -1774,9 +1775,9 @@ void qmp_transaction(TransactionActionList *dev_list, Error **errp)
         dev_info = dev_entry->value;
         dev_entry = dev_entry->next;

-        assert(dev_info->kind < ARRAY_SIZE(actions));
+        assert(dev_info->type < ARRAY_SIZE(actions));

-        ops = &actions[dev_info->kind];
+        ops = &actions[dev_info->type];
         assert(ops->instance_size > 0);

         state = g_malloc0(ops->instance_size);
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 10/17] nbd: Convert to new qapi union layout
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
                   ` (8 preceding siblings ...)
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 09/17] block: " Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 11/17] net: " Eric Blake
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, armbru

We have two issues with our qapi union layout:
1) Even though the QMP wire format spells the tag 'type', the
C code spells it 'kind', requiring some hacks in the generator.
2) The C struct uses an anonymous union, which places all tag
values in the same namespace as all non-variant members. This
leads to spurious collisions if a tag value matches a QMP name.

Make the conversion to the new layout for NBD-related code.

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

---
v9: new patch, but incorporates parts of v5 31/46 and Markus' RFC:
http://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02236.html
---
 qemu-nbd.c          | 16 ++++++++--------
 util/qemu-sockets.c | 42 +++++++++++++++++++++---------------------
 2 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/qemu-nbd.c b/qemu-nbd.c
index 6428c15..449032d 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -364,17 +364,17 @@ static SocketAddress *nbd_build_socket_address(const char *sockpath,

     saddr = g_new0(SocketAddress, 1);
     if (sockpath) {
-        saddr->kind = SOCKET_ADDRESS_KIND_UNIX;
-        saddr->q_unix = g_new0(UnixSocketAddress, 1);
-        saddr->q_unix->path = g_strdup(sockpath);
+        saddr->type = SOCKET_ADDRESS_KIND_UNIX;
+        saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
+        saddr->u.q_unix->path = g_strdup(sockpath);
     } else {
-        saddr->kind = SOCKET_ADDRESS_KIND_INET;
-        saddr->inet = g_new0(InetSocketAddress, 1);
-        saddr->inet->host = g_strdup(bindto);
+        saddr->type = SOCKET_ADDRESS_KIND_INET;
+        saddr->u.inet = g_new0(InetSocketAddress, 1);
+        saddr->u.inet->host = g_strdup(bindto);
         if (port) {
-            saddr->inet->port = g_strdup(port);
+            saddr->u.inet->port = g_strdup(port);
         } else  {
-            saddr->inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
+            saddr->u.inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
         }
     }

diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 2add83a..492d148 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -904,23 +904,23 @@ SocketAddress *socket_parse(const char *str, Error **errp)
             error_setg(errp, "invalid Unix socket address");
             goto fail;
         } else {
-            addr->kind = SOCKET_ADDRESS_KIND_UNIX;
-            addr->q_unix = g_new(UnixSocketAddress, 1);
-            addr->q_unix->path = g_strdup(str + 5);
+            addr->type = SOCKET_ADDRESS_KIND_UNIX;
+            addr->u.q_unix = g_new(UnixSocketAddress, 1);
+            addr->u.q_unix->path = g_strdup(str + 5);
         }
     } else if (strstart(str, "fd:", NULL)) {
         if (str[3] == '\0') {
             error_setg(errp, "invalid file descriptor address");
             goto fail;
         } else {
-            addr->kind = SOCKET_ADDRESS_KIND_FD;
-            addr->fd = g_new(String, 1);
-            addr->fd->str = g_strdup(str + 3);
+            addr->type = SOCKET_ADDRESS_KIND_FD;
+            addr->u.fd = g_new(String, 1);
+            addr->u.fd->str = g_strdup(str + 3);
         }
     } else {
-        addr->kind = SOCKET_ADDRESS_KIND_INET;
-        addr->inet = inet_parse(str, errp);
-        if (addr->inet == NULL) {
+        addr->type = SOCKET_ADDRESS_KIND_INET;
+        addr->u.inet = inet_parse(str, errp);
+        if (addr->u.inet == NULL) {
             goto fail;
         }
     }
@@ -938,19 +938,19 @@ int socket_connect(SocketAddress *addr, Error **errp,
     int fd;

     opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
-    switch (addr->kind) {
+    switch (addr->type) {
     case SOCKET_ADDRESS_KIND_INET:
-        inet_addr_to_opts(opts, addr->inet);
+        inet_addr_to_opts(opts, addr->u.inet);
         fd = inet_connect_opts(opts, errp, callback, opaque);
         break;

     case SOCKET_ADDRESS_KIND_UNIX:
-        qemu_opt_set(opts, "path", addr->q_unix->path, &error_abort);
+        qemu_opt_set(opts, "path", addr->u.q_unix->path, &error_abort);
         fd = unix_connect_opts(opts, errp, callback, opaque);
         break;

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

     opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
-    switch (addr->kind) {
+    switch (addr->type) {
     case SOCKET_ADDRESS_KIND_INET:
-        inet_addr_to_opts(opts, addr->inet);
+        inet_addr_to_opts(opts, addr->u.inet);
         fd = inet_listen_opts(opts, 0, errp);
         break;

     case SOCKET_ADDRESS_KIND_UNIX:
-        qemu_opt_set(opts, "path", addr->q_unix->path, &error_abort);
+        qemu_opt_set(opts, "path", addr->u.q_unix->path, &error_abort);
         fd = unix_listen_opts(opts, errp);
         break;

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

     default:
@@ -998,12 +998,12 @@ int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp)
     int fd;

     opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
-    switch (remote->kind) {
+    switch (remote->type) {
     case SOCKET_ADDRESS_KIND_INET:
-        inet_addr_to_opts(opts, remote->inet);
+        inet_addr_to_opts(opts, remote->u.inet);
         if (local) {
-            qemu_opt_set(opts, "localaddr", local->inet->host, &error_abort);
-            qemu_opt_set(opts, "localport", local->inet->port, &error_abort);
+            qemu_opt_set(opts, "localaddr", local->u.inet->host, &error_abort);
+            qemu_opt_set(opts, "localport", local->u.inet->port, &error_abort);
         }
         fd = inet_dgram_opts(opts, errp);
         break;
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 11/17] net: Convert to new qapi union layout
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
                   ` (9 preceding siblings ...)
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 10/17] nbd: " Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 12/17] char: " Eric Blake
                   ` (6 subsequent siblings)
  17 siblings, 0 replies; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Jason Wang, armbru, Michael S. Tsirkin

We have two issues with our qapi union layout:
1) Even though the QMP wire format spells the tag 'type', the
C code spells it 'kind', requiring some hacks in the generator.
2) The C struct uses an anonymous union, which places all tag
values in the same namespace as all non-variant members. This
leads to spurious collisions if a tag value matches a QMP name.

Make the conversion to the new layout for net-related code.

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

---
v9: new patch, but incorporates parts of v5 31/46 and Markus' RFC:
http://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02236.html
---
 net/dump.c       |  4 ++--
 net/hub.c        |  4 ++--
 net/l2tpv3.c     |  4 ++--
 net/net.c        | 24 ++++++++++++------------
 net/slirp.c      |  4 ++--
 net/socket.c     |  4 ++--
 net/tap-win32.c  |  4 ++--
 net/tap.c        |  8 ++++----
 net/vde.c        |  4 ++--
 net/vhost-user.c |  4 ++--
 10 files changed, 32 insertions(+), 32 deletions(-)

diff --git a/net/dump.c b/net/dump.c
index 08259af..2812070 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -154,8 +154,8 @@ int net_init_dump(const NetClientOptions *opts, const char *name,
     char def_file[128];
     const NetdevDumpOptions *dump;

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

     assert(peer);

diff --git a/net/hub.c b/net/hub.c
index 3047f12..9ae9f01 100644
--- a/net/hub.c
+++ b/net/hub.c
@@ -285,9 +285,9 @@ int net_init_hubport(const NetClientOptions *opts, const char *name,
 {
     const NetdevHubPortOptions *hubport;

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

     net_hub_add_port(hubport->hubid, name);
     return 0;
diff --git a/net/l2tpv3.c b/net/l2tpv3.c
index 4f9bcee..8e68e54 100644
--- a/net/l2tpv3.c
+++ b/net/l2tpv3.c
@@ -545,8 +545,8 @@ int net_init_l2tpv3(const NetClientOptions *opts,
     s->queue_tail = 0;
     s->header_mismatch = false;

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

     if (l2tpv3->has_ipv6 && l2tpv3->ipv6) {
         s->ipv6 = l2tpv3->ipv6;
diff --git a/net/net.c b/net/net.c
index 39af893..c21c90f 100644
--- a/net/net.c
+++ b/net/net.c
@@ -874,8 +874,8 @@ static int net_init_nic(const NetClientOptions *opts, const char *name,
     NICInfo *nd;
     const NetLegacyNicOptions *nic;

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

     idx = nic_get_free_idx();
     if (idx == -1 || nb_nics >= MAX_NICS) {
@@ -976,9 +976,9 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
         opts = netdev->opts;
         name = netdev->id;

-        if (opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP ||
-            opts->kind == NET_CLIENT_OPTIONS_KIND_NIC ||
-            !net_client_init_fun[opts->kind]) {
+        if (opts->type == NET_CLIENT_OPTIONS_KIND_DUMP ||
+            opts->type == NET_CLIENT_OPTIONS_KIND_NIC ||
+            !net_client_init_fun[opts->type]) {
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
                        "a netdev backend type");
             return -1;
@@ -989,16 +989,16 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
         /* missing optional values have been initialized to "all bits zero" */
         name = net->has_id ? net->id : net->name;

-        if (opts->kind == NET_CLIENT_OPTIONS_KIND_NONE) {
+        if (opts->type == NET_CLIENT_OPTIONS_KIND_NONE) {
             return 0; /* nothing to do */
         }
-        if (opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+        if (opts->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
                        "a net type");
             return -1;
         }

-        if (!net_client_init_fun[opts->kind]) {
+        if (!net_client_init_fun[opts->type]) {
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
                        "a net backend type (maybe it is not compiled "
                        "into this binary)");
@@ -1006,17 +1006,17 @@ 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->kind != NET_CLIENT_OPTIONS_KIND_NIC ||
-            !opts->nic->has_netdev) {
+        if (opts->type != NET_CLIENT_OPTIONS_KIND_NIC ||
+            !opts->u.nic->has_netdev) {
             peer = net_hub_add_port(net->has_vlan ? net->vlan : 0, NULL);
         }
     }

-    if (net_client_init_fun[opts->kind](opts, name, peer, errp) < 0) {
+    if (net_client_init_fun[opts->type](opts, name, peer, errp) < 0) {
         /* FIXME drop when all init functions store an Error */
         if (errp && !*errp) {
             error_setg(errp, QERR_DEVICE_INIT_FAILED,
-                       NetClientOptionsKind_lookup[opts->kind]);
+                       NetClientOptionsKind_lookup[opts->type]);
         }
         return -1;
     }
diff --git a/net/slirp.c b/net/slirp.c
index 7657b38..f505570 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -746,8 +746,8 @@ int net_init_slirp(const NetClientOptions *opts, const char *name,
     const NetdevUserOptions *user;
     const char **dnssearch;

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

     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 b1e3b1c..e8605d4 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -706,8 +706,8 @@ int net_init_socket(const NetClientOptions *opts, const char *name,
     Error *err = NULL;
     const NetdevSocketOptions *sock;

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

     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 625d53c..4e2fa55 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -767,8 +767,8 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
     /* FIXME error_setg(errp, ...) on failure */
     const NetdevTapOptions *tap;

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

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

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

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

-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
-    tap = opts->tap;
+    assert(opts->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    tap = opts->u.tap;
     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 dacaa64..4475d92 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -115,8 +115,8 @@ int net_init_vde(const NetClientOptions *opts, const char *name,
     /* FIXME error_setg(errp, ...) on failure */
     const NetdevVdeOptions *vde;

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

     /* 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 8f354eb..918d4bd 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -273,8 +273,8 @@ int net_init_vhost_user(const NetClientOptions *opts, const char *name,
     const NetdevVhostUserOptions *vhost_user_opts;
     CharDriverState *chr;

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

     chr = net_vhost_parse_chardev(vhost_user_opts, errp);
     if (!chr) {
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 12/17] char: Convert to new qapi union layout
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
                   ` (10 preceding siblings ...)
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 11/17] net: " Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 13/17] input: " Eric Blake
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, armbru

We have two issues with our qapi union layout:
1) Even though the QMP wire format spells the tag 'type', the
C code spells it 'kind', requiring some hacks in the generator.
2) The C struct uses an anonymous union, which places all tag
values in the same namespace as all non-variant members. This
leads to spurious collisions if a tag value matches a QMP name.

Make the conversion to the new layout for character-related
code.

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

---
v9: new patch, but incorporates parts of v5 31/46 and Markus' RFC:
http://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02236.html
---
 qemu-char.c       | 160 +++++++++++++++++++++++++++---------------------------
 spice-qemu-char.c |  12 ++--
 2 files changed, 86 insertions(+), 86 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index a0caca3..22de85e 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -122,18 +122,18 @@ static int SocketAddress_to_str(char *dest, int max_len,
                                 const char *prefix, SocketAddress *addr,
                                 bool is_listen, bool is_telnet)
 {
-    switch (addr->kind) {
+    switch (addr->type) {
     case SOCKET_ADDRESS_KIND_INET:
         return snprintf(dest, max_len, "%s%s:%s:%s%s", prefix,
-                        is_telnet ? "telnet" : "tcp", addr->inet->host,
-                        addr->inet->port, is_listen ? ",server" : "");
+                        is_telnet ? "telnet" : "tcp", addr->u.inet->host,
+                        addr->u.inet->port, is_listen ? ",server" : "");
         break;
     case SOCKET_ADDRESS_KIND_UNIX:
         return snprintf(dest, max_len, "%sunix:%s%s", prefix,
-                        addr->q_unix->path, is_listen ? ",server" : "");
+                        addr->u.q_unix->path, is_listen ? ",server" : "");
         break;
     case SOCKET_ADDRESS_KIND_FD:
-        return snprintf(dest, max_len, "%sfd:%s%s", prefix, addr->fd->str,
+        return snprintf(dest, max_len, "%sfd:%s%s", prefix, addr->u.fd->str,
                         is_listen ? ",server" : "");
         break;
     default:
@@ -686,7 +686,7 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
                                           ChardevBackend *backend,
                                           ChardevReturn *ret, Error **errp)
 {
-    ChardevMux *mux = backend->mux;
+    ChardevMux *mux = backend->u.mux;
     CharDriverState *chr, *drv;
     MuxDriver *d;

@@ -1095,7 +1095,7 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
                                            ChardevReturn *ret,
                                            Error **errp)
 {
-    ChardevHostdev *opts = backend->pipe;
+    ChardevHostdev *opts = backend->u.pipe;
     int fd_in, fd_out;
     char filename_in[CHR_MAX_FILENAME_SIZE];
     char filename_out[CHR_MAX_FILENAME_SIZE];
@@ -1173,7 +1173,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
                                             ChardevReturn *ret,
                                             Error **errp)
 {
-    ChardevStdio *opts = backend->stdio;
+    ChardevStdio *opts = backend->u.stdio;
     CharDriverState *chr;
     struct sigaction act;

@@ -2172,7 +2172,7 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
                                            ChardevReturn *ret,
                                            Error **errp)
 {
-    ChardevHostdev *opts = backend->pipe;
+    ChardevHostdev *opts = backend->u.pipe;
     const char *filename = opts->device;
     CharDriverState *chr;
     WinCharState *s;
@@ -3227,7 +3227,7 @@ static CharDriverState *qemu_chr_open_ringbuf(const char *id,
                                               ChardevReturn *ret,
                                               Error **errp)
 {
-    ChardevRingbuf *opts = backend->ringbuf;
+    ChardevRingbuf *opts = backend->u.ringbuf;
     CharDriverState *chr;
     RingBufCharDriver *d;

@@ -3502,16 +3502,16 @@ static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: file: no filename given");
         return;
     }
-    backend->file = g_new0(ChardevFile, 1);
-    backend->file->out = g_strdup(path);
+    backend->u.file = g_new0(ChardevFile, 1);
+    backend->u.file->out = g_strdup(path);
 }

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

 #ifdef HAVE_CHARDEV_SERIAL
@@ -3524,8 +3524,8 @@ static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: serial/tty: no device path given");
         return;
     }
-    backend->serial = g_new0(ChardevHostdev, 1);
-    backend->serial->device = g_strdup(device);
+    backend->u.serial = g_new0(ChardevHostdev, 1);
+    backend->u.serial->device = g_strdup(device);
 }
 #endif

@@ -3539,8 +3539,8 @@ static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: parallel: no device path given");
         return;
     }
-    backend->parallel = g_new0(ChardevHostdev, 1);
-    backend->parallel->device = g_strdup(device);
+    backend->u.parallel = g_new0(ChardevHostdev, 1);
+    backend->u.parallel->device = g_strdup(device);
 }
 #endif

@@ -3553,8 +3553,8 @@ static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: pipe: no device path given");
         return;
     }
-    backend->pipe = g_new0(ChardevHostdev, 1);
-    backend->pipe->device = g_strdup(device);
+    backend->u.pipe = g_new0(ChardevHostdev, 1);
+    backend->u.pipe->device = g_strdup(device);
 }

 static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
@@ -3562,12 +3562,12 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
 {
     int val;

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

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

@@ -3580,8 +3580,8 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: mux: no chardev given");
         return;
     }
-    backend->mux = g_new0(ChardevMux, 1);
-    backend->mux->chardev = g_strdup(chardev);
+    backend->u.mux = g_new0(ChardevMux, 1);
+    backend->u.mux->chardev = g_strdup(chardev);
 }

 static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
@@ -3608,37 +3608,37 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
         }
     }

-    backend->socket = g_new0(ChardevSocket, 1);
+    backend->u.socket = g_new0(ChardevSocket, 1);

-    backend->socket->has_nodelay = true;
-    backend->socket->nodelay = do_nodelay;
-    backend->socket->has_server = true;
-    backend->socket->server = is_listen;
-    backend->socket->has_telnet = true;
-    backend->socket->telnet = is_telnet;
-    backend->socket->has_wait = true;
-    backend->socket->wait = is_waitconnect;
-    backend->socket->has_reconnect = true;
-    backend->socket->reconnect = reconnect;
+    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;

     addr = g_new0(SocketAddress, 1);
     if (path) {
-        addr->kind = SOCKET_ADDRESS_KIND_UNIX;
-        addr->q_unix = g_new0(UnixSocketAddress, 1);
-        addr->q_unix->path = g_strdup(path);
+        addr->type = SOCKET_ADDRESS_KIND_UNIX;
+        addr->u.q_unix = g_new0(UnixSocketAddress, 1);
+        addr->u.q_unix->path = g_strdup(path);
     } else {
-        addr->kind = SOCKET_ADDRESS_KIND_INET;
-        addr->inet = g_new0(InetSocketAddress, 1);
-        addr->inet->host = g_strdup(host);
-        addr->inet->port = g_strdup(port);
-        addr->inet->has_to = qemu_opt_get(opts, "to");
-        addr->inet->to = qemu_opt_get_number(opts, "to", 0);
-        addr->inet->has_ipv4 = qemu_opt_get(opts, "ipv4");
-        addr->inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0);
-        addr->inet->has_ipv6 = qemu_opt_get(opts, "ipv6");
-        addr->inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0);
+        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);
     }
-    backend->socket->addr = addr;
+    backend->u.socket->addr = addr;
 }

 static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
@@ -3669,27 +3669,27 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
         has_local = true;
     }

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

     addr = g_new0(SocketAddress, 1);
-    addr->kind = SOCKET_ADDRESS_KIND_INET;
-    addr->inet = g_new0(InetSocketAddress, 1);
-    addr->inet->host = g_strdup(host);
-    addr->inet->port = g_strdup(port);
-    addr->inet->has_ipv4 = qemu_opt_get(opts, "ipv4");
-    addr->inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0);
-    addr->inet->has_ipv6 = qemu_opt_get(opts, "ipv6");
-    addr->inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0);
-    backend->udp->remote = addr;
+    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);
+    backend->u.udp->remote = addr;

     if (has_local) {
-        backend->udp->has_local = true;
+        backend->u.udp->has_local = true;
         addr = g_new0(SocketAddress, 1);
-        addr->kind = SOCKET_ADDRESS_KIND_INET;
-        addr->inet = g_new0(InetSocketAddress, 1);
-        addr->inet->host = g_strdup(localaddr);
-        addr->inet->port = g_strdup(localport);
-        backend->udp->local = addr;
+        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;
     }
 }

@@ -3762,7 +3762,7 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
     }

     chr = NULL;
-    backend->kind = cd->kind;
+    backend->type = cd->kind;
     if (cd->parse) {
         cd->parse(opts, backend, &local_err);
         if (local_err) {
@@ -3779,9 +3779,9 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
         qapi_free_ChardevBackend(backend);
         qapi_free_ChardevReturn(ret);
         backend = g_new0(ChardevBackend, 1);
-        backend->mux = g_new0(ChardevMux, 1);
-        backend->kind = CHARDEV_BACKEND_KIND_MUX;
-        backend->mux->chardev = g_strdup(bid);
+        backend->u.mux = g_new0(ChardevMux, 1);
+        backend->type = CHARDEV_BACKEND_KIND_MUX;
+        backend->u.mux->chardev = g_strdup(bid);
         ret = qmp_chardev_add(id, backend, errp);
         if (!ret) {
             chr = qemu_chr_find(bid);
@@ -4122,7 +4122,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
                                               ChardevReturn *ret,
                                               Error **errp)
 {
-    ChardevFile *file = backend->file;
+    ChardevFile *file = backend->u.file;
     int flags, in = -1, out;

     flags = O_WRONLY | O_TRUNC | O_CREAT | O_BINARY;
@@ -4149,7 +4149,7 @@ static CharDriverState *qmp_chardev_open_serial(const char *id,
                                                 ChardevReturn *ret,
                                                 Error **errp)
 {
-    ChardevHostdev *serial = backend->serial;
+    ChardevHostdev *serial = backend->u.serial;
     int fd;

     fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
@@ -4167,7 +4167,7 @@ static CharDriverState *qmp_chardev_open_parallel(const char *id,
                                                   ChardevReturn *ret,
                                                   Error **errp)
 {
-    ChardevHostdev *parallel = backend->parallel;
+    ChardevHostdev *parallel = backend->u.parallel;
     int fd;

     fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
@@ -4212,7 +4212,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
 {
     CharDriverState *chr;
     TCPCharDriver *s;
-    ChardevSocket *sock = backend->socket;
+    ChardevSocket *sock = backend->u.socket;
     SocketAddress *addr = sock->addr;
     bool do_nodelay     = sock->has_nodelay ? sock->nodelay : false;
     bool is_listen      = sock->has_server  ? sock->server  : true;
@@ -4225,7 +4225,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,

     s->fd = -1;
     s->listen_fd = -1;
-    s->is_unix = addr->kind == SOCKET_ADDRESS_KIND_UNIX;
+    s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX;
     s->is_listen = is_listen;
     s->is_telnet = is_telnet;
     s->do_nodelay = do_nodelay;
@@ -4279,7 +4279,7 @@ static CharDriverState *qmp_chardev_open_udp(const char *id,
                                              ChardevReturn *ret,
                                              Error **errp)
 {
-    ChardevUdp *udp = backend->udp;
+    ChardevUdp *udp = backend->u.udp;
     int fd;

     fd = socket_dgram(udp->remote, udp->local, errp);
@@ -4308,7 +4308,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
     for (i = backends; i; i = i->next) {
         cd = i->data;

-        if (cd->kind == backend->kind) {
+        if (cd->kind == backend->type) {
             chr = cd->create(id, backend, ret, &local_err);
             if (local_err) {
                 error_propagate(errp, local_err);
@@ -4326,9 +4326,9 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,

     chr->label = g_strdup(id);
     chr->avail_connections =
-        (backend->kind == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1;
+        (backend->type == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1;
     if (!chr->filename) {
-        chr->filename = g_strdup(ChardevBackendKind_lookup[backend->kind]);
+        chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]);
     }
     if (!chr->explicit_be_open) {
         qemu_chr_be_event(chr, CHR_EVENT_OPENED);
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index a20fb5c..e70e0f7 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -301,7 +301,7 @@ static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
                                                 ChardevReturn *ret,
                                                 Error **errp)
 {
-    const char *type = backend->spicevmc->type;
+    const char *type = backend->u.spicevmc->type;
     const char **psubtype = spice_server_char_device_recognized_subtypes();

     for (; *psubtype != NULL; ++psubtype) {
@@ -324,7 +324,7 @@ static CharDriverState *qemu_chr_open_spice_port(const char *id,
                                                  ChardevReturn *ret,
                                                  Error **errp)
 {
-    const char *name = backend->spiceport->fqdn;
+    const char *name = backend->u.spiceport->fqdn;
     CharDriverState *chr;
     SpiceCharDriver *s;

@@ -362,8 +362,8 @@ static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: spice channel: no name given");
         return;
     }
-    backend->spicevmc = g_new0(ChardevSpiceChannel, 1);
-    backend->spicevmc->type = g_strdup(name);
+    backend->u.spicevmc = g_new0(ChardevSpiceChannel, 1);
+    backend->u.spicevmc->type = g_strdup(name);
 }

 static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
@@ -375,8 +375,8 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: spice port: no name given");
         return;
     }
-    backend->spiceport = g_new0(ChardevSpicePort, 1);
-    backend->spiceport->fqdn = g_strdup(name);
+    backend->u.spiceport = g_new0(ChardevSpicePort, 1);
+    backend->u.spiceport->fqdn = g_strdup(name);
 }

 static void register_types(void)
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 13/17] input: Convert to new qapi union layout
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
                   ` (11 preceding siblings ...)
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 12/17] char: " Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 14/17] memory: " Eric Blake
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Michael S. Tsirkin, Gerd Hoffmann, armbru,
	Luiz Capitulino

We have two issues with our qapi union layout:
1) Even though the QMP wire format spells the tag 'type', the
C code spells it 'kind', requiring some hacks in the generator.
2) The C struct uses an anonymous union, which places all tag
values in the same namespace as all non-variant members. This
leads to spurious collisions if a tag value matches a QMP name.

Make the conversion to the new layout for input-related code.

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

---
v9: new patch, but incorporates parts of v5 31/46 and Markus' RFC:
http://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02236.html
---
 hmp.c                       |  8 ++---
 hw/char/escc.c              | 12 +++----
 hw/input/hid.c              | 32 ++++++++---------
 hw/input/ps2.c              | 24 ++++++-------
 hw/input/virtio-input-hid.c | 27 ++++++++-------
 ui/console.c                | 20 +++++------
 ui/input-keymap.c           | 20 +++++------
 ui/input-legacy.c           | 21 ++++++------
 ui/input.c                  | 84 ++++++++++++++++++++++-----------------------
 9 files changed, 125 insertions(+), 123 deletions(-)

diff --git a/hmp.c b/hmp.c
index 88fd804..cc4946d 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1735,15 +1735,15 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict)
             if (*endp != '\0') {
                 goto err_out;
             }
-            keylist->value->kind = KEY_VALUE_KIND_NUMBER;
-            keylist->value->number = value;
+            keylist->value->type = KEY_VALUE_KIND_NUMBER;
+            keylist->value->u.number = value;
         } else {
             int idx = index_from_key(keyname_buf);
             if (idx == Q_KEY_CODE_MAX) {
                 goto err_out;
             }
-            keylist->value->kind = KEY_VALUE_KIND_QCODE;
-            keylist->value->qcode = idx;
+            keylist->value->type = KEY_VALUE_KIND_QCODE;
+            keylist->value->u.qcode = idx;
         }

         if (!separator) {
diff --git a/hw/char/escc.c b/hw/char/escc.c
index ba653ef..c144e95 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -842,13 +842,13 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
     ChannelState *s = (ChannelState *)dev;
     int qcode, keycode;

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

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

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

     keycode = qcode_to_keycode[qcode];
-    if (!evt->key->down) {
+    if (!evt->u.key->down) {
         keycode |= 0x80;
     }
     trace_escc_sunkbd_event_out(keycode);
diff --git a/hw/input/hid.c b/hw/input/hid.c
index 21ebd9e..e39269f 100644
--- a/hw/input/hid.c
+++ b/hw/input/hid.c
@@ -119,33 +119,33 @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
     assert(hs->n < QUEUE_LENGTH);
     e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];

-    switch (evt->kind) {
+    switch (evt->type) {
     case INPUT_EVENT_KIND_REL:
-        if (evt->rel->axis == INPUT_AXIS_X) {
-            e->xdx += evt->rel->value;
-        } else if (evt->rel->axis == INPUT_AXIS_Y) {
-            e->ydy += evt->rel->value;
+        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;
         }
         break;

     case INPUT_EVENT_KIND_ABS:
-        if (evt->rel->axis == INPUT_AXIS_X) {
-            e->xdx = evt->rel->value;
-        } else if (evt->rel->axis == INPUT_AXIS_Y) {
-            e->ydy = evt->rel->value;
+        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;
         }
         break;

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

@@ -223,8 +223,8 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
     int scancodes[3], i, count;
     int slot;

-    count = qemu_input_key_value_to_scancode(evt->key->key,
-                                             evt->key->down,
+    count = qemu_input_key_value_to_scancode(evt->u.key->key,
+                                             evt->u.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 fdbe565..3d6d496 100644
--- a/hw/input/ps2.c
+++ b/hw/input/ps2.c
@@ -183,8 +183,8 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
     int scancodes[3], i, count;

     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
-    count = qemu_input_key_value_to_scancode(evt->key->key,
-                                             evt->key->down,
+    count = qemu_input_key_value_to_scancode(evt->u.key->key,
+                                             evt->u.key->down,
                                              scancodes);
     for (i = 0; i < count; i++) {
         ps2_put_keycode(s, scancodes[i]);
@@ -393,25 +393,25 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
     if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
         return;

-    switch (evt->kind) {
+    switch (evt->type) {
     case INPUT_EVENT_KIND_REL:
-        if (evt->rel->axis == INPUT_AXIS_X) {
-            s->mouse_dx += evt->rel->value;
-        } else if (evt->rel->axis == INPUT_AXIS_Y) {
-            s->mouse_dy -= evt->rel->value;
+        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;
         }
         break;

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

diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c
index 4d85dad..bdd479c 100644
--- a/hw/input/virtio-input-hid.c
+++ b/hw/input/virtio-input-hid.c
@@ -191,44 +191,45 @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src,
     virtio_input_event event;
     int qcode;

-    switch (evt->kind) {
+    switch (evt->type) {
     case INPUT_EVENT_KIND_KEY:
-        qcode = qemu_input_key_value_to_qcode(evt->key->key);
+        qcode = qemu_input_key_value_to_qcode(evt->u.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->key->down ? 1 : 0);
+            event.value = cpu_to_le32(evt->u.key->down ? 1 : 0);
             virtio_input_send(vinput, &event);
         } else {
-            if (evt->key->down) {
+            if (evt->u.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->btn->button]) {
+        if (keymap_button[evt->u.btn->button]) {
             event.type  = cpu_to_le16(EV_KEY);
-            event.code  = cpu_to_le16(keymap_button[evt->btn->button]);
-            event.value = cpu_to_le32(evt->btn->down ? 1 : 0);
+            event.code  = cpu_to_le16(keymap_button[evt->u.btn->button]);
+            event.value = cpu_to_le32(evt->u.btn->down ? 1 : 0);
             virtio_input_send(vinput, &event);
         } else {
-            if (evt->btn->down) {
+            if (evt->u.btn->down) {
                 fprintf(stderr, "%s: unmapped button: %d [%s]\n", __func__,
-                        evt->btn->button, InputButton_lookup[evt->btn->button]);
+                        evt->u.btn->button,
+                        InputButton_lookup[evt->u.btn->button]);
             }
         }
         break;
     case INPUT_EVENT_KIND_REL:
         event.type  = cpu_to_le16(EV_REL);
-        event.code  = cpu_to_le16(axismap_rel[evt->rel->axis]);
-        event.value = cpu_to_le32(evt->rel->value);
+        event.code  = cpu_to_le16(axismap_rel[evt->u.rel->axis]);
+        event.value = cpu_to_le32(evt->u.rel->value);
         virtio_input_send(vinput, &event);
         break;
     case INPUT_EVENT_KIND_ABS:
         event.type  = cpu_to_le16(EV_ABS);
-        event.code  = cpu_to_le16(axismap_abs[evt->abs->axis]);
-        event.value = cpu_to_le32(evt->abs->value);
+        event.code  = cpu_to_le16(axismap_abs[evt->u.abs->axis]);
+        event.value = cpu_to_le32(evt->u.abs->value);
         virtio_input_send(vinput, &event);
         break;
     default:
diff --git a/ui/console.c b/ui/console.c
index cf649b2..f26544e 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -2016,7 +2016,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->vc, errp);
+    return vc_handler(backend->u.vc, errp);
 }

 void register_vc_handler(VcHandler *handler)
@@ -2057,30 +2057,30 @@ static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
 {
     int val;

-    backend->vc = g_new0(ChardevVC, 1);
+    backend->u.vc = g_new0(ChardevVC, 1);

     val = qemu_opt_get_number(opts, "width", 0);
     if (val != 0) {
-        backend->vc->has_width = true;
-        backend->vc->width = val;
+        backend->u.vc->has_width = true;
+        backend->u.vc->width = val;
     }

     val = qemu_opt_get_number(opts, "height", 0);
     if (val != 0) {
-        backend->vc->has_height = true;
-        backend->vc->height = val;
+        backend->u.vc->has_height = true;
+        backend->u.vc->height = val;
     }

     val = qemu_opt_get_number(opts, "cols", 0);
     if (val != 0) {
-        backend->vc->has_cols = true;
-        backend->vc->cols = val;
+        backend->u.vc->has_cols = true;
+        backend->u.vc->cols = val;
     }

     val = qemu_opt_get_number(opts, "rows", 0);
     if (val != 0) {
-        backend->vc->has_rows = true;
-        backend->vc->rows = val;
+        backend->u.vc->has_rows = true;
+        backend->u.vc->rows = val;
     }
 }

diff --git a/ui/input-keymap.c b/ui/input-keymap.c
index 7635cb0..d36be4b 100644
--- a/ui/input-keymap.c
+++ b/ui/input-keymap.c
@@ -139,11 +139,11 @@ static int number_to_qcode[0x100];

 int qemu_input_key_value_to_number(const KeyValue *value)
 {
-    if (value->kind == KEY_VALUE_KIND_QCODE) {
-        return qcode_to_number[value->qcode];
+    if (value->type == KEY_VALUE_KIND_QCODE) {
+        return qcode_to_number[value->u.qcode];
     } else {
-        assert(value->kind == KEY_VALUE_KIND_NUMBER);
-        return value->number;
+        assert(value->type == KEY_VALUE_KIND_NUMBER);
+        return value->u.number;
     }
 }

@@ -166,11 +166,11 @@ int qemu_input_key_number_to_qcode(uint8_t nr)

 int qemu_input_key_value_to_qcode(const KeyValue *value)
 {
-    if (value->kind == KEY_VALUE_KIND_QCODE) {
-        return value->qcode;
+    if (value->type == KEY_VALUE_KIND_QCODE) {
+        return value->u.qcode;
     } else {
-        assert(value->kind == KEY_VALUE_KIND_NUMBER);
-        return qemu_input_key_number_to_qcode(value->number);
+        assert(value->type == KEY_VALUE_KIND_NUMBER);
+        return qemu_input_key_number_to_qcode(value->u.number);
     }
 }

@@ -180,8 +180,8 @@ int qemu_input_key_value_to_scancode(const KeyValue *value, bool down,
     int keycode = qemu_input_key_value_to_number(value);
     int count = 0;

-    if (value->kind == KEY_VALUE_KIND_QCODE &&
-        value->qcode == Q_KEY_CODE_PAUSE) {
+    if (value->type == KEY_VALUE_KIND_QCODE &&
+        value->u.qcode == 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 e50f296..a67ed32 100644
--- a/ui/input-legacy.c
+++ b/ui/input-legacy.c
@@ -113,8 +113,8 @@ static void legacy_kbd_event(DeviceState *dev, QemuConsole *src,
     if (!entry || !entry->put_kbd) {
         return;
     }
-    count = qemu_input_key_value_to_scancode(evt->key->key,
-                                             evt->key->down,
+    count = qemu_input_key_value_to_scancode(evt->u.key->key,
+                                             evt->u.key->down,
                                              scancodes);
     for (i = 0; i < count; i++) {
         entry->put_kbd(entry->opaque, scancodes[i]);
@@ -150,21 +150,22 @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src,
     };
     QEMUPutMouseEntry *s = (QEMUPutMouseEntry *)dev;

-    switch (evt->kind) {
+    switch (evt->type) {
     case INPUT_EVENT_KIND_BTN:
-        if (evt->btn->down) {
-            s->buttons |= bmap[evt->btn->button];
+        if (evt->u.btn->down) {
+            s->buttons |= bmap[evt->u.btn->button];
         } else {
-            s->buttons &= ~bmap[evt->btn->button];
+            s->buttons &= ~bmap[evt->u.btn->button];
         }
-        if (evt->btn->down && evt->btn->button == INPUT_BUTTON_WHEEL_UP) {
+        if (evt->u.btn->down && evt->u.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->btn->down && evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+        if (evt->u.btn->down &&
+            evt->u.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],
@@ -173,10 +174,10 @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src,
         }
         break;
     case INPUT_EVENT_KIND_ABS:
-        s->axis[evt->abs->axis] = evt->abs->value;
+        s->axis[evt->u.abs->axis] = evt->u.abs->value;
         break;
     case INPUT_EVENT_KIND_REL:
-        s->axis[evt->rel->axis] += evt->rel->value;
+        s->axis[evt->u.rel->axis] += evt->u.rel->value;
         break;
     default:
         break;
diff --git a/ui/input.c b/ui/input.c
index 1a552d1..643f885 100644
--- a/ui/input.c
+++ b/ui/input.c
@@ -147,10 +147,10 @@ void qmp_x_input_send_event(bool has_console, int64_t console,
     for (e = events; e != NULL; e = e->next) {
         InputEvent *event = e->value;

-        if (!qemu_input_find_handler(1 << event->kind, con)) {
+        if (!qemu_input_find_handler(1 << event->type, con)) {
             error_setg(errp, "Input handler not found for "
                              "event type %s",
-                            InputEventKind_lookup[event->kind]);
+                            InputEventKind_lookup[event->type]);
             return;
         }
     }
@@ -168,22 +168,22 @@ static void qemu_input_transform_abs_rotate(InputEvent *evt)
 {
     switch (graphic_rotate) {
     case 90:
-        if (evt->abs->axis == INPUT_AXIS_X) {
-            evt->abs->axis = INPUT_AXIS_Y;
-        } else if (evt->abs->axis == INPUT_AXIS_Y) {
-            evt->abs->axis = INPUT_AXIS_X;
-            evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value;
+        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;
         }
         break;
     case 180:
-        evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value;
+        evt->u.abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->u.abs->value;
         break;
     case 270:
-        if (evt->abs->axis == INPUT_AXIS_X) {
-            evt->abs->axis = INPUT_AXIS_Y;
-            evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value;
-        } else if (evt->abs->axis == INPUT_AXIS_Y) {
-            evt->abs->axis = INPUT_AXIS_X;
+        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;
         }
         break;
     }
@@ -197,18 +197,18 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
     if (src) {
         idx = qemu_console_get_index(src);
     }
-    switch (evt->kind) {
+    switch (evt->type) {
     case INPUT_EVENT_KIND_KEY:
-        switch (evt->key->key->kind) {
+        switch (evt->u.key->key->type) {
         case KEY_VALUE_KIND_NUMBER:
-            qcode = qemu_input_key_number_to_qcode(evt->key->key->number);
+            qcode = qemu_input_key_number_to_qcode(evt->u.key->key->u.number);
             name = QKeyCode_lookup[qcode];
-            trace_input_event_key_number(idx, evt->key->key->number,
-                                         name, evt->key->down);
+            trace_input_event_key_number(idx, evt->u.key->key->u.number,
+                                         name, evt->u.key->down);
             break;
         case KEY_VALUE_KIND_QCODE:
-            name = QKeyCode_lookup[evt->key->key->qcode];
-            trace_input_event_key_qcode(idx, name, evt->key->down);
+            name = QKeyCode_lookup[evt->u.key->key->u.qcode];
+            trace_input_event_key_qcode(idx, name, evt->u.key->down);
             break;
         case KEY_VALUE_KIND_MAX:
             /* keep gcc happy */
@@ -216,16 +216,16 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
         }
         break;
     case INPUT_EVENT_KIND_BTN:
-        name = InputButton_lookup[evt->btn->button];
-        trace_input_event_btn(idx, name, evt->btn->down);
+        name = InputButton_lookup[evt->u.btn->button];
+        trace_input_event_btn(idx, name, evt->u.btn->down);
         break;
     case INPUT_EVENT_KIND_REL:
-        name = InputAxis_lookup[evt->rel->axis];
-        trace_input_event_rel(idx, name, evt->rel->value);
+        name = InputAxis_lookup[evt->u.rel->axis];
+        trace_input_event_rel(idx, name, evt->u.rel->value);
         break;
     case INPUT_EVENT_KIND_ABS:
-        name = InputAxis_lookup[evt->abs->axis];
-        trace_input_event_abs(idx, name, evt->abs->value);
+        name = InputAxis_lookup[evt->u.abs->axis];
+        trace_input_event_abs(idx, name, evt->u.abs->value);
         break;
     case INPUT_EVENT_KIND_MAX:
         /* keep gcc happy */
@@ -311,12 +311,12 @@ void qemu_input_event_send(QemuConsole *src, InputEvent *evt)
     qemu_input_event_trace(src, evt);

     /* pre processing */
-    if (graphic_rotate && (evt->kind == INPUT_EVENT_KIND_ABS)) {
+    if (graphic_rotate && (evt->type == INPUT_EVENT_KIND_ABS)) {
             qemu_input_transform_abs_rotate(evt);
     }

     /* send event */
-    s = qemu_input_find_handler(1 << evt->kind, src);
+    s = qemu_input_find_handler(1 << evt->type, src);
     if (!s) {
         return;
     }
@@ -348,10 +348,10 @@ void qemu_input_event_sync(void)
 InputEvent *qemu_input_event_new_key(KeyValue *key, bool down)
 {
     InputEvent *evt = g_new0(InputEvent, 1);
-    evt->key = g_new0(InputKeyEvent, 1);
-    evt->kind = INPUT_EVENT_KIND_KEY;
-    evt->key->key = key;
-    evt->key->down = down;
+    evt->u.key = g_new0(InputKeyEvent, 1);
+    evt->type = INPUT_EVENT_KIND_KEY;
+    evt->u.key->key = key;
+    evt->u.key->down = down;
     return evt;
 }

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

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

@@ -398,10 +398,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->btn = g_new0(InputBtnEvent, 1);
-    evt->kind = INPUT_EVENT_KIND_BTN;
-    evt->btn->button = btn;
-    evt->btn->down = down;
+    evt->u.btn = g_new0(InputBtnEvent, 1);
+    evt->type = INPUT_EVENT_KIND_BTN;
+    evt->u.btn->button = btn;
+    evt->u.btn->down = down;
     return evt;
 }

@@ -451,8 +451,8 @@ InputEvent *qemu_input_event_new_move(InputEventKind kind,
     InputEvent *evt = g_new0(InputEvent, 1);
     InputMoveEvent *move = g_new0(InputMoveEvent, 1);

-    evt->kind = kind;
-    evt->data = move;
+    evt->type = kind;
+    evt->u.data = move;
     move->axis = axis;
     move->value = value;
     return evt;
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 14/17] memory: Convert to new qapi union layout
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
                   ` (12 preceding siblings ...)
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 13/17] input: " Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 15/17] tpm: " Eric Blake
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel
  Cc: Igor Mammedov, Michael S. Tsirkin, armbru, Eduardo Habkost,
	Luiz Capitulino

We have two issues with our qapi union layout:
1) Even though the QMP wire format spells the tag 'type', the
C code spells it 'kind', requiring some hacks in the generator.
2) The C struct uses an anonymous union, which places all tag
values in the same namespace as all non-variant members. This
leads to spurious collisions if a tag value matches a QMP name.

Make the conversion to the new layout for memory-related code.

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

---
v9: new patch, but incorporates parts of v5 31/46 and Markus' RFC:
http://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02236.html
---
 hmp.c            | 6 +++---
 hw/mem/pc-dimm.c | 6 +++---
 numa.c           | 8 ++++----
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/hmp.c b/hmp.c
index cc4946d..39d5815 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1958,12 +1958,12 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
         value = info->value;

         if (value) {
-            switch (value->kind) {
+            switch (value->type) {
             case MEMORY_DEVICE_INFO_KIND_DIMM:
-                di = value->dimm;
+                di = value->u.dimm;

                 monitor_printf(mon, "Memory device [%s]: \"%s\"\n",
-                               MemoryDeviceInfoKind_lookup[value->kind],
+                               MemoryDeviceInfoKind_lookup[value->type],
                                di->id ? di->id : "");
                 monitor_printf(mon, "  addr: 0x%" PRIx64 "\n", di->addr);
                 monitor_printf(mon, "  slot: %" PRId64 "\n", di->slot);
diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c
index 506fe0d..7479570 100644
--- a/hw/mem/pc-dimm.c
+++ b/hw/mem/pc-dimm.c
@@ -173,7 +173,7 @@ int qmp_pc_dimm_device_list(Object *obj, void *opaque)
                                                NULL);
             di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem));

-            info->dimm = di;
+            info->u.dimm = di;
             elem->value = info;
             elem->next = NULL;
             **prev = elem;
@@ -197,9 +197,9 @@ ram_addr_t get_current_ram_size(void)
         MemoryDeviceInfo *value = info->value;

         if (value) {
-            switch (value->kind) {
+            switch (value->type) {
             case MEMORY_DEVICE_INFO_KIND_DIMM:
-                size += value->dimm->size;
+                size += value->u.dimm->size;
                 break;
             default:
                 break;
diff --git a/numa.c b/numa.c
index e9b18f5..fdfe294 100644
--- a/numa.c
+++ b/numa.c
@@ -226,9 +226,9 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)
         goto error;
     }

-    switch (object->kind) {
+    switch (object->type) {
     case NUMA_OPTIONS_KIND_NODE:
-        numa_node_parse(object->node, opts, &err);
+        numa_node_parse(object->u.node, opts, &err);
         if (err) {
             goto error;
         }
@@ -487,9 +487,9 @@ static void numa_stat_memory_devices(uint64_t node_mem[])
         MemoryDeviceInfo *value = info->value;

         if (value) {
-            switch (value->kind) {
+            switch (value->type) {
             case MEMORY_DEVICE_INFO_KIND_DIMM:
-                node_mem[value->dimm->node] += value->dimm->size;
+                node_mem[value->u.dimm->node] += value->u.dimm->size;
                 break;
             default:
                 break;
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 15/17] tpm: Convert to new qapi union layout
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
                   ` (13 preceding siblings ...)
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 14/17] memory: " Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-22 14:19   ` Markus Armbruster
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 16/17] qapi: Finish converting " Eric Blake
                   ` (2 subsequent siblings)
  17 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Luiz Capitulino

We have two issues with our qapi union layout:
1) Even though the QMP wire format spells the tag 'type', the
C code spells it 'kind', requiring some hacks in the generator.
2) The C struct uses an anonymous union, which places all tag
values in the same namespace as all non-variant members. This
leads to spurious collisions if a tag value matches a QMP name.

Make the conversion to the new layout for TPM-related code.

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

---
v9: new patch, but incorporates parts of v5 31/46 and Markus' RFC:
http://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02236.html
---
 hmp.c | 6 +++---
 tpm.c | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/hmp.c b/hmp.c
index 39d5815..a15d00c 100644
--- a/hmp.c
+++ b/hmp.c
@@ -841,11 +841,11 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
                        c, TpmModel_lookup[ti->model]);

         monitor_printf(mon, "  \\ %s: type=%s",
-                       ti->id, TpmTypeOptionsKind_lookup[ti->options->kind]);
+                       ti->id, TpmTypeOptionsKind_lookup[ti->options->type]);

-        switch (ti->options->kind) {
+        switch (ti->options->type) {
         case TPM_TYPE_OPTIONS_KIND_PASSTHROUGH:
-            tpo = ti->options->passthrough;
+            tpo = ti->options->u.passthrough;
             monitor_printf(mon, "%s%s%s%s",
                            tpo->has_path ? ",path=" : "",
                            tpo->has_path ? tpo->path : "",
diff --git a/tpm.c b/tpm.c
index 4e9b109..f2c59d1 100644
--- a/tpm.c
+++ b/tpm.c
@@ -260,9 +260,9 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)

     switch (drv->ops->type) {
     case TPM_TYPE_PASSTHROUGH:
-        res->options->kind = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
+        res->options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
         tpo = g_new0(TPMPassthroughOptions, 1);
-        res->options->passthrough = tpo;
+        res->options->u.passthrough = tpo;
         if (drv->path) {
             tpo->path = g_strdup(drv->path);
             tpo->has_path = true;
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 16/17] qapi: Finish converting to new qapi union layout
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
                   ` (14 preceding siblings ...)
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 15/17] tpm: " Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-22 14:50   ` Markus Armbruster
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 17/17] qapi: Simplify gen_struct_field() Eric Blake
  2015-10-22 15:13 ` [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Markus Armbruster
  17 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

We have two issues with our qapi union layout:
1) Even though the QMP wire format spells the tag 'type', the
C code spells it 'kind', requiring some hacks in the generator.
2) The C struct uses an anonymous union, which places all tag
values in the same namespace as all non-variant members. This
leads to spurious collisions if a tag value matches a QMP name.

This patch is the back end for a series that converts to a
saner qapi union layout.  Now that all clients have been
converted to use 'type' and 'obj->u.value', we can drop the
temporary support for 'kind' and 'obj->value', and undo the
temporary restriction against 'u' as a branch name.

Given a simple union qapi type:

{ 'union':'Foo', 'data': { 'a':'int', 'b':'bool' } }

this is the overall effect, when compared to the state before
this series of patches:

| struct Foo {
|-    FooKind kind;
|-    union { /* union tag is @kind */
|+    FooKind type;
|+    union { /* union tag is @type */
|         void *data;
|         int64_t a;
|         bool b;
|-    };
|+    } u;
| };

Note, however, that we do not rename the generated enum, which
is still 'FooKind'.  A further patch could generate implicit
enums as 'FooType', but that causes more churn to C code, and
gets harder since the generator already reserved the '*Kind'
namespace, but there are already QMP constructs with '*Type'
naming which means we cannot easily reserve it for qapi.

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

---
v9: new patch, but incorporates parts of v5 31/46 and Markus' RFC:
http://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02236.html
---
 scripts/qapi-types.py                   | 26 +++++---------------------
 tests/qapi-schema/qapi-schema-test.json |  4 +---
 tests/qapi-schema/qapi-schema-test.out  |  4 ++--
 3 files changed, 8 insertions(+), 26 deletions(-)

diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 0a14451..4ba39b6 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -136,23 +136,10 @@ struct %(c_name)s {
     if base:
         ret += gen_struct_fields([], base)
     else:
-        # TODO As a hack, we emit both 'kind' and 'type'. Ultimately, we
-        # want to use only 'type', but the conversion is large enough to
-        # require staging over several commits.
-        ret += mcgen('''
-    union {
-        %(c_type)s kind;
-        %(c_type)s type;
-    };
-''',
-                     c_type=c_name(variants.tag_member.type.name))
+        ret += gen_struct_field(variants.tag_member.name,
+                                variants.tag_member.type,
+                                False)

-    # TODO As a hack, we emit the union twice, once as an anonymous union
-    # and once as a named union.  Ultimately, we want to use only the
-    # named union version (as it avoids conflicts between tag values as
-    # branch names competing with non-variant QMP names), but the conversion
-    # is large enough to require staging over several commits.
-    tmp = ''
     # 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
@@ -161,7 +148,7 @@ struct %(c_name)s {
     # 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?
-    tmp += mcgen('''
+    ret += mcgen('''
     union { /* union tag is @%(c_name)s */
         void *data;
 ''',
@@ -170,17 +157,14 @@ struct %(c_name)s {
     for var in variants.variants:
         # Ugly special case for simple union TODO get rid of it
         typ = var.simple_union_type() or var.type
-        tmp += mcgen('''
+        ret += mcgen('''
         %(c_type)s %(c_name)s;
 ''',
                      c_type=typ.c_type(),
                      c_name=c_name(var.name))

-    ret += tmp
-    ret += '    ' + '\n    '.join(tmp.split('\n'))
     ret += mcgen('''
     } u;
-    };
 };
 ''')

diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 876ce18..22e15eb 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -113,10 +113,8 @@
 # should still be valid as a type or union branch name. And although
 # '*Kind' and '*List' are forbidden as type names, they should not be
 # forbidden as a member or branch name.
-# TODO - we temporarily do not support 'u' as branch name, while converting
-# code to use the new union layout
 { 'struct': 'has_a', 'data': { 'MyKind': 'int', 'MyList': ['int'] } }
-{ 'union': 'u', 'data': { 'u8': 'uint8', 'myKind': 'has_a',
+{ 'union': 'u', 'data': { 'u': 'uint8', 'myKind': 'has_a',
                           'myList': 'has_a' } }

 # testing commands
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index cb12435..feaf20d 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -202,10 +202,10 @@ object has_a
     member MyKind: int optional=False
     member MyList: intList optional=False
 object u
-    case u8: :obj-uint8-wrapper
+    case u: :obj-uint8-wrapper
     case myKind: :obj-has_a-wrapper
     case myList: :obj-has_a-wrapper
-enum uKind ['u8', 'myKind', 'myList']
+enum uKind ['u', 'myKind', 'myList']
 command user_def_cmd None -> None
    gen=True success_response=True
 command user_def_cmd1 :obj-user_def_cmd1-arg -> None
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 17/17] qapi: Simplify gen_struct_field()
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
                   ` (15 preceding siblings ...)
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 16/17] qapi: Finish converting " Eric Blake
@ 2015-10-16  4:15 ` Eric Blake
  2015-10-22 15:13 ` [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Markus Armbruster
  17 siblings, 0 replies; 57+ messages in thread
From: Eric Blake @ 2015-10-16  4:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Rather than having all callers pass a name, type, and optional
flag, have them instead pass a QAPISchemaObjectTypeMember which
already has all that information.

No change to generated code.

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

---
v9: rebase after kind/base cleanups, don't rely on member.c_name()
v8: new patch
---
 scripts/qapi-types.py | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 4ba39b6..235fe07 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -36,18 +36,18 @@ struct %(c_name)s {
                  c_name=c_name(name), c_type=element_type.c_type())


-def gen_struct_field(name, typ, optional):
+def gen_struct_field(member):
     ret = ''

-    if optional:
+    if member.optional:
         ret += mcgen('''
     bool has_%(c_name)s;
 ''',
-                     c_name=c_name(name))
+                     c_name=c_name(member.name))
     ret += mcgen('''
     %(c_type)s %(c_name)s;
 ''',
-                 c_type=typ.c_type(), c_name=c_name(name))
+                 c_type=member.type.c_type(), c_name=c_name(member.name))
     return ret


@@ -66,7 +66,7 @@ def gen_struct_fields(members, base, nested=False):
 ''')

     for memb in members:
-        ret += gen_struct_field(memb.name, memb.type, memb.optional)
+        ret += gen_struct_field(memb)
     return ret


@@ -136,9 +136,7 @@ struct %(c_name)s {
     if base:
         ret += gen_struct_fields([], base)
     else:
-        ret += gen_struct_field(variants.tag_member.name,
-                                variants.tag_member.type,
-                                False)
+        ret += gen_struct_field(variants.tag_member)

     # 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
-- 
2.4.3

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

* [Qemu-devel] [PATCH v9 05.5/17] fixup to qapi: Unbox base members
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 05/17] qapi: Unbox base members Eric Blake
@ 2015-10-16 19:12   ` Eric Blake
  2015-10-20 12:09   ` [Qemu-devel] [PATCH v9 05/17] " Markus Armbruster
  1 sibling, 0 replies; 57+ messages in thread
From: Eric Blake @ 2015-10-16 19:12 UTC (permalink / raw)
  To: qemu-devel; +Cc: lcapitulino, kraxel, armbru, mdroth

valgrind reports that our testsuite is making a branch based on
uninitialized memory; and depending on compile options, the state
of the stack could result in the test segfaulting.  I missed the
fact that we were previously relying on 'u' being fully
initialized.

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

---
Introduced by my patch still under review, so easiest is to squash
this in rather than supply it as a separate patch.
---
 tests/test-qmp-output-visitor.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 88e01ea..cfb06bb 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -299,7 +299,8 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
                                            const void *unused)
 {
     EnumOne bad_values[] = { ENUM_ONE_MAX, -1 };
-    UserDefOne u, *pu = &u;
+    UserDefOne u = {0};
+    UserDefOne *pu = &u;
     Error *err;
     int i;

-- 
2.4.3

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

* Re: [Qemu-devel] [PATCH v9 01/17] qapi: Add tests for reserved name abuse
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 01/17] qapi: Add tests for reserved name abuse Eric Blake
@ 2015-10-19 16:05   ` Markus Armbruster
  2015-10-20 16:23     ` Eric Blake
  0 siblings, 1 reply; 57+ messages in thread
From: Markus Armbruster @ 2015-10-19 16:05 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> A future patch wants to change qapi union representation from
> an anonymous C union over to a named C union 'u', so that the
> C names of tag values are in a separate namespace and thus
> cannot collide with the C names of non-variant QMP members.
> But for that to not cause any problems with future extensions
> to existing qapi, it would be best if we prohibit 'u' as a
> member name everywhere, to reserve it for our internal use.
> (Remember that although 'u' would only actually collide in
> flat unions, we must also worry about the fact that it is
> possible to convert from a struct to a flat union in a future
> qemu version while remaining backwards-compatible in QMP.)

This part is awkward: we're adding a negative test that fails to fail
for use of a name that isn't actually reserved until two patches later.

I guess I'd move the 'u' tests to the patch that reserves the name.  Or
at least start the commit message with one of the non-awkward cases :)

> We are failing to detect a collision between a QMP member and
> the implicit 'has_*' flag for another optional QMP member. The
> easiest fix would be for a future patch to reserve the entire
> "has[-_]" namespace for member names (also for branch names,
> but only as long as branch names can collide with QMP names).
>
> Our current representation of qapi arrays is done by appending
> 'List' to the element name; but we are not preventing the
> creation of a non-array type with the same name.  It is also
> possible to abuse the internal naming by writing such things
> as ['intList'] for a 2-dimensional array of integers; however,
> we may want to later add support for explicit 2D arrays such
> as [['int']], so it is better to defer writing tests for what
> we will permit and reject when it comes to multi-dimensioned
> arrays until that later design is complete; for now, we are
> only testing type collision.

I started to write that you might want to mention we already reserve the
'Kind' suffix, then noticed you do in PATCH 02.  No need to say it
twice.

> On the other hand, there is no reason to forbid type name
> patterns from member names, or member name patterns from types,
> since the two are not in the same namespace in C and won't
> collide.

However, we could *choose* to enforce a single name space for
simplicity.  By convention, type names are StudlyCaps (except for
built-ins), and member names are dashed-lower-case, so collisions are
unlikely anyway.

Perhaps you should write "there is no technical reason".

> Modify and add tests to cover these issues.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: new patch
> ---
>  tests/Makefile                                 |  3 +++
>  tests/qapi-schema/args-name-has.err            |  0
>  tests/qapi-schema/args-name-has.exit           |  1 +
>  tests/qapi-schema/args-name-has.json           |  6 ++++++
>  tests/qapi-schema/args-name-has.out            |  6 ++++++
>  tests/qapi-schema/flat-union-clash-branch.json | 17 +++++++----------
>  tests/qapi-schema/flat-union-clash-branch.out  |  9 +++------
>  tests/qapi-schema/qapi-schema-test.json        |  9 +++++++++
>  tests/qapi-schema/qapi-schema-test.out         | 12 ++++++++++++
>  tests/qapi-schema/struct-member-u.err          |  0
>  tests/qapi-schema/struct-member-u.exit         |  1 +
>  tests/qapi-schema/struct-member-u.json         |  6 ++++++
>  tests/qapi-schema/struct-member-u.out          |  3 +++
>  tests/qapi-schema/struct-name-list.err         |  0
>  tests/qapi-schema/struct-name-list.exit        |  1 +
>  tests/qapi-schema/struct-name-list.json        |  5 +++++
>  tests/qapi-schema/struct-name-list.out         |  3 +++
>  17 files changed, 66 insertions(+), 16 deletions(-)

Since this is a test-only patch, I'd prefix the subject with
"tests/qapi-schema:" instead of "qapi:".

>  create mode 100644 tests/qapi-schema/args-name-has.err
>  create mode 100644 tests/qapi-schema/args-name-has.exit
>  create mode 100644 tests/qapi-schema/args-name-has.json
>  create mode 100644 tests/qapi-schema/args-name-has.out
>  create mode 100644 tests/qapi-schema/struct-member-u.err
>  create mode 100644 tests/qapi-schema/struct-member-u.exit
>  create mode 100644 tests/qapi-schema/struct-member-u.json
>  create mode 100644 tests/qapi-schema/struct-member-u.out
>  create mode 100644 tests/qapi-schema/struct-name-list.err
>  create mode 100644 tests/qapi-schema/struct-name-list.exit
>  create mode 100644 tests/qapi-schema/struct-name-list.json
>  create mode 100644 tests/qapi-schema/struct-name-list.out
>
> diff --git a/tests/Makefile b/tests/Makefile
> index cb221de..ef2a19f 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -241,6 +241,7 @@ qapi-schema += args-invalid.json
>  qapi-schema += args-member-array-bad.json
>  qapi-schema += args-member-unknown.json
>  qapi-schema += args-name-clash.json
> +qapi-schema += args-name-has.json
>  qapi-schema += args-union.json
>  qapi-schema += args-unknown.json
>  qapi-schema += bad-base.json
> @@ -323,6 +324,8 @@ qapi-schema += struct-base-clash-deep.json
>  qapi-schema += struct-base-clash.json
>  qapi-schema += struct-data-invalid.json
>  qapi-schema += struct-member-invalid.json
> +qapi-schema += struct-member-u.json
> +qapi-schema += struct-name-list.json
>  qapi-schema += trailing-comma-list.json
>  qapi-schema += trailing-comma-object.json
>  qapi-schema += type-bypass-bad-gen.json
> diff --git a/tests/qapi-schema/args-name-has.err b/tests/qapi-schema/args-name-has.err
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/args-name-has.exit b/tests/qapi-schema/args-name-has.exit
> new file mode 100644
> index 0000000..573541a
> --- /dev/null
> +++ b/tests/qapi-schema/args-name-has.exit
> @@ -0,0 +1 @@
> +0
> diff --git a/tests/qapi-schema/args-name-has.json b/tests/qapi-schema/args-name-has.json
> new file mode 100644
> index 0000000..a2197de
> --- /dev/null
> +++ b/tests/qapi-schema/args-name-has.json
> @@ -0,0 +1,6 @@
> +# C member name collision
> +# FIXME - This parses, but fails to compile, because the C struct is given
> +# two 'has_a' members, one from the flag for optional 'a', and the other
> +# from member 'has-a'.  Either reject this at parse time, or munge the C
> +# names to avoid the collision.
> +{ 'command': 'oops', 'data': { '*a': 'str', 'has-a': 'str' } }

Complements existing args-name-clash.json, which tests 'a-b' and 'a_b'.

Call it args-has-clash.json?

> diff --git a/tests/qapi-schema/args-name-has.out b/tests/qapi-schema/args-name-has.out
> new file mode 100644
> index 0000000..5a18b6b
> --- /dev/null
> +++ b/tests/qapi-schema/args-name-has.out
> @@ -0,0 +1,6 @@
> +object :empty
> +object :obj-oops-arg
> +    member a: str optional=True
> +    member has-a: str optional=False
> +command oops :obj-oops-arg -> None
> +   gen=True success_response=True
> diff --git a/tests/qapi-schema/flat-union-clash-branch.json b/tests/qapi-schema/flat-union-clash-branch.json
> index e593336..c9f08e3 100644
> --- a/tests/qapi-schema/flat-union-clash-branch.json
> +++ b/tests/qapi-schema/flat-union-clash-branch.json
> @@ -1,18 +1,15 @@
>  # Flat union branch name collision
> -# FIXME: this parses, but then fails to compile due to a duplicate 'c_d'
> -# (one from the base member, the other from the branch name).  We should
> -# either reject the collision at parse time, or munge the generated branch
> -# name to allow this to compile.
> +# FIXME: this parses, but then fails to compile due to a duplicate 'has_a'
> +# (one as the implicit flag for the optional base member, the other from
> +# the C member for the branch name).  We should either reject the collision
> +# at parse time, or munge the generated branch name to allow this to compile.
>  { 'enum': 'TestEnum',
> -  'data': [ 'base', 'c-d' ] }
> +  'data': [ 'has-a' ] }
>  { 'struct': 'Base',
> -  'data': { 'enum1': 'TestEnum', '*c_d': 'str' } }
> +  'data': { 'enum1': 'TestEnum', '*a': 'str' } }
>  { 'struct': 'Branch1',
>    'data': { 'string': 'str' } }
> -{ 'struct': 'Branch2',
> -  'data': { 'value': 'int' } }
>  { 'union': 'TestUnion',
>    'base': 'Base',
>    'discriminator': 'enum1',
> -  'data': { 'base': 'Branch1',
> -            'c-d': 'Branch2' } }
> +  'data': { 'has-a': 'Branch1' } }

This replaces the test of branch name 'c-d' clashing with member 'c_d'
by a test of branch name 'has-a' clashing with the has_a flag of
optional member 'a'.  Okay, since flat-union-clash-type.json covers
clash of branch name with member name.

> diff --git a/tests/qapi-schema/flat-union-clash-branch.out b/tests/qapi-schema/flat-union-clash-branch.out
> index 8e0da73..1491081 100644
> --- a/tests/qapi-schema/flat-union-clash-branch.out
> +++ b/tests/qapi-schema/flat-union-clash-branch.out
> @@ -1,14 +1,11 @@
>  object :empty
>  object Base
>      member enum1: TestEnum optional=False
> -    member c_d: str optional=True
> +    member a: str optional=True
>  object Branch1
>      member string: str optional=False
> -object Branch2
> -    member value: int optional=False
> -enum TestEnum ['base', 'c-d']
> +enum TestEnum ['has-a']
>  object TestUnion
>      base Base
>      tag enum1
> -    case base: Branch1
> -    case c-d: Branch2
> +    case has-a: Branch1
> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index 4e2d7c2..c842e22 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -105,6 +105,15 @@
>              'sizes': ['size'],
>              'any': ['any'] } }
>
> +# Even if 'u' is forbidden as a struct member name, it should still be
> +# valid as a type or union branch name. And although '*Kind' is forbidden
> +# as a type name, it should not be forbidden as a member or branch name.
> +# TODO - '*List' should also be forbidden as a type name, and 'has_*' as
> +# a member name.
> +{ 'struct': 'has_a', 'data': { 'MyKind': 'int', 'MyList': ['int'] } }
> +{ 'union': 'u', 'data': { 'u': 'uint8', 'myKind': 'has_a',
> +                          'myList': 'has_a' } }
> +
>  # testing commands
>  { 'command': 'user_def_cmd', 'data': {} }
>  { 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} }

Value of these positive tests seems marginal.  But if you think they're
worth keeping, I'll take them.

> diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
> index a6c80e0..30c3ff0 100644
> --- a/tests/qapi-schema/qapi-schema-test.out
> +++ b/tests/qapi-schema/qapi-schema-test.out
> @@ -22,6 +22,8 @@ object :obj-guest-get-time-arg
>      member b: int optional=True
>  object :obj-guest-sync-arg
>      member arg: any optional=False
> +object :obj-has_a-wrapper
> +    member data: has_a optional=False
>  object :obj-int16List-wrapper
>      member data: int16List optional=False
>  object :obj-int32List-wrapper
> @@ -46,6 +48,8 @@ object :obj-uint32List-wrapper
>      member data: uint32List optional=False
>  object :obj-uint64List-wrapper
>      member data: uint64List optional=False
> +object :obj-uint8-wrapper
> +    member data: uint8 optional=False
>  object :obj-uint8List-wrapper
>      member data: uint8List optional=False
>  object :obj-user_def_cmd1-arg
> @@ -191,6 +195,14 @@ command guest-get-time :obj-guest-get-time-arg -> int
>     gen=True success_response=True
>  command guest-sync :obj-guest-sync-arg -> any
>     gen=True success_response=True
> +object has_a
> +    member MyKind: int optional=False
> +    member MyList: intList optional=False
> +object u
> +    case u: :obj-uint8-wrapper
> +    case myKind: :obj-has_a-wrapper
> +    case myList: :obj-has_a-wrapper
> +enum uKind ['u', 'myKind', 'myList']
>  command user_def_cmd None -> None
>     gen=True success_response=True
>  command user_def_cmd1 :obj-user_def_cmd1-arg -> None
> diff --git a/tests/qapi-schema/struct-member-u.err b/tests/qapi-schema/struct-member-u.err
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/struct-member-u.exit b/tests/qapi-schema/struct-member-u.exit
> new file mode 100644
> index 0000000..573541a
> --- /dev/null
> +++ b/tests/qapi-schema/struct-member-u.exit
> @@ -0,0 +1 @@
> +0
> diff --git a/tests/qapi-schema/struct-member-u.json b/tests/qapi-schema/struct-member-u.json
> new file mode 100644
> index 0000000..d72023d
> --- /dev/null
> +++ b/tests/qapi-schema/struct-member-u.json
> @@ -0,0 +1,6 @@
> +# Potential C member name collision
> +# FIXME - This parses and compiles, but would cause a collision if this
> +# struct is later reworked to be part of a flat union, once unions hide
> +# their tag values under a C union named 'u'. We should reject the use
> +# of this identifier to reserve it for internal use.
> +{ 'struct': 'Oops', 'data': { 'u': 'str' } }

If the later patch outlaws 'u' in structs as well, this test will do,
only the comment will change.

If it outlaws 'u' only where it actually clashes, namely in unions, the
test will need updating.

More reason to move the test to the patch that does the outlawing.

> diff --git a/tests/qapi-schema/struct-member-u.out b/tests/qapi-schema/struct-member-u.out
> new file mode 100644
> index 0000000..aa53e7f
> --- /dev/null
> +++ b/tests/qapi-schema/struct-member-u.out
> @@ -0,0 +1,3 @@
> +object :empty
> +object Oops
> +    member u: str optional=False
> diff --git a/tests/qapi-schema/struct-name-list.err b/tests/qapi-schema/struct-name-list.err
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/struct-name-list.exit b/tests/qapi-schema/struct-name-list.exit
> new file mode 100644
> index 0000000..573541a
> --- /dev/null
> +++ b/tests/qapi-schema/struct-name-list.exit
> @@ -0,0 +1 @@
> +0
> diff --git a/tests/qapi-schema/struct-name-list.json b/tests/qapi-schema/struct-name-list.json
> new file mode 100644
> index 0000000..8ad38e6
> --- /dev/null
> +++ b/tests/qapi-schema/struct-name-list.json
> @@ -0,0 +1,5 @@
> +# Potential C name collision
> +# FIXME - This parses and compiles on its own, but prevents the user from
> +# creating a type named 'Foo' and using ['Foo'] for an array.  We should
> +# reject the use of any non-array type names ending in 'List'.
> +{ 'struct': 'FooList', 'data': { 's': 'str' } }
> diff --git a/tests/qapi-schema/struct-name-list.out b/tests/qapi-schema/struct-name-list.out
> new file mode 100644
> index 0000000..0406bfe
> --- /dev/null
> +++ b/tests/qapi-schema/struct-name-list.out
> @@ -0,0 +1,3 @@
> +object :empty
> +object FooList
> +    member s: str optional=False

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

* Re: [Qemu-devel] [PATCH v9 02/17] qapi: Reserve '*List' type names for arrays
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 02/17] qapi: Reserve '*List' type names for arrays Eric Blake
@ 2015-10-19 16:14   ` Markus Armbruster
  2015-10-20 18:12     ` Eric Blake
  0 siblings, 1 reply; 57+ messages in thread
From: Markus Armbruster @ 2015-10-19 16:14 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> We already reserved '*Kind' for implicit enums (since commit

"We reserved '*Kind' since" sounds odd.  Present tense?

> 4dc2e69), but failed to reserve '*List' for array types.  Since
> no QMP was yet using it, we might as well make the reservation
> official.

Uhm, how can we fail to reserve something, and then make the reservation
official?  I think what you're trying to say is something like

    Type names ending in 'List' can clash with array types in generated
    C.  We don't currently use such names.  Outlaw them.

    Precedence: commit 4dc2e69 outlawed names ending in 'Kind', because
    they can clash with implicit enum types of unions.

> Note that this forbids creation of new types or commands ending
> in 'List', but does NOT forbid abuse of attempts like ['intList']
> for creating a 2-dimensional array of 'int'.

Really?  If I write ['T'], then T must be a declared type, mustn't it?
And after this patch, I cant declare type intList, can I?

>                                               The whole idea of
> multi-dimensional arrays needs more design thought, best left for
> another day.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>

Patch looks good to me.

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

* Re: [Qemu-devel] [PATCH v9 03/17] qapi: Reserve 'u' and 'has[-_]*' member names
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 03/17] qapi: Reserve 'u' and 'has[-_]*' member names Eric Blake
@ 2015-10-19 17:19   ` Markus Armbruster
  2015-10-20 21:29     ` Eric Blake
  0 siblings, 1 reply; 57+ messages in thread
From: Markus Armbruster @ 2015-10-19 17:19 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> To make collision detection between member names easier, we
> might as well reject all attempts to use anything that would
> collide with our use of 'has_' as a flag for optional members.
> Also, a later patch will introduce a named union 'u' for
> holding the branch names of a qapi union in a separate
> namespace from non-variant QMP members, at which point it
> would collide with a 'u' member.  Fortunately, none of our
> qapi files were using these names, so we can reserve them now.
>
> Note that we cannot forbid 'u' everywhere (by adding the
> rejection code to check_name()), because the existing QKeyCode
> enum already uses it, and because qapi-schema-test.json
> intentionally uses it as a branch name (we could feasibly have
> a union with one branch per QKeyCode).  Also, forbidding 'has_'
> in branch names will eventually disappear, once branch names
> cannot collide with other names.  So instead, this code does
> separate checks for check_type() (when iterating over a dict,
> common to struct, command args, and event data) and for
> unions.  We do NOT need to reserve the names for alternates,
> because alternates do not have any QMP members alongside its
> branch names.

I'm not quite comfortable with reserving 'u' now, becaue I feel we
haven't fully explored the design space for avoiding branch - member
clashes.

I still like the basic idea to give the unnamed union a name.  It needs
to be a short one, to keep the C code legible.  'u' is an obvious
option, but it requires reserving 'u' at least as member name.  '_u'
wouldn't.  Alternatively, call the union 'u', but avoid the clash by
mapping QAPI member name 'u' to C identifier '_u'.

I feel the decision should be made over the patch that give the union a
name.

> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: new patch
> ---
>  docs/qapi-code-gen.txt                         |  5 ++++-
>  scripts/qapi.py                                | 12 ++++++++++++
>  tests/qapi-schema/args-name-has.err            |  1 +
>  tests/qapi-schema/args-name-has.exit           |  2 +-
>  tests/qapi-schema/args-name-has.json           |  7 +++----
>  tests/qapi-schema/args-name-has.out            |  6 ------
>  tests/qapi-schema/flat-union-clash-branch.err  |  1 +
>  tests/qapi-schema/flat-union-clash-branch.exit |  2 +-
>  tests/qapi-schema/flat-union-clash-branch.json |  7 ++++---
>  tests/qapi-schema/flat-union-clash-branch.out  | 11 -----------
>  tests/qapi-schema/qapi-schema-test.json        |  9 ++++-----
>  tests/qapi-schema/struct-member-u.err          |  1 +
>  tests/qapi-schema/struct-member-u.exit         |  2 +-
>  tests/qapi-schema/struct-member-u.json         |  8 ++++----
>  tests/qapi-schema/struct-member-u.out          |  3 ---
>  15 files changed, 37 insertions(+), 40 deletions(-)
>
> diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
> index c4264a8..df2d198 100644
> --- a/docs/qapi-code-gen.txt
> +++ b/docs/qapi-code-gen.txt
> @@ -112,7 +112,10 @@ and field names within a type, should be all lower case with words
>  separated by a hyphen.  However, some existing older commands and
>  complex types use underscore; when extending such expressions,
>  consistency is preferred over blindly avoiding underscore.  Event
> -names should be ALL_CAPS with words separated by underscore.
> +names should be ALL_CAPS with words separated by underscore.  Field
> +names cannot be 'u', as this is reserved for generated code for
> +unions, nor can they start with 'has-' or 'has_', as this is
> +reserved for tracking optional fields.
>
>  Any name (command, event, type, field, or enum value) beginning with
>  "x-" is marked experimental, and may be withdrawn or changed
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 09ba44b..1e7e08b 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -488,6 +488,10 @@ def check_type(expr_info, source, value, allow_array=False,
>      for (key, arg) in value.items():
>          check_name(expr_info, "Member of %s" % source, key,
>                     allow_optional=allow_optional)
> +        if key == 'u' or key.startswith('has-') or key.startswith('has_'):

Something like c_name(key).startswith('has_') would avoid hardcoding the
mapping of '-' to '_' here.  Dunno.

> +            raise QAPIExprError(expr_info,
> +                                "Member of %s uses reserved name '%s'"
> +                                % (source, key))
>          # Todo: allow dictionaries to represent default values of
>          # an optional argument.
>          check_type(expr_info, "Member '%s' of %s" % (key, source), arg,
> @@ -588,6 +592,14 @@ def check_union(expr, expr_info):
>      # Check every branch
>      for (key, value) in members.items():
>          check_name(expr_info, "Member of union '%s'" % name, key)
> +        # TODO: As long as branch names can collide with QMP names, we
> +        # must prevent branches starting with 'has_'. However, we do not
> +        # need to reject 'u', because that is reserved for when we start
> +        # sticking branch names in a C union named 'u'.
> +        if key.startswith('has-') or key.startswith('has_'):
> +            raise QAPIExprError(expr_info,
> +                                "Branch of union '%s' uses reserved name '%s'"
> +                                % (name, key))

This will go away again when we give the unnamed union a name.

I feel we should punt all further clash detection until late in the
cleanup work.  It's merely nice to have (sane error message from
generator instead of possibly confusing one from the C compiler,
basically), and adding it now causes churn later on.

>
>          # Each value must name a known type; furthermore, in flat unions,
>          # branches must be a struct with no overlapping member names
> diff --git a/tests/qapi-schema/args-name-has.err b/tests/qapi-schema/args-name-has.err
> index e69de29..cb57552 100644
> --- a/tests/qapi-schema/args-name-has.err
> +++ b/tests/qapi-schema/args-name-has.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/args-name-has.json:5: Member of 'data' for command 'oops' uses reserved name 'has-a'
> diff --git a/tests/qapi-schema/args-name-has.exit b/tests/qapi-schema/args-name-has.exit
> index 573541a..d00491f 100644
> --- a/tests/qapi-schema/args-name-has.exit
> +++ b/tests/qapi-schema/args-name-has.exit
> @@ -1 +1 @@
> -0
> +1
> diff --git a/tests/qapi-schema/args-name-has.json b/tests/qapi-schema/args-name-has.json
> index a2197de..4297549 100644
> --- a/tests/qapi-schema/args-name-has.json
> +++ b/tests/qapi-schema/args-name-has.json
> @@ -1,6 +1,5 @@
>  # C member name collision
> -# FIXME - This parses, but fails to compile, because the C struct is given
> -# two 'has_a' members, one from the flag for optional 'a', and the other
> -# from member 'has-a'.  Either reject this at parse time, or munge the C
> -# names to avoid the collision.
> +# This would attempt to create two 'has_a' members of the C struct, one
> +# from the flag for optional 'a', and the other from member 'has-a'.
> +# TODO we could munge the optional flag name to avoid the collision.

You mean call them _has_FOO instead of has_FOO?  The generated code
would be rather confusing...

If we don't want to reserve all names starting with 'has_', then I'd
narrowly outlaw having both an optional member FOO and a member has_FOO.
I think I'd like that a bit better than outlawing 'has_'.  But not
enough to accept much implementation complexity.

>  { 'command': 'oops', 'data': { '*a': 'str', 'has-a': 'str' } }
> diff --git a/tests/qapi-schema/args-name-has.out b/tests/qapi-schema/args-name-has.out
> index 5a18b6b..e69de29 100644
> --- a/tests/qapi-schema/args-name-has.out
> +++ b/tests/qapi-schema/args-name-has.out
> @@ -1,6 +0,0 @@
> -object :empty
> -object :obj-oops-arg
> -    member a: str optional=True
> -    member has-a: str optional=False
> -command oops :obj-oops-arg -> None
> -   gen=True success_response=True
> diff --git a/tests/qapi-schema/flat-union-clash-branch.err b/tests/qapi-schema/flat-union-clash-branch.err
> index e69de29..e6b6294 100644
> --- a/tests/qapi-schema/flat-union-clash-branch.err
> +++ b/tests/qapi-schema/flat-union-clash-branch.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/flat-union-clash-branch.json:13: Branch of union 'TestUnion' uses reserved name 'has-a'
> diff --git a/tests/qapi-schema/flat-union-clash-branch.exit b/tests/qapi-schema/flat-union-clash-branch.exit
> index 573541a..d00491f 100644
> --- a/tests/qapi-schema/flat-union-clash-branch.exit
> +++ b/tests/qapi-schema/flat-union-clash-branch.exit
> @@ -1 +1 @@
> -0
> +1
> diff --git a/tests/qapi-schema/flat-union-clash-branch.json b/tests/qapi-schema/flat-union-clash-branch.json
> index c9f08e3..8efbcfd 100644
> --- a/tests/qapi-schema/flat-union-clash-branch.json
> +++ b/tests/qapi-schema/flat-union-clash-branch.json
> @@ -1,8 +1,9 @@
>  # Flat union branch name collision
> -# FIXME: this parses, but then fails to compile due to a duplicate 'has_a'
> +# This is rejected because the C struct would have duplicate 'has_a'
>  # (one as the implicit flag for the optional base member, the other from
> -# the C member for the branch name).  We should either reject the collision
> -# at parse time, or munge the generated branch name to allow this to compile.
> +# the C member for the branch name).
> +# TODO: we should munge generated branch names to not collide with the
> +# non-variant struct members.

Okay if I read "munge branch names" to include giving the unnamed union
a name.

>  { 'enum': 'TestEnum',
>    'data': [ 'has-a' ] }
>  { 'struct': 'Base',
> diff --git a/tests/qapi-schema/flat-union-clash-branch.out b/tests/qapi-schema/flat-union-clash-branch.out
> index 1491081..e69de29 100644
> --- a/tests/qapi-schema/flat-union-clash-branch.out
> +++ b/tests/qapi-schema/flat-union-clash-branch.out
> @@ -1,11 +0,0 @@
> -object :empty
> -object Base
> -    member enum1: TestEnum optional=False
> -    member a: str optional=True
> -object Branch1
> -    member string: str optional=False
> -enum TestEnum ['has-a']
> -object TestUnion
> -    base Base
> -    tag enum1
> -    case has-a: Branch1
> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index 81d19d0..1ca7e4f 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -105,11 +105,10 @@
>              'sizes': ['size'],
>              'any': ['any'] } }
>
> -# Even if 'u' is forbidden as a struct member name, it should still be
> -# valid as a type or union branch name. And although '*Kind' and '*List'
> -# are forbidden as type names, they should not be forbidden as a member
> -# or branch name.
> -# TODO - 'has_*' should also be forbidden as a member name.
> +# Even though 'u' and 'has_*' are forbidden as struct member names, they
> +# should still be valid as a type or union branch name. And although
> +# '*Kind' and '*List' are forbidden as type names, they should not be
> +# forbidden as a member or branch name.
>  { 'struct': 'has_a', 'data': { 'MyKind': 'int', 'MyList': ['int'] } }
>  { 'union': 'u', 'data': { 'u': 'uint8', 'myKind': 'has_a',
>                            'myList': 'has_a' } }
> diff --git a/tests/qapi-schema/struct-member-u.err b/tests/qapi-schema/struct-member-u.err
> index e69de29..5610310 100644
> --- a/tests/qapi-schema/struct-member-u.err
> +++ b/tests/qapi-schema/struct-member-u.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/struct-member-u.json:6: Member of 'data' for struct 'Oops' uses reserved name 'u'
> diff --git a/tests/qapi-schema/struct-member-u.exit b/tests/qapi-schema/struct-member-u.exit
> index 573541a..d00491f 100644
> --- a/tests/qapi-schema/struct-member-u.exit
> +++ b/tests/qapi-schema/struct-member-u.exit
> @@ -1 +1 @@
> -0
> +1
> diff --git a/tests/qapi-schema/struct-member-u.json b/tests/qapi-schema/struct-member-u.json
> index d72023d..a0e05b5 100644
> --- a/tests/qapi-schema/struct-member-u.json
> +++ b/tests/qapi-schema/struct-member-u.json
> @@ -1,6 +1,6 @@
>  # Potential C member name collision
> -# FIXME - This parses and compiles, but would cause a collision if this
> -# struct is later reworked to be part of a flat union, once unions hide
> -# their tag values under a C union named 'u'. We should reject the use
> -# of this identifier to reserve it for internal use.
> +# We reject use of 'u' as a member name, to allow it for internal use in
> +# putting union branch members in a separate namespace from QMP members.
> +# This is true even for non-unions, because it is possible to convert a
> +# struct to flat union while remaining backwards compatible in QMP.
>  { 'struct': 'Oops', 'data': { 'u': 'str' } }
> diff --git a/tests/qapi-schema/struct-member-u.out b/tests/qapi-schema/struct-member-u.out
> index aa53e7f..e69de29 100644
> --- a/tests/qapi-schema/struct-member-u.out
> +++ b/tests/qapi-schema/struct-member-u.out
> @@ -1,3 +0,0 @@
> -object :empty
> -object Oops
> -    member u: str optional=False

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

* Re: [Qemu-devel] [PATCH v9 04/17] vnc: hoist allocation of VncBasicInfo to callers
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 04/17] vnc: hoist allocation of VncBasicInfo to callers Eric Blake
@ 2015-10-20  7:38   ` Markus Armbruster
  2015-10-20  8:54     ` Gerd Hoffmann
  2015-10-20 22:56     ` Eric Blake
  0 siblings, 2 replies; 57+ messages in thread
From: Markus Armbruster @ 2015-10-20  7:38 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Gerd Hoffmann

Eric Blake <eblake@redhat.com> writes:

> A future qapi patch will rework generated structs with a base
> class to be unboxed.  In preparation for that, change the code
> that allocates then populates an info struct to instead merely
> populate the fields of an info field passed in as a parameter.
> Add rudimentary Error handling for cases where the old code
> returned NULL; but as before, callers merely ignore errors for
> now.

Actually, the call chain rooted at vnc_qmp_event() does handle failure
before this patch.  It ignores the error *object* after the patch.

>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: (no v6-8): hoist from v5 33/46
> ---
>  ui/vnc.c | 52 ++++++++++++++++++++++++++++------------------------
>  1 file changed, 28 insertions(+), 24 deletions(-)
>
> diff --git a/ui/vnc.c b/ui/vnc.c
> index d73966a..61af4ba 100644
> --- a/ui/vnc.c
> +++ b/ui/vnc.c
> @@ -156,10 +156,11 @@ char *vnc_socket_remote_addr(const char *format, int fd) {
>      return addr_to_string(format, &sa, salen);
>  }
>
> -static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa,
> -                                        socklen_t salen)
> +static void vnc_basic_info_get(struct sockaddr_storage *sa,
> +                               socklen_t salen,
> +                               VncBasicInfo *info,
> +                               Error **errp)
>  {
> -    VncBasicInfo *info;
>      char host[NI_MAXHOST];
>      char serv[NI_MAXSERV];
>      int err;
> @@ -168,42 +169,44 @@ static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa,
>                             host, sizeof(host),
>                             serv, sizeof(serv),
>                             NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
> -        VNC_DEBUG("Cannot resolve address %d: %s\n",
> -                  err, gai_strerror(err));
> -        return NULL;
> +        error_setg(errp, "Cannot resolve address %d: %s",
> +                   err, gai_strerror(err));

Printing err is fine for a debug message.  Less so for an error message.
Drop it?

> +        return;
>      }
>
> -    info = g_malloc0(sizeof(VncBasicInfo));
>      info->host = g_strdup(host);
>      info->service = g_strdup(serv);
>      info->family = inet_netfamily(sa->ss_family);
> -    return info;
>  }

The function no longer "gets info", it merely initializes it.  Rename it
accordingly?  Gerd?

More of the same below.

>
> -static VncBasicInfo *vnc_basic_info_get_from_server_addr(int fd)
> +static void vnc_basic_info_get_from_server_addr(int fd, VncBasicInfo *info,
> +                                                Error **errp)
>  {
>      struct sockaddr_storage sa;
>      socklen_t salen;
>
>      salen = sizeof(sa);
>      if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) {
> -        return NULL;
> +        error_setg_errno(errp, errno, "getsockname failed");
> +        return;
>      }
>
> -    return vnc_basic_info_get(&sa, salen);
> +    vnc_basic_info_get(&sa, salen, info, errp);
>  }
>
> -static VncBasicInfo *vnc_basic_info_get_from_remote_addr(int fd)
> +static void vnc_basic_info_get_from_remote_addr(int fd, VncBasicInfo *info,
> +                                                Error **errp)
>  {
>      struct sockaddr_storage sa;
>      socklen_t salen;
>
>      salen = sizeof(sa);
>      if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) {
> -        return NULL;
> +        error_setg_errno(errp, errno, "getpeername failed");
> +        return;
>      }
>
> -    return vnc_basic_info_get(&sa, salen);
> +    vnc_basic_info_get(&sa, salen, info, errp);
>  }
>
>  static const char *vnc_auth_name(VncDisplay *vd) {
> @@ -256,13 +259,10 @@ static const char *vnc_auth_name(VncDisplay *vd) {
>  static VncServerInfo *vnc_server_info_get(VncDisplay *vd)
>  {
>      VncServerInfo *info;
> -    VncBasicInfo *bi = vnc_basic_info_get_from_server_addr(vd->lsock);
> -    if (!bi) {
> -        return NULL;
> -    }
>
>      info = g_malloc(sizeof(*info));
> -    info->base = bi;
> +    info->base = g_malloc(sizeof(*info->base));
> +    vnc_basic_info_get_from_server_addr(vd->lsock, info->base, NULL);
>      info->has_auth = true;
>      info->auth = g_strdup(vnc_auth_name(vd));
>      return info;

Uh, doesn't this change the return value when getsockname() in
vnc_basic_info_get_from_server_addr() fails?

> @@ -291,11 +291,15 @@ static void vnc_client_cache_auth(VncState *client)
>
>  static void vnc_client_cache_addr(VncState *client)
>  {
> -    VncBasicInfo *bi = vnc_basic_info_get_from_remote_addr(client->csock);
> -
> -    if (bi) {
> -        client->info = g_malloc0(sizeof(*client->info));
> -        client->info->base = bi;
> +    Error *err = NULL;
> +    client->info = g_malloc0(sizeof(*client->info));
> +    client->info->base = g_malloc0(sizeof(*client->info->base));
> +    vnc_basic_info_get_from_remote_addr(client->csock, client->info->base,
> +                                        &err);
> +    if (err) {
> +        qapi_free_VncClientInfo(client->info);
> +        client->info = NULL;
> +        error_free(err);
>      }
>  }

Outside this patch's scope, but since I'm looking at the code already...

Here's the only caller of vnc_server_info_get():

    static void vnc_qmp_event(VncState *vs, QAPIEvent event)
    {
        VncServerInfo *si;

        if (!vs->info) {
            return;
        }
        g_assert(vs->info->base);

        si = vnc_server_info_get(vs->vd);
        if (!si) {
--->        return;
        }

        switch (event) {
        case QAPI_EVENT_VNC_CONNECTED:
            qapi_event_send_vnc_connected(si, vs->info->base, &error_abort);
            break;
        case QAPI_EVENT_VNC_INITIALIZED:
            qapi_event_send_vnc_initialized(si, vs->info, &error_abort);
            break;
        case QAPI_EVENT_VNC_DISCONNECTED:
            qapi_event_send_vnc_disconnected(si, vs->info, &error_abort);
            break;
        default:
            break;
        }

        qapi_free_VncServerInfo(si);
    }

When vnc_server_info_get() fails, the event is dropped.  Why is that
okay?  Failure seems unlikely, though.

Here's the only caller of vnc_client_cache_addr():

    static void vnc_connect(VncDisplay *vd, int csock,
                            bool skipauth, bool websocket)
    {
        VncState *vs = g_malloc0(sizeof(VncState));
        int i;

[...]

--->    vnc_client_cache_addr(vs);
        vnc_qmp_event(vs, QAPI_EVENT_VNC_CONNECTED);
        vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING);

        if (!vs->websocket) {
            vnc_init_state(vs);
        }

        if (vd->num_connecting > vd->connections_limit) {
            QTAILQ_FOREACH(vs, &vd->clients, next) {
                if (vs->share_mode == VNC_SHARE_MODE_CONNECTING) {
                    vnc_disconnect_start(vs);
                    return;
                }
            }
        }
    }

vnc_client_cache_addr(vs) leaves vs->info null on failure.  Gives me a
queasy feeling...  The code in this file appears to cope with it.
Didn't check code elsewhere.

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

* Re: [Qemu-devel] [PATCH v9 04/17] vnc: hoist allocation of VncBasicInfo to callers
  2015-10-20  7:38   ` Markus Armbruster
@ 2015-10-20  8:54     ` Gerd Hoffmann
  2015-10-20 14:46       ` Markus Armbruster
  2015-10-20 22:56     ` Eric Blake
  1 sibling, 1 reply; 57+ messages in thread
From: Gerd Hoffmann @ 2015-10-20  8:54 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel

  Hi,

> > -static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa,
> > -                                        socklen_t salen)
> > +static void vnc_basic_info_get(struct sockaddr_storage *sa,
> > +                               socklen_t salen,
> > +                               VncBasicInfo *info,
> > +                               Error **errp)

> The function no longer "gets info", it merely initializes it.  Rename it
> accordingly?  Gerd?

vnc_fill_basic_info maybe?

> Outside this patch's scope, but since I'm looking at the code already...
> 
> Here's the only caller of vnc_server_info_get():
> 
>     static void vnc_qmp_event(VncState *vs, QAPIEvent event)
>     {
>         VncServerInfo *si;
> 
>         if (!vs->info) {
>             return;
>         }
>         g_assert(vs->info->base);
> 
>         si = vnc_server_info_get(vs->vd);
>         if (!si) {
> --->        return;
>         }
> 
>         switch (event) {
>         case QAPI_EVENT_VNC_CONNECTED:
>             qapi_event_send_vnc_connected(si, vs->info->base, &error_abort);
>             break;
>         case QAPI_EVENT_VNC_INITIALIZED:
>             qapi_event_send_vnc_initialized(si, vs->info, &error_abort);
>             break;
>         case QAPI_EVENT_VNC_DISCONNECTED:
>             qapi_event_send_vnc_disconnected(si, vs->info, &error_abort);
>             break;
>         default:
>             break;
>         }
> 
>         qapi_free_VncServerInfo(si);
>     }
> 
> When vnc_server_info_get() fails, the event is dropped.  Why is that
> okay?  Failure seems unlikely, though.

Suggestions what else to do?  I don't wanna crash qemu by calling
qapi_event_send_vnc_* with a NULL pointer.  And, yes, it should be
highly unlikely so trying some more sophisticated error handling would
probably be dead code ...

cheers,
  Gerd

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

* Re: [Qemu-devel] [PATCH v9 05/17] qapi: Unbox base members
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 05/17] qapi: Unbox base members Eric Blake
  2015-10-16 19:12   ` [Qemu-devel] [PATCH v9 05.5/17] fixup to " Eric Blake
@ 2015-10-20 12:09   ` Markus Armbruster
  2015-10-20 16:08     ` Eric Blake
  1 sibling, 1 reply; 57+ messages in thread
From: Markus Armbruster @ 2015-10-20 12:09 UTC (permalink / raw)
  To: Eric Blake; +Cc: Luiz Capitulino, Gerd Hoffmann, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Rather than storing a base class as a pointer to a box, just
> store the fields of that base class in the same order, so that
> a child struct can be safely cast to its parent.  This gives
> less malloc overhead, less pointer dereferencing, and even less
> generated code.

Lovely!

> Without boxing, the corner case of one empty struct having
> another empty struct as its base type now requires inserting a
> dummy member (previously, the pointer to the base would have
> sufficed).
>
> And now that we no longer consume a 'base' member in the generated
> C struct, we can delete the former negative struct-base-clash-base
> test and replace it with a positive test in qapi-schema-test that
> ensures we don't reintroduce the naming collision.

This test protects against us foolishly adding a member called base to
the generated struct, but provides no protection against equally foolish
additions with other names.  I believe someone adding base back is about
as likely as someone adding other crap.  Therefore, I doubt the positive
test is worthwhile.

> Compare to the earlier commit 1e6c1616a "qapi: Generate a nicer
> struct for flat unions".

Should we mention the struct can be cast to its base?

> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: (no v6-8): hoist from v5 34/46, rebase to master
> ---
>  hmp.c                                         |  6 +++---
>  scripts/qapi-types.py                         | 29 ++++++++++++++-------------
>  scripts/qapi-visit.py                         |  9 ++++++---
>  tests/Makefile                                |  1 -
>  tests/qapi-schema/qapi-schema-test.json       |  4 ++++
>  tests/qapi-schema/qapi-schema-test.out        |  3 +++
>  tests/qapi-schema/struct-base-clash-base.err  |  0
>  tests/qapi-schema/struct-base-clash-base.exit |  1 -
>  tests/qapi-schema/struct-base-clash-base.json |  9 ---------
>  tests/qapi-schema/struct-base-clash-base.out  |  5 -----
>  tests/test-qmp-commands.c                     | 15 +++++---------
>  tests/test-qmp-event.c                        |  8 ++------
>  tests/test-qmp-input-visitor.c                |  4 ++--
>  tests/test-qmp-output-visitor.c               | 12 ++++-------
>  tests/test-visitor-serialization.c            | 14 ++++++-------
>  ui/spice-core.c                               | 23 ++++++++++++---------
>  ui/vnc.c                                      | 20 +++++++++---------
>  17 files changed, 72 insertions(+), 91 deletions(-)
>  delete mode 100644 tests/qapi-schema/struct-base-clash-base.err
>  delete mode 100644 tests/qapi-schema/struct-base-clash-base.exit
>  delete mode 100644 tests/qapi-schema/struct-base-clash-base.json
>  delete mode 100644 tests/qapi-schema/struct-base-clash-base.out
>
> diff --git a/hmp.c b/hmp.c
> index 5048eee..88fd804 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -569,8 +569,8 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict)
>          for (client = info->clients; client; client = client->next) {
>              monitor_printf(mon, "Client:\n");
>              monitor_printf(mon, "     address: %s:%s\n",
> -                           client->value->base->host,
> -                           client->value->base->service);
> +                           client->value->host,
> +                           client->value->service);
>              monitor_printf(mon, "  x509_dname: %s\n",
>                             client->value->x509_dname ?
>                             client->value->x509_dname : "none");
> @@ -638,7 +638,7 @@ void hmp_info_spice(Monitor *mon, const QDict *qdict)
>          for (chan = info->channels; chan; chan = chan->next) {
>              monitor_printf(mon, "Channel:\n");
>              monitor_printf(mon, "     address: %s:%s%s\n",
> -                           chan->value->base->host, chan->value->base->port,
> +                           chan->value->host, chan->value->port,
>                             chan->value->tls ? " [tls]" : "");
>              monitor_printf(mon, "     session: %" PRId64 "\n",
>                             chan->value->connection_id);

Here you can see the the base box going away.

> diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
> index 4fe618e..bcef39d 100644
> --- a/scripts/qapi-types.py
> +++ b/scripts/qapi-types.py
> @@ -51,8 +51,19 @@ def gen_struct_field(name, typ, optional):
>      return ret
>
>
> -def gen_struct_fields(members):
> +def gen_struct_fields(members, base, nested=False):
>      ret = ''
> +    if base:
> +        if not nested:
> +            ret += mcgen('''
> +    /* Members inherited from %(c_name)s: */
> +''',
> +                         c_name=base.c_name())
> +        ret += gen_struct_fields(base.local_members, base.base, True)
> +        if not nested:
> +            ret += mcgen('''
> +    /* Own members: */
> +''')

Before: gen_struct_fields() emits *own* fields.

After: it emits *all* fields.

Since the signature changes, all callers are visible in the patch, and
can be reviewed.

Code looks a bit awkward, but I don't have better ideas.  Perhaps we can
do better when we fuse gen_struct() and gen_union().

>
>      for memb in members:
>          ret += gen_struct_field(memb.name, memb.type, memb.optional)
> @@ -66,16 +77,13 @@ struct %(c_name)s {
>  ''',
>                  c_name=c_name(name))
>
> -    if base:
> -        ret += gen_struct_field('base', base, False)
> -
> -    ret += gen_struct_fields(members)
> +    ret += gen_struct_fields(members, base)

Before: emit boxed base, then own fields.

After: emit all fields.  Good.

Aside: deleting the gen_struct_field() call here enables your "[PATCH
17] qapi: Simplify gen_struct_field()".

>
>      # Make sure that all structs have at least one field; 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 not members:
> +    if not (base and base.members) and not members:
>          ret += mcgen('''
>      char qapi_dummy_field_for_empty_struct;
>  ''')
> @@ -126,14 +134,7 @@ struct %(c_name)s {

This is gen_union().

>  ''',
>                  c_name=c_name(name))
>      if base:
> -        ret += mcgen('''
> -    /* Members inherited from %(c_name)s: */
> -''',
> -                     c_name=c_name(base.name))
> -        ret += gen_struct_fields(base.members)
> -        ret += mcgen('''
> -    /* Own members: */
> -''')
> +        ret += gen_struct_fields([], base)
>      else:
>          ret += mcgen('''
>      %(c_type)s kind;

Before: emit base fields, then own fields.

After: emit all fields.  Good, but please mention in the commit message
that you're touching gen_union().  You could also do this part in a
separate patch, but that's probably overkill.

> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index d0759d7..8aae8da 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -62,12 +62,15 @@ static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error *
>
>
>  def gen_visit_struct_fields(name, base, members):
> +    if name in struct_fields_seen:
> +        return ''
>      struct_fields_seen.add(name)
>
>      ret = ''
>
>      if base:
> -        ret += gen_visit_implicit_struct(base)
> +        ret += gen_visit_struct_fields(base.name, base.base,
> +                                       base.local_members)
>
>      ret += mcgen('''
>

This change looks innocent enough on first glance, but it's actually
quite hairy.

= Before =

The visit_type_FOO_fields() are generated in QAPISchema visit order,
i.e. right when QAPISchemaObjectType 'FOO' is visited.

The visit_type_implicit_FOO() are generated on demand, right before
their first use.  It's used by visit_type_STRUCT_fields() when STRUCT
has base FOO, and by visit_type_UNION() when flat UNION has a member
FOO.

Aside: only flat unions, because simple unions are an ugly special case
here.  To be unified.

Generating visit_type_implicit_FOO() on demand makes sense, because the
function isn't always needed.

Since visit_type_implicit_FOO() calls visit_type_FOO_fields(), and the
former can be generated before the latter, we may need a forward
declaration.  struct_fields_seen is used to detect this.  See commit
8c3f8e7.

= After =

visit_type_implicit_FOO() is now used only when flat UNION has a member
FOO.  May need a forward declaration of visit_type_FOO_fields() anyway.

visit_type_FOO_fields() is now also generated on demand, right before
first use other than visit_type_implicit_FOO().  Weird.

The resulting code motion makes the change to generated code difficult
to review.

Generating visit_type_FOO_fields() on demand makes less sense, because
the function is always needed.  All it can accomplish is saving a few
forward declarations, at the cost of making gen_visit_struct_fields()
recursive, which begs the recursion termination question, and less
uniform generated code.

The naive answer to the recursion termination question is that types
can't contain themselves, therefore we can't ever get into a cycle.
That begs the next question: why do we need the if name in
struct_fields_seen conditional then?  We do need it (turning it into an
assertion promptly fails it), but I can't tell why offhand.

I'm sure I could figure out why this works, but I don't want to.  Let's
keep the code simple instead: keep generating visit_type_FOO_fields() in
QAPISchema visit order, emit necessary forward declarations.

Suggest to factor the code to emit a forward declaration out of
gen_visit_implicit_struct() and reuse it in gen_visit_struct_fields().

> @@ -80,9 +83,9 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e
>
>      if base:
>          ret += mcgen('''
> -    visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err);
> +    visit_type_%(c_type)s_fields(v, (%(c_type)s **)obj, &err);
>  ''',
> -                     c_type=base.c_name(), c_name=c_name('base'))
> +                     c_type=base.c_name())
>          ret += gen_err_check()
>
>      ret += gen_visit_fields(members, prefix='(*obj)->')
> diff --git a/tests/Makefile b/tests/Makefile
> index ef2a19f..b3516ad 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -319,7 +319,6 @@ qapi-schema += returns-array-bad.json
>  qapi-schema += returns-dict.json
>  qapi-schema += returns-unknown.json
>  qapi-schema += returns-whitelist.json
> -qapi-schema += struct-base-clash-base.json
>  qapi-schema += struct-base-clash-deep.json
>  qapi-schema += struct-base-clash.json
>  qapi-schema += struct-data-invalid.json
> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index 1ca7e4f..22e15eb 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -40,6 +40,10 @@
>    'data': { 'string0': 'str',
>              'dict1': 'UserDefTwoDict' } }
>
> +# ensure that we don't have an artificial collision on 'base'
> +{ 'struct': 'UserDefThree',
> +  'base': 'UserDefOne', 'data': { 'base': 'str' } }
> +
>  # dummy struct to force generation of array types not otherwise mentioned
>  { 'struct': 'ForceArrays',
>    'data': { 'unused1':['UserDefOne'], 'unused2':['UserDefTwo'] } }

This is the positive test I challenged above.

> diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
> index 30c3ff0..feaf20d 100644
> --- a/tests/qapi-schema/qapi-schema-test.out
> +++ b/tests/qapi-schema/qapi-schema-test.out
> @@ -153,6 +153,9 @@ object UserDefOptions
>      member u16: uint16List optional=True
>      member i64x: int optional=True
>      member u64x: uint64 optional=True
> +object UserDefThree
> +    base UserDefOne
> +    member base: str optional=False
>  object UserDefTwo
>      member string0: str optional=False
>      member dict1: UserDefTwoDict optional=False
> diff --git a/tests/qapi-schema/struct-base-clash-base.err b/tests/qapi-schema/struct-base-clash-base.err
> deleted file mode 100644
> index e69de29..0000000
> diff --git a/tests/qapi-schema/struct-base-clash-base.exit b/tests/qapi-schema/struct-base-clash-base.exit
> deleted file mode 100644
> index 573541a..0000000
> --- a/tests/qapi-schema/struct-base-clash-base.exit
> +++ /dev/null
> @@ -1 +0,0 @@
> -0
> diff --git a/tests/qapi-schema/struct-base-clash-base.json b/tests/qapi-schema/struct-base-clash-base.json
> deleted file mode 100644
> index 0c84025..0000000
> --- a/tests/qapi-schema/struct-base-clash-base.json
> +++ /dev/null
> @@ -1,9 +0,0 @@
> -# Struct member 'base'
> -# FIXME: this parses, but then fails to compile due to a duplicate 'base'
> -# (one explicit in QMP, the other used to box the base class members).
> -# We should either reject the collision at parse time, or change the
> -# generated struct to allow this to compile.
> -{ 'struct': 'Base', 'data': {} }
> -{ 'struct': 'Sub',
> -  'base': 'Base',
> -  'data': { 'base': 'str' } }
> diff --git a/tests/qapi-schema/struct-base-clash-base.out b/tests/qapi-schema/struct-base-clash-base.out
> deleted file mode 100644
> index e69a416..0000000
> --- a/tests/qapi-schema/struct-base-clash-base.out
> +++ /dev/null
> @@ -1,5 +0,0 @@
> -object :empty
> -object Base
> -object Sub
> -    base Base
> -    member base: str optional=False
> diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
> index bc59835..ea700d8 100644
> --- a/tests/test-qmp-commands.c
> +++ b/tests/test-qmp-commands.c
> @@ -25,11 +25,9 @@ UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a,
>      UserDefOne *ud1d = g_malloc0(sizeof(UserDefOne));
>
>      ud1c->string = strdup(ud1a->string);
> -    ud1c->base = g_new0(UserDefZero, 1);
> -    ud1c->base->integer = ud1a->base->integer;
> +    ud1c->integer = ud1a->integer;
>      ud1d->string = strdup(has_udb1 ? ud1b->string : "blah0");
> -    ud1d->base = g_new0(UserDefZero, 1);
> -    ud1d->base->integer = has_udb1 ? ud1b->base->integer : 0;
> +    ud1d->integer = has_udb1 ? ud1b->integer : 0;
>
>      ret = g_new0(UserDefTwo, 1);
>      ret->string0 = strdup("blah1");
> @@ -176,20 +174,17 @@ static void test_dealloc_types(void)
>      UserDefOneList *ud1list;
>
>      ud1test = g_malloc0(sizeof(UserDefOne));
> -    ud1test->base = g_new0(UserDefZero, 1);
> -    ud1test->base->integer = 42;
> +    ud1test->integer = 42;
>      ud1test->string = g_strdup("hi there 42");
>
>      qapi_free_UserDefOne(ud1test);
>
>      ud1a = g_malloc0(sizeof(UserDefOne));
> -    ud1a->base = g_new0(UserDefZero, 1);
> -    ud1a->base->integer = 43;
> +    ud1a->integer = 43;
>      ud1a->string = g_strdup("hi there 43");
>
>      ud1b = g_malloc0(sizeof(UserDefOne));
> -    ud1b->base = g_new0(UserDefZero, 1);
> -    ud1b->base->integer = 44;
> +    ud1b->integer = 44;
>      ud1b->string = g_strdup("hi there 44");
>
>      ud1list = g_malloc0(sizeof(UserDefOneList));
> diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c
> index 28f146d..035c65c 100644
> --- a/tests/test-qmp-event.c
> +++ b/tests/test-qmp-event.c
> @@ -179,9 +179,7 @@ static void test_event_c(TestEventData *data,
>      QDict *d, *d_data, *d_b;
>
>      UserDefOne b;
> -    UserDefZero z;
> -    z.integer = 2;
> -    b.base = &z;
> +    b.integer = 2;
>      b.string = g_strdup("test1");
>      b.has_enum1 = false;
>
> @@ -209,11 +207,9 @@ static void test_event_d(TestEventData *data,
>  {
>      UserDefOne struct1;
>      EventStructOne a;
> -    UserDefZero z;
>      QDict *d, *d_data, *d_a, *d_struct1;
>
> -    z.integer = 2;
> -    struct1.base = &z;
> +    struct1.integer = 2;
>      struct1.string = g_strdup("test1");
>      struct1.has_enum1 = true;
>      struct1.enum1 = ENUM_ONE_VALUE1;
> diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
> index 8941963..514cfd0 100644
> --- a/tests/test-qmp-input-visitor.c
> +++ b/tests/test-qmp-input-visitor.c
> @@ -262,7 +262,7 @@ static void test_visitor_in_struct_nested(TestInputVisitorData *data,
>
>      check_and_free_str(udp->string0, "string0");
>      check_and_free_str(udp->dict1->string1, "string1");
> -    g_assert_cmpint(udp->dict1->dict2->userdef->base->integer, ==, 42);
> +    g_assert_cmpint(udp->dict1->dict2->userdef->integer, ==, 42);
>      check_and_free_str(udp->dict1->dict2->userdef->string, "string");
>      check_and_free_str(udp->dict1->dict2->string, "string2");
>      g_assert(udp->dict1->has_dict3 == false);
> @@ -292,7 +292,7 @@ static void test_visitor_in_list(TestInputVisitorData *data,
>
>          snprintf(string, sizeof(string), "string%d", i);
>          g_assert_cmpstr(item->value->string, ==, string);
> -        g_assert_cmpint(item->value->base->integer, ==, 42 + i);
> +        g_assert_cmpint(item->value->integer, ==, 42 + i);
>      }
>
>      qapi_free_UserDefOneList(head);
> diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
> index c84002e..88e01ea 100644
> --- a/tests/test-qmp-output-visitor.c
> +++ b/tests/test-qmp-output-visitor.c
> @@ -250,16 +250,14 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
>      ud2->dict1->dict2 = g_malloc0(sizeof(*ud2->dict1->dict2));
>      ud2->dict1->dict2->userdef = g_new0(UserDefOne, 1);
>      ud2->dict1->dict2->userdef->string = g_strdup(string);
> -    ud2->dict1->dict2->userdef->base = g_new0(UserDefZero, 1);
> -    ud2->dict1->dict2->userdef->base->integer = value;
> +    ud2->dict1->dict2->userdef->integer = value;
>      ud2->dict1->dict2->string = g_strdup(strings[2]);
>
>      ud2->dict1->dict3 = g_malloc0(sizeof(*ud2->dict1->dict3));
>      ud2->dict1->has_dict3 = true;
>      ud2->dict1->dict3->userdef = g_new0(UserDefOne, 1);
>      ud2->dict1->dict3->userdef->string = g_strdup(string);
> -    ud2->dict1->dict3->userdef->base = g_new0(UserDefZero, 1);
> -    ud2->dict1->dict3->userdef->base->integer = value;
> +    ud2->dict1->dict3->userdef->integer = value;
>      ud2->dict1->dict3->string = g_strdup(strings[3]);
>
>      visit_type_UserDefTwo(data->ov, &ud2, "unused", &err);
> @@ -301,8 +299,7 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
>                                             const void *unused)
>  {
>      EnumOne bad_values[] = { ENUM_ONE_MAX, -1 };
> -    UserDefZero b;
> -    UserDefOne u = { .base = &b }, *pu = &u;
> +    UserDefOne u, *pu = &u;
>      Error *err;
>      int i;
>

With the fixup squashed in, this becomes:

                                              const void *unused)
   {
       EnumOne bad_values[] = { ENUM_ONE_MAX, -1 };
  -    UserDefZero b;
  -    UserDefOne u = { .base = &b }, *pu = &u;
  +    UserDefOne u = {0};
  +    UserDefOne *pu = &u;
       Error *err;
       int i;

> @@ -416,8 +413,7 @@ static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data,
>          p->value->dict1->dict2 = g_new0(UserDefTwoDictDict, 1);
>          p->value->dict1->dict2->userdef = g_new0(UserDefOne, 1);
>          p->value->dict1->dict2->userdef->string = g_strdup(string);
> -        p->value->dict1->dict2->userdef->base = g_new0(UserDefZero, 1);
> -        p->value->dict1->dict2->userdef->base->integer = 42;
> +        p->value->dict1->dict2->userdef->integer = 42;
>          p->value->dict1->dict2->string = g_strdup(string);
>          p->value->dict1->has_dict3 = false;
>
> diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
> index fa86cae..634563b 100644
> --- a/tests/test-visitor-serialization.c
> +++ b/tests/test-visitor-serialization.c
> @@ -258,15 +258,13 @@ static UserDefTwo *nested_struct_create(void)
>      udnp->dict1->string1 = strdup("test_string1");
>      udnp->dict1->dict2 = g_malloc0(sizeof(*udnp->dict1->dict2));
>      udnp->dict1->dict2->userdef = g_new0(UserDefOne, 1);
> -    udnp->dict1->dict2->userdef->base = g_new0(UserDefZero, 1);
> -    udnp->dict1->dict2->userdef->base->integer = 42;
> +    udnp->dict1->dict2->userdef->integer = 42;
>      udnp->dict1->dict2->userdef->string = strdup("test_string");
>      udnp->dict1->dict2->string = strdup("test_string2");
>      udnp->dict1->dict3 = g_malloc0(sizeof(*udnp->dict1->dict3));
>      udnp->dict1->has_dict3 = true;
>      udnp->dict1->dict3->userdef = g_new0(UserDefOne, 1);
> -    udnp->dict1->dict3->userdef->base = g_new0(UserDefZero, 1);
> -    udnp->dict1->dict3->userdef->base->integer = 43;
> +    udnp->dict1->dict3->userdef->integer = 43;
>      udnp->dict1->dict3->userdef->string = strdup("test_string");
>      udnp->dict1->dict3->string = strdup("test_string3");
>      return udnp;
> @@ -278,15 +276,15 @@ static void nested_struct_compare(UserDefTwo *udnp1, UserDefTwo *udnp2)
>      g_assert(udnp2);
>      g_assert_cmpstr(udnp1->string0, ==, udnp2->string0);
>      g_assert_cmpstr(udnp1->dict1->string1, ==, udnp2->dict1->string1);
> -    g_assert_cmpint(udnp1->dict1->dict2->userdef->base->integer, ==,
> -                    udnp2->dict1->dict2->userdef->base->integer);
> +    g_assert_cmpint(udnp1->dict1->dict2->userdef->integer, ==,
> +                    udnp2->dict1->dict2->userdef->integer);
>      g_assert_cmpstr(udnp1->dict1->dict2->userdef->string, ==,
>                      udnp2->dict1->dict2->userdef->string);
>      g_assert_cmpstr(udnp1->dict1->dict2->string, ==,
>                      udnp2->dict1->dict2->string);
>      g_assert(udnp1->dict1->has_dict3 == udnp2->dict1->has_dict3);
> -    g_assert_cmpint(udnp1->dict1->dict3->userdef->base->integer, ==,
> -                    udnp2->dict1->dict3->userdef->base->integer);
> +    g_assert_cmpint(udnp1->dict1->dict3->userdef->integer, ==,
> +                    udnp2->dict1->dict3->userdef->integer);
>      g_assert_cmpstr(udnp1->dict1->dict3->userdef->string, ==,
>                      udnp2->dict1->dict3->userdef->string);
>      g_assert_cmpstr(udnp1->dict1->dict3->string, ==,
> diff --git a/ui/spice-core.c b/ui/spice-core.c
> index bf4fd07..86f4441 100644
> --- a/ui/spice-core.c
> +++ b/ui/spice-core.c
> @@ -200,8 +200,6 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
>  {
>      SpiceServerInfo *server = g_malloc0(sizeof(*server));
>      SpiceChannel *client = g_malloc0(sizeof(*client));
> -    server->base = g_malloc0(sizeof(*server->base));
> -    client->base = g_malloc0(sizeof(*client->base));
>
>      /*
>       * Spice server might have called us from spice worker thread
> @@ -218,9 +216,11 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
>      }
>
>      if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
> -        add_addr_info(client->base, (struct sockaddr *)&info->paddr_ext,
> +        add_addr_info((SpiceBasicInfo *)client,

Ah, you're already exploiting the ability to cast to the base type!

Idea: should we generate a type-safe macro or inline function for this?

> +                      (struct sockaddr *)&info->paddr_ext,
>                        info->plen_ext);
> -        add_addr_info(server->base, (struct sockaddr *)&info->laddr_ext,
> +        add_addr_info((SpiceBasicInfo *)server,
> +                      (struct sockaddr *)&info->laddr_ext,
>                        info->llen_ext);
>      } else {
>          error_report("spice: %s, extended address is expected",
> @@ -229,7 +229,9 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
>
>      switch (event) {
>      case SPICE_CHANNEL_EVENT_CONNECTED:
> -        qapi_event_send_spice_connected(server->base, client->base, &error_abort);
> +        qapi_event_send_spice_connected((SpiceBasicInfo *)server,
> +                                        (SpiceBasicInfo *)client,
> +                                        &error_abort);
>          break;
>      case SPICE_CHANNEL_EVENT_INITIALIZED:
>          if (auth) {
> @@ -242,7 +244,9 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
>          break;
>      case SPICE_CHANNEL_EVENT_DISCONNECTED:
>          channel_list_del(info);
> -        qapi_event_send_spice_disconnected(server->base, client->base, &error_abort);
> +        qapi_event_send_spice_disconnected((SpiceBasicInfo *)server,
> +                                           (SpiceBasicInfo *)client,
> +                                           &error_abort);
>          break;
>      default:
>          break;
> @@ -378,16 +382,15 @@ static SpiceChannelList *qmp_query_spice_channels(void)
>
>          chan = g_malloc0(sizeof(*chan));
>          chan->value = g_malloc0(sizeof(*chan->value));
> -        chan->value->base = g_malloc0(sizeof(*chan->value->base));
>
>          paddr = (struct sockaddr *)&item->info->paddr_ext;
>          plen = item->info->plen_ext;
>          getnameinfo(paddr, plen,
>                      host, sizeof(host), port, sizeof(port),
>                      NI_NUMERICHOST | NI_NUMERICSERV);
> -        chan->value->base->host = g_strdup(host);
> -        chan->value->base->port = g_strdup(port);
> -        chan->value->base->family = inet_netfamily(paddr->sa_family);
> +        chan->value->host = g_strdup(host);
> +        chan->value->port = g_strdup(port);
> +        chan->value->family = inet_netfamily(paddr->sa_family);
>
>          chan->value->connection_id = item->info->connection_id;
>          chan->value->channel_type = item->info->type;
> diff --git a/ui/vnc.c b/ui/vnc.c
> index 61af4ba..e7c29fe 100644
> --- a/ui/vnc.c
> +++ b/ui/vnc.c
> @@ -261,8 +261,7 @@ static VncServerInfo *vnc_server_info_get(VncDisplay *vd)
>      VncServerInfo *info;
>
>      info = g_malloc(sizeof(*info));
> -    info->base = g_malloc(sizeof(*info->base));
> -    vnc_basic_info_get_from_server_addr(vd->lsock, info->base, NULL);
> +    vnc_basic_info_get_from_server_addr(vd->lsock, (VncBasicInfo *)info, NULL);
>      info->has_auth = true;
>      info->auth = g_strdup(vnc_auth_name(vd));
>      return info;
> @@ -293,8 +292,8 @@ static void vnc_client_cache_addr(VncState *client)
>  {
>      Error *err = NULL;
>      client->info = g_malloc0(sizeof(*client->info));
> -    client->info->base = g_malloc0(sizeof(*client->info->base));
> -    vnc_basic_info_get_from_remote_addr(client->csock, client->info->base,
> +    vnc_basic_info_get_from_remote_addr(client->csock,
> +                                        (VncBasicInfo *)client->info,
>                                          &err);
>      if (err) {
>          qapi_free_VncClientInfo(client->info);
> @@ -310,7 +309,6 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event)
>      if (!vs->info) {
>          return;
>      }
> -    g_assert(vs->info->base);
>
>      si = vnc_server_info_get(vs->vd);
>      if (!si) {
> @@ -319,7 +317,8 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event)
>
>      switch (event) {
>      case QAPI_EVENT_VNC_CONNECTED:
> -        qapi_event_send_vnc_connected(si, vs->info->base, &error_abort);
> +        qapi_event_send_vnc_connected(si, (VncBasicInfo *)vs->info,
> +                                      &error_abort);
>          break;
>      case QAPI_EVENT_VNC_INITIALIZED:
>          qapi_event_send_vnc_initialized(si, vs->info, &error_abort);
> @@ -354,11 +353,10 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client)
>      }
>
>      info = g_malloc0(sizeof(*info));
> -    info->base = g_malloc0(sizeof(*info->base));
> -    info->base->host = g_strdup(host);
> -    info->base->service = g_strdup(serv);
> -    info->base->family = inet_netfamily(sa.ss_family);
> -    info->base->websocket = client->websocket;
> +    info->host = g_strdup(host);
> +    info->service = g_strdup(serv);
> +    info->family = inet_netfamily(sa.ss_family);
> +    info->websocket = client->websocket;
>
>      if (client->tls) {
>          info->x509_dname = qcrypto_tls_session_get_peer_name(client->tls);

Turned out nicely, I anticipated more churn.

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

* Re: [Qemu-devel] [PATCH v9 04/17] vnc: hoist allocation of VncBasicInfo to callers
  2015-10-20  8:54     ` Gerd Hoffmann
@ 2015-10-20 14:46       ` Markus Armbruster
  2015-10-20 22:53         ` Eric Blake
  0 siblings, 1 reply; 57+ messages in thread
From: Markus Armbruster @ 2015-10-20 14:46 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

Gerd Hoffmann <kraxel@redhat.com> writes:

>   Hi,
>
>> > -static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa,
>> > -                                        socklen_t salen)
>> > +static void vnc_basic_info_get(struct sockaddr_storage *sa,
>> > +                               socklen_t salen,
>> > +                               VncBasicInfo *info,
>> > +                               Error **errp)
>
>> The function no longer "gets info", it merely initializes it.  Rename it
>> accordingly?  Gerd?
>
> vnc_fill_basic_info maybe?

Fine with me.  Could also call it _init_ instead of _fill_.

>> Outside this patch's scope, but since I'm looking at the code already...
>> 
>> Here's the only caller of vnc_server_info_get():
>> 
>>     static void vnc_qmp_event(VncState *vs, QAPIEvent event)
>>     {
>>         VncServerInfo *si;
>> 
>>         if (!vs->info) {
>>             return;
>>         }
>>         g_assert(vs->info->base);
>> 
>>         si = vnc_server_info_get(vs->vd);
>>         if (!si) {
>> --->        return;
>>         }
>> 
>>         switch (event) {
>>         case QAPI_EVENT_VNC_CONNECTED:
>>             qapi_event_send_vnc_connected(si, vs->info->base, &error_abort);
>>             break;
>>         case QAPI_EVENT_VNC_INITIALIZED:
>>             qapi_event_send_vnc_initialized(si, vs->info, &error_abort);
>>             break;
>>         case QAPI_EVENT_VNC_DISCONNECTED:
>>             qapi_event_send_vnc_disconnected(si, vs->info, &error_abort);
>>             break;
>>         default:
>>             break;
>>         }
>> 
>>         qapi_free_VncServerInfo(si);
>>     }
>> 
>> When vnc_server_info_get() fails, the event is dropped.  Why is that
>> okay?  Failure seems unlikely, though.
>
> Suggestions what else to do?  I don't wanna crash qemu by calling
> qapi_event_send_vnc_* with a NULL pointer.  And, yes, it should be
> highly unlikely so trying some more sophisticated error handling would
> probably be dead code ...

These events signal a state change.  Dropping them make me feel uneasy,
because if a client uses them to track state, it gets out of sync.

What's the state machine here?  If I understand things correctly, we
have a number of VNC servers, tracked in vnc_displays, which is a list
of VncDisplay.  Each of them goes through the following states:

* Unconnected
  On connect, emit VNC_CONNECTED and go to Pre-auth

* Pre-auth
  On authentication, emit VNC_INITIALIZED, and go to Active
  On disconnect, emit VNC_DISCONNECTED, and go to Unconnected

* Active
  On disconnect, emit VNC_DISCONNECTED, and go to Unconnected

Correct?

The events need to identify the server to be of any use for state
tracking.  Currently, this is members host, service, family of data
member server.

We could avoid failures in vnc_qmp_event() as follows:

1. When we create a server, we obtain its info with getsockname() and
   getnameinfo().  If they fail, we fail server creation.  Else, we
   store the info for vnc_qmp_event().

2. When a client connects, we obtain its info with getpeername() and
   getnameinfo().  If they fail, we refuse the connection.  Else, we
   store the infor for vnc_qmp_event().

Alternatively, we can embrace the failures and send the events without
the information we failed to get.  Requires another way to identify the
server.  VncDisplay's id, perhaps?  Might be nice to have anyway.

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

* Re: [Qemu-devel] [PATCH v9 05/17] qapi: Unbox base members
  2015-10-20 12:09   ` [Qemu-devel] [PATCH v9 05/17] " Markus Armbruster
@ 2015-10-20 16:08     ` Eric Blake
  2015-10-21 13:34       ` Markus Armbruster
  0 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-20 16:08 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Luiz Capitulino, Gerd Hoffmann, qemu-devel, Michael Roth

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

On 10/20/2015 06:09 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Rather than storing a base class as a pointer to a box, just
>> store the fields of that base class in the same order, so that
>> a child struct can be safely cast to its parent.

^^^^

>> Compare to the earlier commit 1e6c1616a "qapi: Generate a nicer
>> struct for flat unions".
> 
> Should we mention the struct can be cast to its base?

As in the ^^^ sentence above? Or did you mean somewhere additionally in
the comments in the code below?

>> -def gen_struct_fields(members):
>> +def gen_struct_fields(members, base, nested=False):
>>      ret = ''
>> +    if base:
>> +        if not nested:
>> +            ret += mcgen('''
>> +    /* Members inherited from %(c_name)s: */
>> +''',
>> +                         c_name=base.c_name())
>> +        ret += gen_struct_fields(base.local_members, base.base, True)
>> +        if not nested:
>> +            ret += mcgen('''
>> +    /* Own members: */
>> +''')
> 
> Before: gen_struct_fields() emits *own* fields.
> 
> After: it emits *all* fields.
> 
> Since the signature changes, all callers are visible in the patch, and
> can be reviewed.
> 
> Code looks a bit awkward, but I don't have better ideas.  Perhaps we can
> do better when we fuse gen_struct() and gen_union().

I haven't tried to do that fusion yet; but we are probably quite close
to it.  I'll see whether it is worth adding on as an 18/17 in this
subset of the series, or saving for later.

> This is gen_union().
> 
>>  ''',
>>                  c_name=c_name(name))
>>      if base:
>> -        ret += mcgen('''
>> -    /* Members inherited from %(c_name)s: */
>> -''',
>> -                     c_name=c_name(base.name))
>> -        ret += gen_struct_fields(base.members)
>> -        ret += mcgen('''
>> -    /* Own members: */
>> -''')
>> +        ret += gen_struct_fields([], base)
>>      else:
>>          ret += mcgen('''
>>      %(c_type)s kind;
> 
> Before: emit base fields, then own fields.
> 
> After: emit all fields.  Good, but please mention in the commit message
> that you're touching gen_union().  You could also do this part in a
> separate patch, but that's probably overkill.

Sure, I can improve the commit message, and maybe split the patch. The
idea at play is that both structs and unions want to emit all fields, as
well as strategic comments about which fields are inherited, so a shared
function is ideal for that; this patch moved code from gen_union() into
gen_struct_fields() so that gen_struct() could reuse it.

>>      if base:
>> -        ret += gen_visit_implicit_struct(base)
>> +        ret += gen_visit_struct_fields(base.name, base.base,
>> +                                       base.local_members)
>>
>>      ret += mcgen('''
>>
> 
> This change looks innocent enough on first glance, but it's actually
> quite hairy.
> 
> = Before =
> 
> The visit_type_FOO_fields() are generated in QAPISchema visit order,
> i.e. right when QAPISchemaObjectType 'FOO' is visited.
> 
> The visit_type_implicit_FOO() are generated on demand, right before
> their first use.  It's used by visit_type_STRUCT_fields() when STRUCT
> has base FOO, and by visit_type_UNION() when flat UNION has a member
> FOO.
> 
> Aside: only flat unions, because simple unions are an ugly special case
> here.  To be unified.
> 
> Generating visit_type_implicit_FOO() on demand makes sense, because the
> function isn't always needed.
> 
> Since visit_type_implicit_FOO() calls visit_type_FOO_fields(), and the
> former can be generated before the latter, we may need a forward
> declaration.  struct_fields_seen is used to detect this.  See commit
> 8c3f8e7.
> 
> = After =
> 
> visit_type_implicit_FOO() is now used only when flat UNION has a member
> FOO.  May need a forward declaration of visit_type_FOO_fields() anyway.
> 
> visit_type_FOO_fields() is now also generated on demand, right before
> first use other than visit_type_implicit_FOO().  Weird.
> 
> The resulting code motion makes the change to generated code difficult
> to review.
> 
> Generating visit_type_FOO_fields() on demand makes less sense, because
> the function is always needed.  All it can accomplish is saving a few
> forward declarations, at the cost of making gen_visit_struct_fields()
> recursive, which begs the recursion termination question, and less
> uniform generated code.
> 
> The naive answer to the recursion termination question is that types
> can't contain themselves, therefore we can't ever get into a cycle.
> That begs the next question: why do we need the if name in
> struct_fields_seen conditional then?  We do need it (turning it into an
> assertion promptly fails it), but I can't tell why offhand.
> 
> I'm sure I could figure out why this works, but I don't want to.  Let's
> keep the code simple instead: keep generating visit_type_FOO_fields() in
> QAPISchema visit order, emit necessary forward declarations.
> 
> Suggest to factor the code to emit a forward declaration out of
> gen_visit_implicit_struct() and reuse it in gen_visit_struct_fields().

Sounds like I need to split this patch then, anyways.  I'll see what I
can come up with for v10.


>> +++ b/tests/qapi-schema/qapi-schema-test.json
>> @@ -40,6 +40,10 @@
>>    'data': { 'string0': 'str',
>>              'dict1': 'UserDefTwoDict' } }
>>
>> +# ensure that we don't have an artificial collision on 'base'
>> +{ 'struct': 'UserDefThree',
>> +  'base': 'UserDefOne', 'data': { 'base': 'str' } }
>> +

> This is the positive test I challenged above.

I can drop it if you don't like it; I felt better with it, but as you
say, it only proves that 'base' is usable as a member name, and not that
someone won't pick some other name when refactoring back into a boxed
base.  See also my comments below about using containers (rather than
boxes or inlined members), where we may need to deal with the naming
clash anyways.

>> @@ -218,9 +216,11 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
>>      }
>>
>>      if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
>> -        add_addr_info(client->base, (struct sockaddr *)&info->paddr_ext,
>> +        add_addr_info((SpiceBasicInfo *)client,
> 
> Ah, you're already exploiting the ability to cast to the base type!

Absolutely :)

> 
> Idea: should we generate a type-safe macro or inline function for this?

Hmm. DO_UPCAST() (and its more powerful underlying container_of())
doesn't fit here, because we inlined the fields rather than directly
including the base.

Below, I'm using this qapi:
{'struct':'Parent', 'data':{'i':'int'}}
{'struct':'Child', 'base':'Parent', 'data':{'b':'bool'}}

What we have without this patch is a box that requires separate
allocation (two layers of pointers, the boxing that we don't want):

struct Child {
    Parent *base;
    bool b;
};

And this patch takes things to be completely inlined (since that's what
we did earlier with flat unions), so that there is no intermediate
structure in the child (and thus nothing for DO_UPCAST() to grab):

struct Child {
    /* Inherited fields from Parent */
    int i;
    /* own fields */
    bool b;
};

But there is a third possible layout (if we do it, we should do it for
flat unions as well), which is using the base as a container rather than
a box, where at least we don't need separate allocation:

struct Child {
    Parent base;
    bool b;
};

or similarly, but in longhand:

struct Child {
    struct {
        int i;
    } base;
    bool b;
};

but then we are back to having to avoid a collision with the C name
'base' with a QMP name in own fields, and all client code has to spell
out child->base.i (instead of the boxed child->base->i, or the inlined
child->i).

There's also the ugly approach of exposing things in a dual naming
system via an anonymous union and struct:

struct Child {
    union {
        struct {
            int i;
        };
        Parent base;
    };
    bool b;
};

which would allow 'child->i' to be the same storage as 'child->base.i',
so that clients can use the short spelling when accessing fields but
also have handy access to the base member for DO_UPCAST().  I'm not sure
I want to go there, though.


Taking your idea from another review message, you mentioned that we
could allow 'base' by tweaking c_name() to munge the user's member
'base' into 'q_base', while reserving 'base' for ourselves; or we could
use '_base' for our own use, which shouldn't collide with user's names
(user names should not start with underscore except for downstream names
which have double-underscore).  Or use '_b' instead of 'base' - the
point remains that if we want to avoid a collision but still use the
container approach, we could pick the C name appropriately.  But
ultimately, we should either commit to the name munging pattern, or
outlaw the name that we plan to use internally, or stick to inlined
members that don't require munging in the first place.

Meanwhile, if we decide that we like the convenience of inlined data,
you are correct in the idea that we could have the qapi generator
automatically spit out an appropriate type-specific upcast inline
function for each type with a base.  So given the same example, this
might mean:

static inline Parent *qapi_Child_to_Parent(Child *child) {
    return (Parent*)child;
}

But while such a representation would add compiler type-safety (hiding
the cast in generated code, where we can prove the generator knew it was
safe, and so that clients don't have to cast), it also adds verbosity.
I can't think of any representation that would be shorter than making
the clients do the cast, short of using a container rather than inline
approach.  Even foo(qapi_baseof_Child(child), blah) is longer than
foo((Parent *)child, blah).

Preferences?

> Turned out nicely, I anticipated more churn.

Yes, that was my feeling as well - we haven't quite used base classes in
enough places to make the conversion painful - but the longer we wait,
the more usage of base classes will sneak into .json files making a
future conversion to a new layout more painful.

-- 
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] 57+ messages in thread

* Re: [Qemu-devel] [PATCH v9 01/17] qapi: Add tests for reserved name abuse
  2015-10-19 16:05   ` Markus Armbruster
@ 2015-10-20 16:23     ` Eric Blake
  2015-10-21 12:08       ` Markus Armbruster
  0 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-20 16:23 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 10/19/2015 10:05 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> A future patch wants to change qapi union representation from
>> an anonymous C union over to a named C union 'u', so that the
>> C names of tag values are in a separate namespace and thus
>> cannot collide with the C names of non-variant QMP members.
>> But for that to not cause any problems with future extensions
>> to existing qapi, it would be best if we prohibit 'u' as a
>> member name everywhere, to reserve it for our internal use.
>> (Remember that although 'u' would only actually collide in
>> flat unions, we must also worry about the fact that it is
>> possible to convert from a struct to a flat union in a future
>> qemu version while remaining backwards-compatible in QMP.)
> 
> This part is awkward: we're adding a negative test that fails to fail
> for use of a name that isn't actually reserved until two patches later.
> 
> I guess I'd move the 'u' tests to the patch that reserves the name.  Or
> at least start the commit message with one of the non-awkward cases :)

If you are okay with it, I can squash this test into commits 2 and 3, so
that we aren't introducing tests and changing state later, but instead
just adding tests at the time I reserve namespace.

> I started to write that you might want to mention we already reserve the
> 'Kind' suffix, then noticed you do in PATCH 02.  No need to say it
> twice.

Especially if I take the approach of not having a separate patch 1 that
just adds tests.

Sometimes, I find it easier to add tests showing one behavior, then flip
the switch to show how the new behavior changes the behavior.  But I can
be persuaded that it is just churn, and that adding the new tests at the
same time as the new behavior is just as effective. :)

> 
>> On the other hand, there is no reason to forbid type name
>> patterns from member names, or member name patterns from types,
>> since the two are not in the same namespace in C and won't
>> collide.
> 
> However, we could *choose* to enforce a single name space for
> simplicity.  By convention, type names are StudlyCaps (except for
> built-ins), and member names are dashed-lower-case, so collisions are
> unlikely anyway.
> 
> Perhaps you should write "there is no technical reason".

Well, I _do_ have a possible technical reason in mind: we've already
mentioned that we may want to someday support automatic '-'/'_' munging;
and it is not too much of a further jump to support case-insensitive
matching (at least on a US keyboard, - and _ are on the same key the
same, so it is already akin to a case conversion to go between the two).
 With case-insensitive member names, we then have collisions between a
MyType type name and a mytype member (where the QMP client can still
access the member via MyType).  But yeah, I can revisit the wording of
this paragraph.

> Since this is a test-only patch, I'd prefix the subject with
> "tests/qapi-schema:" instead of "qapi:".

Unless I split and squash it with other patches, of course.


>> +++ b/tests/qapi-schema/args-name-has.json
>> @@ -0,0 +1,6 @@
>> +# C member name collision
>> +# FIXME - This parses, but fails to compile, because the C struct is given
>> +# two 'has_a' members, one from the flag for optional 'a', and the other
>> +# from member 'has-a'.  Either reject this at parse time, or munge the C
>> +# names to avoid the collision.
>> +{ 'command': 'oops', 'data': { '*a': 'str', 'has-a': 'str' } }
> 
> Complements existing args-name-clash.json, which tests 'a-b' and 'a_b'.
> 
> Call it args-has-clash.json?

Sure, can do.

>> +++ b/tests/qapi-schema/flat-union-clash-branch.json
>> @@ -1,18 +1,15 @@
>>  # Flat union branch name collision
>> -# FIXME: this parses, but then fails to compile due to a duplicate 'c_d'
>> -# (one from the base member, the other from the branch name).  We should
>> -# either reject the collision at parse time, or munge the generated branch
>> -# name to allow this to compile.
>> +# FIXME: this parses, but then fails to compile due to a duplicate 'has_a'
>> +# (one as the implicit flag for the optional base member, the other from
>> +# the C member for the branch name).  We should either reject the collision
>> +# at parse time, or munge the generated branch name to allow this to compile.
>>  { 'enum': 'TestEnum',
>> -  'data': [ 'base', 'c-d' ] }
>> +  'data': [ 'has-a' ] }
>>  { 'struct': 'Base',
>> -  'data': { 'enum1': 'TestEnum', '*c_d': 'str' } }
>> +  'data': { 'enum1': 'TestEnum', '*a': 'str' } }
>>  { 'struct': 'Branch1',
>>    'data': { 'string': 'str' } }
>> -{ 'struct': 'Branch2',
>> -  'data': { 'value': 'int' } }
>>  { 'union': 'TestUnion',
>>    'base': 'Base',
>>    'discriminator': 'enum1',
>> -  'data': { 'base': 'Branch1',
>> -            'c-d': 'Branch2' } }
>> +  'data': { 'has-a': 'Branch1' } }
> 
> This replaces the test of branch name 'c-d' clashing with member 'c_d'
> by a test of branch name 'has-a' clashing with the has_a flag of
> optional member 'a'.  Okay, since flat-union-clash-type.json covers
> clash of branch name with member name.

And since the test disappears completely later in my pending work, once
I change the layout to use a named union.  (See subset C 6/14
https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg04111.html)

>> +# Even if 'u' is forbidden as a struct member name, it should still be
>> +# valid as a type or union branch name. And although '*Kind' is forbidden
>> +# as a type name, it should not be forbidden as a member or branch name.
>> +# TODO - '*List' should also be forbidden as a type name, and 'has_*' as
>> +# a member name.
>> +{ 'struct': 'has_a', 'data': { 'MyKind': 'int', 'MyList': ['int'] } }
>> +{ 'union': 'u', 'data': { 'u': 'uint8', 'myKind': 'has_a',
>> +                          'myList': 'has_a' } }
>> +
>>  # testing commands
>>  { 'command': 'user_def_cmd', 'data': {} }
>>  { 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} }
> 
> Value of these positive tests seems marginal.  But if you think they're
> worth keeping, I'll take them.

I added even more of these positive tests in subset C.  And then I
noticed that you've made the same comment about the positive test I
added in patch 4/17.  I can go either way (the added positive tests
helped me ensure that things compiled while writing patches, but I won't
be offended to omit them if you don't think they add enough to keep
long-term).

>> +++ b/tests/qapi-schema/struct-member-u.json
>> @@ -0,0 +1,6 @@
>> +# Potential C member name collision
>> +# FIXME - This parses and compiles, but would cause a collision if this
>> +# struct is later reworked to be part of a flat union, once unions hide
>> +# their tag values under a C union named 'u'. We should reject the use
>> +# of this identifier to reserve it for internal use.
>> +{ 'struct': 'Oops', 'data': { 'u': 'str' } }
> 
> If the later patch outlaws 'u' in structs as well, this test will do,
> only the comment will change.
> 
> If it outlaws 'u' only where it actually clashes, namely in unions, the
> test will need updating.
> 
> More reason to move the test to the patch that does the outlawing.

Fair enough. And yes, I outlawed 'u' everywhere, not just in unions, on
the grounds that it is possible to convert a struct to a union while
remaining backwards-compatible in the QMP that it accepts; the act of
converting between object types must not invalidate an existing object
member, so if we reserve a member name, we should reserve it for all
objects and not just qapi unions.

-- 
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] 57+ messages in thread

* Re: [Qemu-devel] [PATCH v9 02/17] qapi: Reserve '*List' type names for arrays
  2015-10-19 16:14   ` Markus Armbruster
@ 2015-10-20 18:12     ` Eric Blake
  0 siblings, 0 replies; 57+ messages in thread
From: Eric Blake @ 2015-10-20 18:12 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 10/19/2015 10:14 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> We already reserved '*Kind' for implicit enums (since commit
> 
> "We reserved '*Kind' since" sounds odd.  Present tense?
> 
>> 4dc2e69), but failed to reserve '*List' for array types.  Since
>> no QMP was yet using it, we might as well make the reservation
>> official.
> 
> Uhm, how can we fail to reserve something, and then make the reservation
> official?  I think what you're trying to say is something like
> 
>     Type names ending in 'List' can clash with array types in generated
>     C.  We don't currently use such names.  Outlaw them.
> 
>     Precedence: commit 4dc2e69 outlawed names ending in 'Kind', because
>     they can clash with implicit enum types of unions.
> 

Okay, your wording has some improvements.  I'll take those ideas and try
again.

>> Note that this forbids creation of new types or commands ending
>> in 'List', but does NOT forbid abuse of attempts like ['intList']
>> for creating a 2-dimensional array of 'int'.
> 
> Really?  If I write ['T'], then T must be a declared type, mustn't it?
> And after this patch, I cant declare type intList, can I?

You can't declare { 'struct': 'intList' }, but I was worried whether you
can abuse the type system by using { 'struct':'S', 'data': {
'two-d':['intList'] } }.  But I guess I should actually test things
rather than trying to mis-remember past conversations; a quick test shows:

/home/eblake/qemu/tests/qapi-schema/qapi-schema-test.json:10: Member
'two-d' of 'data' for struct 'S' uses unknown type 'intList'

so what we were experimenting with earlier was [['int']] and not
['intList'].  I'll update the paragraph accordingly.

> 
>>                                               The whole idea of
>> multi-dimensional arrays needs more design thought, best left for
>> another day.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
> 
> Patch looks good to me.
> 

-- 
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] 57+ messages in thread

* Re: [Qemu-devel] [PATCH v9 03/17] qapi: Reserve 'u' and 'has[-_]*' member names
  2015-10-19 17:19   ` Markus Armbruster
@ 2015-10-20 21:29     ` Eric Blake
  2015-10-21 13:08       ` Markus Armbruster
  0 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-20 21:29 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 10/19/2015 11:19 AM, Markus Armbruster wrote:

> I'm not quite comfortable with reserving 'u' now, becaue I feel we
> haven't fully explored the design space for avoiding branch - member
> clashes.
> 
> I still like the basic idea to give the unnamed union a name.  It needs
> to be a short one, to keep the C code legible.  'u' is an obvious
> option, but it requires reserving 'u' at least as member name.  '_u'
> wouldn't.  Alternatively, call the union 'u', but avoid the clash by
> mapping QAPI member name 'u' to C identifier '_u'.

Naming the union '_u' is a bit uglier (more typing in every client)
whereas munging just the member name (what about 'q_u', the way c_name
appends 'q_' to any other name that would otherwise collide) pushes the
ugliness only to the C code that actually uses a member named 'u'.

But the idea of teaching c_name() to munge 'u' as a member name
certainly seems doable, at which point we no longer need to reserve 'u'
as a member name.

> 
> I feel the decision should be made over the patch that give the union a
> name.

Well, that's patch 7/17 of this series, so at most, all I need to do is
shuffle things around when rebasing for v10.

For that matter, if we make c_name() munge 'u', it can just as easily
munge a member named 'has_' to 'q_has' (or 'base' to 'q_base' - except
that we are getting rid of that); or whatever other names we burn for
convenience on the C side.

But no one is using a member named 'u' at the moment, so it's not the
most critical problem to solve; and forbidding it is certainly
conservative (we can relax things to allow the name in QMP after all,
once we figure out the appropriate munging for the C side).


>> +++ b/scripts/qapi.py
>> @@ -488,6 +488,10 @@ def check_type(expr_info, source, value, allow_array=False,
>>      for (key, arg) in value.items():
>>          check_name(expr_info, "Member of %s" % source, key,
>>                     allow_optional=allow_optional)
>> +        if key == 'u' or key.startswith('has-') or key.startswith('has_'):
> 
> Something like c_name(key).startswith('has_') would avoid hardcoding the
> mapping of '-' to '_' here.  Dunno.

Oh, nice idea.

And looking at that, we have a number of places in qapi.py that are
using things like str[-4:] == '....' that might look nicer as
str.endwith('....'). I may add an obvious trivial cleanup patch into the
mix.


>> @@ -588,6 +592,14 @@ def check_union(expr, expr_info):
>>      # Check every branch
>>      for (key, value) in members.items():
>>          check_name(expr_info, "Member of union '%s'" % name, key)
>> +        # TODO: As long as branch names can collide with QMP names, we
>> +        # must prevent branches starting with 'has_'. However, we do not
>> +        # need to reject 'u', because that is reserved for when we start
>> +        # sticking branch names in a C union named 'u'.
>> +        if key.startswith('has-') or key.startswith('has_'):
>> +            raise QAPIExprError(expr_info,
>> +                                "Branch of union '%s' uses reserved name '%s'"
>> +                                % (name, key))
> 
> This will go away again when we give the unnamed union a name.
> 
> I feel we should punt all further clash detection until late in the
> cleanup work.  It's merely nice to have (sane error message from
> generator instead of possibly confusing one from the C compiler,
> basically), and adding it now causes churn later on.

Okay, I can respin along those lines - if my work later in the series
removes a negative test added earlier in the series, then strip that
test from the series as a whole rather than fighting the churn, to
reduce the size of the series.


>> +++ b/tests/qapi-schema/args-name-has.json
>> @@ -1,6 +1,5 @@
>>  # C member name collision
>> -# FIXME - This parses, but fails to compile, because the C struct is given
>> -# two 'has_a' members, one from the flag for optional 'a', and the other
>> -# from member 'has-a'.  Either reject this at parse time, or munge the C
>> -# names to avoid the collision.
>> +# This would attempt to create two 'has_a' members of the C struct, one
>> +# from the flag for optional 'a', and the other from member 'has-a'.
>> +# TODO we could munge the optional flag name to avoid the collision.
> 
> You mean call them _has_FOO instead of has_FOO?  The generated code
> would be rather confusing...
> 
> If we don't want to reserve all names starting with 'has_', then I'd
> narrowly outlaw having both an optional member FOO and a member has_FOO.
> I think I'd like that a bit better than outlawing 'has_'.  But not
> enough to accept much implementation complexity.

The problem comes with child classes - we don't know a priori if an
optional member in one struct will end up being a base class to another
struct or union where the child class will hit the name clash.  It's
easier to outlaw the name, or else come up with a munging scheme that
never clashes.  Changing the existing has_ naming of flags is awkward
(lots of existing code) compared to munging the (unlikely) addition of a
new has_ member to a single qapi type.

-- 
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] 57+ messages in thread

* Re: [Qemu-devel] [PATCH v9 04/17] vnc: hoist allocation of VncBasicInfo to callers
  2015-10-20 14:46       ` Markus Armbruster
@ 2015-10-20 22:53         ` Eric Blake
  2015-10-21 11:02           ` Markus Armbruster
  2015-10-21 11:16           ` Daniel P. Berrange
  0 siblings, 2 replies; 57+ messages in thread
From: Eric Blake @ 2015-10-20 22:53 UTC (permalink / raw)
  To: Markus Armbruster, Gerd Hoffmann; +Cc: qemu-devel

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

On 10/20/2015 08:46 AM, Markus Armbruster wrote:
> Gerd Hoffmann <kraxel@redhat.com> writes:
> 
>>   Hi,
>>
>>>> -static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa,
>>>> -                                        socklen_t salen)
>>>> +static void vnc_basic_info_get(struct sockaddr_storage *sa,
>>>> +                               socklen_t salen,
>>>> +                               VncBasicInfo *info,
>>>> +                               Error **errp)
>>
>>> The function no longer "gets info", it merely initializes it.  Rename it
>>> accordingly?  Gerd?
>>
>> vnc_fill_basic_info maybe?
> 
> Fine with me.  Could also call it _init_ instead of _fill_.

I like init a bit better than fill.

> 
>>> Outside this patch's scope, but since I'm looking at the code already...

I'm guessing that also means that fixing it is outside this series' scope.

>>> When vnc_server_info_get() fails, the event is dropped.  Why is that
>>> okay?  Failure seems unlikely, though.
>>
>> Suggestions what else to do?  I don't wanna crash qemu by calling
>> qapi_event_send_vnc_* with a NULL pointer.  And, yes, it should be
>> highly unlikely so trying some more sophisticated error handling would
>> probably be dead code ...
> 
> These events signal a state change.  Dropping them make me feel uneasy,
> because if a client uses them to track state, it gets out of sync.

Events are already best-effort; clients have to be prepared to miss an
event - but that's mainly when reconnecting (such as across libvirtd
restarts), and not while the monitor is reliably connected.

> The events need to identify the server to be of any use for state
> tracking.  Currently, this is members host, service, family of data
> member server.
> 
> We could avoid failures in vnc_qmp_event() as follows:
> 
> 1. When we create a server, we obtain its info with getsockname() and
>    getnameinfo().  If they fail, we fail server creation.  Else, we
>    store the info for vnc_qmp_event().
> 
> 2. When a client connects, we obtain its info with getpeername() and
>    getnameinfo().  If they fail, we refuse the connection.  Else, we
>    store the infor for vnc_qmp_event().

Seems reasonable to me, but starts to be out of scope for what I'm
currently tackling, so is this something I can hand to you, Gerd?

> 
> Alternatively, we can embrace the failures and send the events without
> the information we failed to get.  Requires another way to identify the
> server.  VncDisplay's id, perhaps?  Might be nice to have anyway.

Yes, including the id as a new field to the event sounds like a
reasonable addition, whether or not my next patch reworking things to
drop info->base makes it impossible to pass qmp_event_send_vnc_* a NULL
pointer, even if we couldn't resolve useful information to display.

-- 
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] 57+ messages in thread

* Re: [Qemu-devel] [PATCH v9 04/17] vnc: hoist allocation of VncBasicInfo to callers
  2015-10-20  7:38   ` Markus Armbruster
  2015-10-20  8:54     ` Gerd Hoffmann
@ 2015-10-20 22:56     ` Eric Blake
  1 sibling, 0 replies; 57+ messages in thread
From: Eric Blake @ 2015-10-20 22:56 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Gerd Hoffmann

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

On 10/20/2015 01:38 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> A future qapi patch will rework generated structs with a base
>> class to be unboxed.  In preparation for that, change the code
>> that allocates then populates an info struct to instead merely
>> populate the fields of an info field passed in as a parameter.
>> Add rudimentary Error handling for cases where the old code
>> returned NULL; but as before, callers merely ignore errors for
>> now.
> 
> Actually, the call chain rooted at vnc_qmp_event() does handle failure
> before this patch.  It ignores the error *object* after the patch.
> 

>> @@ -168,42 +169,44 @@ static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa,
>>                             host, sizeof(host),
>>                             serv, sizeof(serv),
>>                             NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
>> -        VNC_DEBUG("Cannot resolve address %d: %s\n",
>> -                  err, gai_strerror(err));
>> -        return NULL;
>> +        error_setg(errp, "Cannot resolve address %d: %s",
>> +                   err, gai_strerror(err));
> 
> Printing err is fine for a debug message.  Less so for an error message.
> Drop it?

Ah, as in "Cannot resolve address: %s", gai_strerror(err).  Sure, sounds
okay to me.


>> @@ -256,13 +259,10 @@ static const char *vnc_auth_name(VncDisplay *vd) {
>>  static VncServerInfo *vnc_server_info_get(VncDisplay *vd)
>>  {
>>      VncServerInfo *info;
>> -    VncBasicInfo *bi = vnc_basic_info_get_from_server_addr(vd->lsock);
>> -    if (!bi) {
>> -        return NULL;
>> -    }
>>
>>      info = g_malloc(sizeof(*info));
>> -    info->base = bi;
>> +    info->base = g_malloc(sizeof(*info->base));
>> +    vnc_basic_info_get_from_server_addr(vd->lsock, info->base, NULL);
>>      info->has_auth = true;
>>      info->auth = g_strdup(vnc_auth_name(vd));
>>      return info;
> 
> Uh, doesn't this change the return value when getsockname() in
> vnc_basic_info_get_from_server_addr() fails?

Hmm. I wrote the patch back in July (wow - review has been taking a
while...), don't know what I was thinking. Yes, I need to fix this to
return NULL in the same situations the pre-patch version did, or else
pass errp to the caller (looks like just one: vnc_qmp_event()).  Or
maybe I was intentionally thinking that a best-effort result was
appropriate, particularly since the next patch gets rid of the base
member and therefore the possibility of info->base being NULL (maybe
that just means I rebased my series wrong when splitting one patch into
two).

Will fix.

-- 
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] 57+ messages in thread

* Re: [Qemu-devel] [PATCH v9 04/17] vnc: hoist allocation of VncBasicInfo to callers
  2015-10-20 22:53         ` Eric Blake
@ 2015-10-21 11:02           ` Markus Armbruster
  2015-10-21 11:16           ` Daniel P. Berrange
  1 sibling, 0 replies; 57+ messages in thread
From: Markus Armbruster @ 2015-10-21 11:02 UTC (permalink / raw)
  To: Eric Blake; +Cc: Gerd Hoffmann, qemu-devel

Eric Blake <eblake@redhat.com> writes:

> On 10/20/2015 08:46 AM, Markus Armbruster wrote:
>> Gerd Hoffmann <kraxel@redhat.com> writes:
>> 
>>>   Hi,
>>>
>>>>> -static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa,
>>>>> -                                        socklen_t salen)
>>>>> +static void vnc_basic_info_get(struct sockaddr_storage *sa,
>>>>> +                               socklen_t salen,
>>>>> +                               VncBasicInfo *info,
>>>>> +                               Error **errp)
>>>
>>>> The function no longer "gets info", it merely initializes it.  Rename it
>>>> accordingly?  Gerd?
>>>
>>> vnc_fill_basic_info maybe?
>> 
>> Fine with me.  Could also call it _init_ instead of _fill_.
>
> I like init a bit better than fill.
>
>> 
>>>> Outside this patch's scope, but since I'm looking at the code already...
>
> I'm guessing that also means that fixing it is outside this series' scope.

Definitely.

>>>> When vnc_server_info_get() fails, the event is dropped.  Why is that
>>>> okay?  Failure seems unlikely, though.
>>>
>>> Suggestions what else to do?  I don't wanna crash qemu by calling
>>> qapi_event_send_vnc_* with a NULL pointer.  And, yes, it should be
>>> highly unlikely so trying some more sophisticated error handling would
>>> probably be dead code ...
>> 
>> These events signal a state change.  Dropping them make me feel uneasy,
>> because if a client uses them to track state, it gets out of sync.
>
> Events are already best-effort; clients have to be prepared to miss an
> event - but that's mainly when reconnecting (such as across libvirtd
> restarts), and not while the monitor is reliably connected.

Big difference, actually!

Scenario A: events are reliable while you're connected, but you may miss
some during a reconnect.  You have to poll the state on connect to
compensate for missed events.

Scenatio B: events are a best effort.  You have to poll the state
periodically to compensate for dropped events.

In my understanding, we (mean to) provide A.

>> The events need to identify the server to be of any use for state
>> tracking.  Currently, this is members host, service, family of data
>> member server.
>> 
>> We could avoid failures in vnc_qmp_event() as follows:
>> 
>> 1. When we create a server, we obtain its info with getsockname() and
>>    getnameinfo().  If they fail, we fail server creation.  Else, we
>>    store the info for vnc_qmp_event().
>> 
>> 2. When a client connects, we obtain its info with getpeername() and
>>    getnameinfo().  If they fail, we refuse the connection.  Else, we
>>    store the infor for vnc_qmp_event().
>
> Seems reasonable to me, but starts to be out of scope for what I'm
> currently tackling, so is this something I can hand to you, Gerd?
>
>> 
>> Alternatively, we can embrace the failures and send the events without
>> the information we failed to get.  Requires another way to identify the
>> server.  VncDisplay's id, perhaps?  Might be nice to have anyway.
>
> Yes, including the id as a new field to the event sounds like a
> reasonable addition, whether or not my next patch reworking things to
> drop info->base makes it impossible to pass qmp_event_send_vnc_* a NULL
> pointer, even if we couldn't resolve useful information to display.

I don't think the solution to this problem will depend on your QAPI
work.

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

* Re: [Qemu-devel] [PATCH v9 04/17] vnc: hoist allocation of VncBasicInfo to callers
  2015-10-20 22:53         ` Eric Blake
  2015-10-21 11:02           ` Markus Armbruster
@ 2015-10-21 11:16           ` Daniel P. Berrange
  2015-10-23 13:13             ` Markus Armbruster
  1 sibling, 1 reply; 57+ messages in thread
From: Daniel P. Berrange @ 2015-10-21 11:16 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Markus Armbruster, Gerd Hoffmann

On Tue, Oct 20, 2015 at 04:53:24PM -0600, Eric Blake wrote:
> On 10/20/2015 08:46 AM, Markus Armbruster wrote:
> > Gerd Hoffmann <kraxel@redhat.com> writes:
> > 
> >>   Hi,
> >>
> >>>> -static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa,
> >>>> -                                        socklen_t salen)
> >>>> +static void vnc_basic_info_get(struct sockaddr_storage *sa,
> >>>> +                               socklen_t salen,
> >>>> +                               VncBasicInfo *info,
> >>>> +                               Error **errp)
> >>
> >>> The function no longer "gets info", it merely initializes it.  Rename it
> >>> accordingly?  Gerd?
> >>
> >> vnc_fill_basic_info maybe?
> > 
> > Fine with me.  Could also call it _init_ instead of _fill_.
> 
> I like init a bit better than fill.
> 
> > 
> >>> Outside this patch's scope, but since I'm looking at the code already...
> 
> I'm guessing that also means that fixing it is outside this series' scope.
> 
> >>> When vnc_server_info_get() fails, the event is dropped.  Why is that
> >>> okay?  Failure seems unlikely, though.
> >>
> >> Suggestions what else to do?  I don't wanna crash qemu by calling
> >> qapi_event_send_vnc_* with a NULL pointer.  And, yes, it should be
> >> highly unlikely so trying some more sophisticated error handling would
> >> probably be dead code ...
> > 
> > These events signal a state change.  Dropping them make me feel uneasy,
> > because if a client uses them to track state, it gets out of sync.
> 
> Events are already best-effort; clients have to be prepared to miss an
> event - but that's mainly when reconnecting (such as across libvirtd
> restarts), and not while the monitor is reliably connected.
> 
> > The events need to identify the server to be of any use for state
> > tracking.  Currently, this is members host, service, family of data
> > member server.
> > 
> > We could avoid failures in vnc_qmp_event() as follows:
> > 
> > 1. When we create a server, we obtain its info with getsockname() and
> >    getnameinfo().  If they fail, we fail server creation.  Else, we
> >    store the info for vnc_qmp_event().
> > 
> > 2. When a client connects, we obtain its info with getpeername() and
> >    getnameinfo().  If they fail, we refuse the connection.  Else, we
> >    store the infor for vnc_qmp_event().
> 
> Seems reasonable to me, but starts to be out of scope for what I'm
> currently tackling, so is this something I can hand to you, Gerd?

My pending IO channel patches do exactly this

Take a look at the QIOChannelSocket impl

  https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg03440.html

This caches the results of getpeername & getsockname in the
QOIChannelSocket object.

So my patches which convert VNC to use QIOChannelSOcket should solve
this particular failure scenario you're discussing.

Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

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

* Re: [Qemu-devel] [PATCH v9 01/17] qapi: Add tests for reserved name abuse
  2015-10-20 16:23     ` Eric Blake
@ 2015-10-21 12:08       ` Markus Armbruster
  0 siblings, 0 replies; 57+ messages in thread
From: Markus Armbruster @ 2015-10-21 12:08 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 10/19/2015 10:05 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> A future patch wants to change qapi union representation from
>>> an anonymous C union over to a named C union 'u', so that the
>>> C names of tag values are in a separate namespace and thus
>>> cannot collide with the C names of non-variant QMP members.
>>> But for that to not cause any problems with future extensions
>>> to existing qapi, it would be best if we prohibit 'u' as a
>>> member name everywhere, to reserve it for our internal use.
>>> (Remember that although 'u' would only actually collide in
>>> flat unions, we must also worry about the fact that it is
>>> possible to convert from a struct to a flat union in a future
>>> qemu version while remaining backwards-compatible in QMP.)
>> 
>> This part is awkward: we're adding a negative test that fails to fail
>> for use of a name that isn't actually reserved until two patches later.
>> 
>> I guess I'd move the 'u' tests to the patch that reserves the name.  Or
>> at least start the commit message with one of the non-awkward cases :)
>
> If you are okay with it, I can squash this test into commits 2 and 3, so
> that we aren't introducing tests and changing state later, but instead
> just adding tests at the time I reserve namespace.

Sounds good.

>> I started to write that you might want to mention we already reserve the
>> 'Kind' suffix, then noticed you do in PATCH 02.  No need to say it
>> twice.
>
> Especially if I take the approach of not having a separate patch 1 that
> just adds tests.
>
> Sometimes, I find it easier to add tests showing one behavior, then flip
> the switch to show how the new behavior changes the behavior.  But I can
> be persuaded that it is just churn, and that adding the new tests at the
> same time as the new behavior is just as effective. :)

I like adding tests for currently broken behavior first, then the fix.
Your patch does that for the 'has_' prefix and the 'List' suffix.

What I don't like so much is adding non-functional tests for new
behavior first, then make the actual change.  Your patch does that for
the 'u' member name.

I'd recommend to move just the 'u' part to the patch that actually
reserves that name.

>>> On the other hand, there is no reason to forbid type name
>>> patterns from member names, or member name patterns from types,
>>> since the two are not in the same namespace in C and won't
>>> collide.
>> 
>> However, we could *choose* to enforce a single name space for
>> simplicity.  By convention, type names are StudlyCaps (except for
>> built-ins), and member names are dashed-lower-case, so collisions are
>> unlikely anyway.
>> 
>> Perhaps you should write "there is no technical reason".
>
> Well, I _do_ have a possible technical reason in mind: we've already
> mentioned that we may want to someday support automatic '-'/'_' munging;
> and it is not too much of a further jump to support case-insensitive
> matching (at least on a US keyboard, - and _ are on the same key the
> same, so it is already akin to a case conversion to go between the two).
>  With case-insensitive member names, we then have collisions between a
> MyType type name and a mytype member (where the QMP client can still
> access the member via MyType).  But yeah, I can revisit the wording of
> this paragraph.

Okay, "currently no technical reason" :)

>> Since this is a test-only patch, I'd prefix the subject with
>> "tests/qapi-schema:" instead of "qapi:".
>
> Unless I split and squash it with other patches, of course.
>
>
>>> +++ b/tests/qapi-schema/args-name-has.json
>>> @@ -0,0 +1,6 @@
>>> +# C member name collision
>>> +# FIXME - This parses, but fails to compile, because the C struct is given
>>> +# two 'has_a' members, one from the flag for optional 'a', and the other
>>> +# from member 'has-a'.  Either reject this at parse time, or munge the C
>>> +# names to avoid the collision.
>>> +{ 'command': 'oops', 'data': { '*a': 'str', 'has-a': 'str' } }
>> 
>> Complements existing args-name-clash.json, which tests 'a-b' and 'a_b'.
>> 
>> Call it args-has-clash.json?
>
> Sure, can do.
>
>>> +++ b/tests/qapi-schema/flat-union-clash-branch.json
>>> @@ -1,18 +1,15 @@
>>>  # Flat union branch name collision
>>> -# FIXME: this parses, but then fails to compile due to a duplicate 'c_d'
>>> -# (one from the base member, the other from the branch name).  We should
>>> -# either reject the collision at parse time, or munge the generated branch
>>> -# name to allow this to compile.
>>> +# FIXME: this parses, but then fails to compile due to a duplicate 'has_a'
>>> +# (one as the implicit flag for the optional base member, the other from
>>> +# the C member for the branch name).  We should either reject the collision
>>> +# at parse time, or munge the generated branch name to allow this to compile.
>>>  { 'enum': 'TestEnum',
>>> -  'data': [ 'base', 'c-d' ] }
>>> +  'data': [ 'has-a' ] }
>>>  { 'struct': 'Base',
>>> -  'data': { 'enum1': 'TestEnum', '*c_d': 'str' } }
>>> +  'data': { 'enum1': 'TestEnum', '*a': 'str' } }
>>>  { 'struct': 'Branch1',
>>>    'data': { 'string': 'str' } }
>>> -{ 'struct': 'Branch2',
>>> -  'data': { 'value': 'int' } }
>>>  { 'union': 'TestUnion',
>>>    'base': 'Base',
>>>    'discriminator': 'enum1',
>>> -  'data': { 'base': 'Branch1',
>>> -            'c-d': 'Branch2' } }
>>> +  'data': { 'has-a': 'Branch1' } }
>> 
>> This replaces the test of branch name 'c-d' clashing with member 'c_d'
>> by a test of branch name 'has-a' clashing with the has_a flag of
>> optional member 'a'.  Okay, since flat-union-clash-type.json covers
>> clash of branch name with member name.
>
> And since the test disappears completely later in my pending work, once
> I change the layout to use a named union.  (See subset C 6/14
> https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg04111.html)
>
>>> +# Even if 'u' is forbidden as a struct member name, it should still be
>>> +# valid as a type or union branch name. And although '*Kind' is forbidden
>>> +# as a type name, it should not be forbidden as a member or branch name.
>>> +# TODO - '*List' should also be forbidden as a type name, and 'has_*' as
>>> +# a member name.
>>> +{ 'struct': 'has_a', 'data': { 'MyKind': 'int', 'MyList': ['int'] } }
>>> +{ 'union': 'u', 'data': { 'u': 'uint8', 'myKind': 'has_a',
>>> +                          'myList': 'has_a' } }
>>> +
>>>  # testing commands
>>>  { 'command': 'user_def_cmd', 'data': {} }
>>>  { 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} }
>> 
>> Value of these positive tests seems marginal.  But if you think they're
>> worth keeping, I'll take them.
>
> I added even more of these positive tests in subset C.  And then I
> noticed that you've made the same comment about the positive test I
> added in patch 4/17.  I can go either way (the added positive tests
> helped me ensure that things compiled while writing patches, but I won't
> be offended to omit them if you don't think they add enough to keep
> long-term).

Let's drop the positive tests that are really just covering special
cases that soon will be history.

>>> +++ b/tests/qapi-schema/struct-member-u.json
>>> @@ -0,0 +1,6 @@
>>> +# Potential C member name collision
>>> +# FIXME - This parses and compiles, but would cause a collision if this
>>> +# struct is later reworked to be part of a flat union, once unions hide
>>> +# their tag values under a C union named 'u'. We should reject the use
>>> +# of this identifier to reserve it for internal use.
>>> +{ 'struct': 'Oops', 'data': { 'u': 'str' } }
>> 
>> If the later patch outlaws 'u' in structs as well, this test will do,
>> only the comment will change.
>> 
>> If it outlaws 'u' only where it actually clashes, namely in unions, the
>> test will need updating.
>> 
>> More reason to move the test to the patch that does the outlawing.
>
> Fair enough. And yes, I outlawed 'u' everywhere, not just in unions, on
> the grounds that it is possible to convert a struct to a union while
> remaining backwards-compatible in the QMP that it accepts; the act of
> converting between object types must not invalidate an existing object
> member, so if we reserve a member name, we should reserve it for all
> objects and not just qapi unions.

An argument could be made either way, but it's best made in the context
of the patch that actually does the outlawing :)

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

* Re: [Qemu-devel] [PATCH v9 03/17] qapi: Reserve 'u' and 'has[-_]*' member names
  2015-10-20 21:29     ` Eric Blake
@ 2015-10-21 13:08       ` Markus Armbruster
  0 siblings, 0 replies; 57+ messages in thread
From: Markus Armbruster @ 2015-10-21 13:08 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 10/19/2015 11:19 AM, Markus Armbruster wrote:
>
>> I'm not quite comfortable with reserving 'u' now, becaue I feel we
>> haven't fully explored the design space for avoiding branch - member
>> clashes.
>> 
>> I still like the basic idea to give the unnamed union a name.  It needs
>> to be a short one, to keep the C code legible.  'u' is an obvious
>> option, but it requires reserving 'u' at least as member name.  '_u'
>> wouldn't.  Alternatively, call the union 'u', but avoid the clash by
>> mapping QAPI member name 'u' to C identifier '_u'.
>
> Naming the union '_u' is a bit uglier (more typing in every client)
> whereas munging just the member name (what about 'q_u', the way c_name
> appends 'q_' to any other name that would otherwise collide) pushes the
> ugliness only to the C code that actually uses a member named 'u'.
>
> But the idea of teaching c_name() to munge 'u' as a member name
> certainly seems doable, at which point we no longer need to reserve 'u'
> as a member name.

Munging QAPI name 'u' to C identifier 'q_u' begs the question what to do
with QAPI names 'q-u' and 'q_u'.

A relatively simple way out is to reserve QAPI names starting with 'q-'
and 'q_'.

I feel we need rules on name space and name munging instead of the
undisciplined "crap, got a collision, let me hack around it" approach
that's evident in the code.

Currently documented rules (docs/qapi-code-gen.txt):

1. Types, commands, and events share a common namespace.

2. Type definitions should always use CamelCase for user-defined type
   names, while built-in types are lowercase.

3. Type definitions should not end in 'Kind', as this namespace is used
   for creating implicit C enums for visiting union types.

4. Command names, and field names within a type, should be all lower
   case with words separated by a hyphen.  However, some existing older
   commands and complex types use underscore; when extending such
   expressions, consistency is preferred over blindly avoiding
   underscore.

5. Event names should be ALL_CAPS with words separated by underscore.

6. Other than downstream extensions (with leading underscore and the use
   of dots), all names should begin with a letter, and contain only
   ASCII letters, digits, dash, and underscore.

7. It is okay to reuse names that match C keywords; the generator will
   rename a field named "default" in the QAPI to "q_default" in the
   generated C code.

Note the non-use of "shall" for hard rules and "should" for mere
conventions.  As long as that's the case, a rule is hard when it's
enforced by a generator.

Rule 7 leaves "C keywords" unspecified.  If you think "C keywords" means
the keywords in the sense of the C standard, you're in for a surprise.

Rule 7 tacitly reserves "q_" + keyword, but an insufficiently paranoid
reader won't notice.

Rule 6 leaves C identifiers starting with underscore available for the
implementation.  However, C reserves all those for use as identifiers
with file scope, and the ones starting with two underscores or
underscore and uppercase latter for any use.

We can use identifiers starting with underscore and lower case letter or
digit for things like struct and union members, function parameters,
local variables.

If we need more, we'll have to reserve a suitable corner of the QAPI
namespace to make a some C namespace available for the implementation.
Reserving prefixes matching [qQ][-_] seems acceptable to me.

A thorough survey on how the generated C pollutes^Wmakes us of the C
namespaces would be in order, but I don't have time for it today.

>> I feel the decision should be made over the patch that give the union a
>> name.
>
> Well, that's patch 7/17 of this series, so at most, all I need to do is
> shuffle things around when rebasing for v10.
>
> For that matter, if we make c_name() munge 'u', it can just as easily
> munge a member named 'has_' to 'q_has' (or 'base' to 'q_base' - except
> that we are getting rid of that); or whatever other names we burn for
> convenience on the C side.
>
> But no one is using a member named 'u' at the moment, so it's not the
> most critical problem to solve; and forbidding it is certainly
> conservative (we can relax things to allow the name in QMP after all,
> once we figure out the appropriate munging for the C side).

I agree we need progress, not perfection.  When we accept an imperfect
solution to make progress, we should write down the issues for later,
though.  TODO comments should do.

>>> +++ b/scripts/qapi.py
>>> @@ -488,6 +488,10 @@ def check_type(expr_info, source, value, allow_array=False,
>>>      for (key, arg) in value.items():
>>>          check_name(expr_info, "Member of %s" % source, key,
>>>                     allow_optional=allow_optional)
>>> +        if key == 'u' or key.startswith('has-') or key.startswith('has_'):
>> 
>> Something like c_name(key).startswith('has_') would avoid hardcoding the
>> mapping of '-' to '_' here.  Dunno.
>
> Oh, nice idea.
>
> And looking at that, we have a number of places in qapi.py that are
> using things like str[-4:] == '....' that might look nicer as
> str.endwith('....'). I may add an obvious trivial cleanup patch into the
> mix.

Yes, .endswith() would be clearer.

>>> @@ -588,6 +592,14 @@ def check_union(expr, expr_info):
>>>      # Check every branch
>>>      for (key, value) in members.items():
>>>          check_name(expr_info, "Member of union '%s'" % name, key)
>>> +        # TODO: As long as branch names can collide with QMP names, we
>>> +        # must prevent branches starting with 'has_'. However, we do not
>>> +        # need to reject 'u', because that is reserved for when we start
>>> +        # sticking branch names in a C union named 'u'.
>>> +        if key.startswith('has-') or key.startswith('has_'):
>>> +            raise QAPIExprError(expr_info,
>>> +                                "Branch of union '%s' uses reserved name '%s'"
>>> +                                % (name, key))
>> 
>> This will go away again when we give the unnamed union a name.
>> 
>> I feel we should punt all further clash detection until late in the
>> cleanup work.  It's merely nice to have (sane error message from
>> generator instead of possibly confusing one from the C compiler,
>> basically), and adding it now causes churn later on.
>
> Okay, I can respin along those lines - if my work later in the series
> removes a negative test added earlier in the series, then strip that
> test from the series as a whole rather than fighting the churn, to
> reduce the size of the series.

Thanks.  I hope that'll help us focus and make progress.

>>> +++ b/tests/qapi-schema/args-name-has.json
>>> @@ -1,6 +1,5 @@
>>>  # C member name collision
>>> -# FIXME - This parses, but fails to compile, because the C struct is given
>>> -# two 'has_a' members, one from the flag for optional 'a', and the other
>>> -# from member 'has-a'.  Either reject this at parse time, or munge the C
>>> -# names to avoid the collision.
>>> +# This would attempt to create two 'has_a' members of the C struct, one
>>> +# from the flag for optional 'a', and the other from member 'has-a'.
>>> +# TODO we could munge the optional flag name to avoid the collision.
>> 
>> You mean call them _has_FOO instead of has_FOO?  The generated code
>> would be rather confusing...
>> 
>> If we don't want to reserve all names starting with 'has_', then I'd
>> narrowly outlaw having both an optional member FOO and a member has_FOO.
>> I think I'd like that a bit better than outlawing 'has_'.  But not
>> enough to accept much implementation complexity.
>
> The problem comes with child classes - we don't know a priori if an
> optional member in one struct will end up being a base class to another
> struct or union where the child class will hit the name clash.  It's
> easier to outlaw the name, or else come up with a munging scheme that
> never clashes.  Changing the existing has_ naming of flags is awkward
> (lots of existing code) compared to munging the (unlikely) addition of a
> new has_ member to a single qapi type.

Points to be considered in the context of the patch that actually does
something about the 'has-' collisions.

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

* Re: [Qemu-devel] [PATCH v9 05/17] qapi: Unbox base members
  2015-10-20 16:08     ` Eric Blake
@ 2015-10-21 13:34       ` Markus Armbruster
  2015-10-21 21:16         ` Eric Blake
  2015-10-23  1:50         ` Eric Blake
  0 siblings, 2 replies; 57+ messages in thread
From: Markus Armbruster @ 2015-10-21 13:34 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth, Gerd Hoffmann, Luiz Capitulino

Eric Blake <eblake@redhat.com> writes:

> On 10/20/2015 06:09 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> Rather than storing a base class as a pointer to a box, just
>>> store the fields of that base class in the same order, so that
>>> a child struct can be safely cast to its parent.
>
> ^^^^
>
>>> Compare to the earlier commit 1e6c1616a "qapi: Generate a nicer
>>> struct for flat unions".
>> 
>> Should we mention the struct can be cast to its base?
>
> As in the ^^^ sentence above? Or did you mean somewhere additionally in
> the comments in the code below?

Wish I could read %-}  Sorry for the noise!

>>> -def gen_struct_fields(members):
>>> +def gen_struct_fields(members, base, nested=False):
>>>      ret = ''
>>> +    if base:
>>> +        if not nested:
>>> +            ret += mcgen('''
>>> +    /* Members inherited from %(c_name)s: */
>>> +''',
>>> +                         c_name=base.c_name())
>>> +        ret += gen_struct_fields(base.local_members, base.base, True)
>>> +        if not nested:
>>> +            ret += mcgen('''
>>> +    /* Own members: */
>>> +''')
>> 
>> Before: gen_struct_fields() emits *own* fields.
>> 
>> After: it emits *all* fields.
>> 
>> Since the signature changes, all callers are visible in the patch, and
>> can be reviewed.
>> 
>> Code looks a bit awkward, but I don't have better ideas.  Perhaps we can
>> do better when we fuse gen_struct() and gen_union().
>
> I haven't tried to do that fusion yet; but we are probably quite close
> to it.  I'll see whether it is worth adding on as an 18/17 in this
> subset of the series, or saving for later.

Doesn't feel urgent to me.

>> This is gen_union().
>> 
>>>  ''',
>>>                  c_name=c_name(name))
>>>      if base:
>>> -        ret += mcgen('''
>>> -    /* Members inherited from %(c_name)s: */
>>> -''',
>>> -                     c_name=c_name(base.name))
>>> -        ret += gen_struct_fields(base.members)
>>> -        ret += mcgen('''
>>> -    /* Own members: */
>>> -''')
>>> +        ret += gen_struct_fields([], base)
>>>      else:
>>>          ret += mcgen('''
>>>      %(c_type)s kind;
>> 
>> Before: emit base fields, then own fields.
>> 
>> After: emit all fields.  Good, but please mention in the commit message
>> that you're touching gen_union().  You could also do this part in a
>> separate patch, but that's probably overkill.
>
> Sure, I can improve the commit message, and maybe split the patch. The
> idea at play is that both structs and unions want to emit all fields, as
> well as strategic comments about which fields are inherited, so a shared
> function is ideal for that; this patch moved code from gen_union() into
> gen_struct_fields() so that gen_struct() could reuse it.

I feel there's a variant record caught inside our struct and union
types, struggling to get out.  It's already out in introspection.  Your
patch is a welcome step towards getting it out elsewhere.

>>>      if base:
>>> -        ret += gen_visit_implicit_struct(base)
>>> +        ret += gen_visit_struct_fields(base.name, base.base,
>>> +                                       base.local_members)
>>>
>>>      ret += mcgen('''
>>>
>> 
>> This change looks innocent enough on first glance, but it's actually
>> quite hairy.
>> 
>> = Before =
>> 
>> The visit_type_FOO_fields() are generated in QAPISchema visit order,
>> i.e. right when QAPISchemaObjectType 'FOO' is visited.
>> 
>> The visit_type_implicit_FOO() are generated on demand, right before
>> their first use.  It's used by visit_type_STRUCT_fields() when STRUCT
>> has base FOO, and by visit_type_UNION() when flat UNION has a member
>> FOO.
>> 
>> Aside: only flat unions, because simple unions are an ugly special case
>> here.  To be unified.
>> 
>> Generating visit_type_implicit_FOO() on demand makes sense, because the
>> function isn't always needed.
>> 
>> Since visit_type_implicit_FOO() calls visit_type_FOO_fields(), and the
>> former can be generated before the latter, we may need a forward
>> declaration.  struct_fields_seen is used to detect this.  See commit
>> 8c3f8e7.
>> 
>> = After =
>> 
>> visit_type_implicit_FOO() is now used only when flat UNION has a member
>> FOO.  May need a forward declaration of visit_type_FOO_fields() anyway.
>> 
>> visit_type_FOO_fields() is now also generated on demand, right before
>> first use other than visit_type_implicit_FOO().  Weird.
>> 
>> The resulting code motion makes the change to generated code difficult
>> to review.
>> 
>> Generating visit_type_FOO_fields() on demand makes less sense, because
>> the function is always needed.  All it can accomplish is saving a few
>> forward declarations, at the cost of making gen_visit_struct_fields()
>> recursive, which begs the recursion termination question, and less
>> uniform generated code.
>> 
>> The naive answer to the recursion termination question is that types
>> can't contain themselves, therefore we can't ever get into a cycle.
>> That begs the next question: why do we need the if name in
>> struct_fields_seen conditional then?  We do need it (turning it into an
>> assertion promptly fails it), but I can't tell why offhand.
>> 
>> I'm sure I could figure out why this works, but I don't want to.  Let's
>> keep the code simple instead: keep generating visit_type_FOO_fields() in
>> QAPISchema visit order, emit necessary forward declarations.
>> 
>> Suggest to factor the code to emit a forward declaration out of
>> gen_visit_implicit_struct() and reuse it in gen_visit_struct_fields().
>
> Sounds like I need to split this patch then, anyways.  I'll see what I
> can come up with for v10.

Looking forward to it.  I'll continue reviewing v9 as time permits.

>>> +++ b/tests/qapi-schema/qapi-schema-test.json
>>> @@ -40,6 +40,10 @@
>>>    'data': { 'string0': 'str',
>>>              'dict1': 'UserDefTwoDict' } }
>>>
>>> +# ensure that we don't have an artificial collision on 'base'
>>> +{ 'struct': 'UserDefThree',
>>> +  'base': 'UserDefOne', 'data': { 'base': 'str' } }
>>> +
>
>> This is the positive test I challenged above.
>
> I can drop it if you don't like it; I felt better with it, but as you
> say, it only proves that 'base' is usable as a member name, and not that
> someone won't pick some other name when refactoring back into a boxed
> base.  See also my comments below about using containers (rather than
> boxes or inlined members), where we may need to deal with the naming
> clash anyways.
>
>>> @@ -218,9 +216,11 @@ static void channel_event(int event,
>>> SpiceChannelEventInfo *info)
>>>      }
>>>
>>>      if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
>>> -        add_addr_info(client->base, (struct sockaddr *)&info->paddr_ext,
>>> +        add_addr_info((SpiceBasicInfo *)client,
>> 
>> Ah, you're already exploiting the ability to cast to the base type!
>
> Absolutely :)
>
>> 
>> Idea: should we generate a type-safe macro or inline function for this?
>
> Hmm. DO_UPCAST() (and its more powerful underlying container_of())
> doesn't fit here, because we inlined the fields rather than directly
> including the base.

Yes, because it results in slightly more readable code: always simply
p->m instead of things like p->base.base.m when m is inherited (which is
usually of no concern when it's used).

Drawbacks of inlining the base include:

* You have to keep the copies consistent.  Too much bother in
  hand-written code, but trivially safe in generated code like ours.

* Loss of the obvious safe way to convert to base: &p->base

* Loss of the conventional way to convert from base: container_of().

I remember seeing conversion to base in your patch, but not the other
way round.

> Below, I'm using this qapi:
> {'struct':'Parent', 'data':{'i':'int'}}
> {'struct':'Child', 'base':'Parent', 'data':{'b':'bool'}}
>
> What we have without this patch is a box that requires separate
> allocation (two layers of pointers, the boxing that we don't want):
>
> struct Child {
>     Parent *base;
>     bool b;
> };
>
> And this patch takes things to be completely inlined (since that's what
> we did earlier with flat unions), so that there is no intermediate
> structure in the child (and thus nothing for DO_UPCAST() to grab):
>
> struct Child {
>     /* Inherited fields from Parent */
>     int i;
>     /* own fields */
>     bool b;
> };
>
> But there is a third possible layout (if we do it, we should do it for
> flat unions as well), which is using the base as a container rather than
> a box, where at least we don't need separate allocation:
>
> struct Child {
>     Parent base;
>     bool b;
> };
>
> or similarly, but in longhand:
>
> struct Child {
>     struct {
>         int i;
>     } base;
>     bool b;
> };
>
> but then we are back to having to avoid a collision with the C name
> 'base' with a QMP name in own fields, and all client code has to spell
> out child->base.i (instead of the boxed child->base->i, or the inlined
> child->i).

Yes.

> There's also the ugly approach of exposing things in a dual naming
> system via an anonymous union and struct:
>
> struct Child {
>     union {
>         struct {
>             int i;
>         };
>         Parent base;
>     };
>     bool b;
> };
>
> which would allow 'child->i' to be the same storage as 'child->base.i',
> so that clients can use the short spelling when accessing fields but
> also have handy access to the base member for DO_UPCAST().  I'm not sure
> I want to go there, though.

Seems to clever for its own sake :)

> Taking your idea from another review message, you mentioned that we
> could allow 'base' by tweaking c_name() to munge the user's member
> 'base' into 'q_base', while reserving 'base' for ourselves; or we could
> use '_base' for our own use, which shouldn't collide with user's names
> (user names should not start with underscore except for downstream names
> which have double-underscore).  Or use '_b' instead of 'base' - the
> point remains that if we want to avoid a collision but still use the
> container approach, we could pick the C name appropriately.  But
> ultimately, we should either commit to the name munging pattern, or
> outlaw the name that we plan to use internally, or stick to inlined
> members that don't require munging in the first place.
>
> Meanwhile, if we decide that we like the convenience of inlined data,
> you are correct in the idea that we could have the qapi generator
> automatically spit out an appropriate type-specific upcast inline
> function for each type with a base.  So given the same example, this
> might mean:
>
> static inline Parent *qapi_Child_to_Parent(Child *child) {
>     return (Parent*)child;
> }
>
> But while such a representation would add compiler type-safety (hiding
> the cast in generated code, where we can prove the generator knew it was
> safe, and so that clients don't have to cast), it also adds verbosity.
> I can't think of any representation that would be shorter than making
> the clients do the cast, short of using a container rather than inline
> approach.  Even foo(qapi_baseof_Child(child), blah) is longer than
> foo((Parent *)child, blah).
>
> Preferences?

The least verbose naming convention for a conversion function I can
think of right now is TBase(), where T is the name of a type with a
base.  Compare:

    foo((Parent *)child, blah)
    foo(ChildBase(child), blah)

Tolerable?  Worthwhile?

Note: *Base instead of *_base not because I like StudlyCaps (I do not),
but for consistency with *List and *Kind.

>> Turned out nicely, I anticipated more churn.
>
> Yes, that was my feeling as well - we haven't quite used base classes in
> enough places to make the conversion painful - but the longer we wait,
> the more usage of base classes will sneak into .json files making a
> future conversion to a new layout more painful.

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

* Re: [Qemu-devel] [PATCH v9 06/17] qapi-visit: Remove redundant functions for flat union base
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 06/17] qapi-visit: Remove redundant functions for flat union base Eric Blake
@ 2015-10-21 17:36   ` Markus Armbruster
  2015-10-21 19:01     ` Eric Blake
  0 siblings, 1 reply; 57+ messages in thread
From: Markus Armbruster @ 2015-10-21 17:36 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> The code for visiting the base class of a child struct created
> visit_type_Base_fields() which covers all fields of Base; while
> the code for visiting the base class of a flat union created
> visit_type_Union_fields() covering all fields of the base
> except the discriminator.  But if the base class were to always
> visit all its fields, then we wouldn't need a separate visit of
> the discriminator for a union.  Not only is consistently
> visiting all fields easier to understand, it lets us share code.
>
> Now that gen_visit_struct_fields() can potentially collect more
> than one function into 'ret', a regular expression searching for
> whether a label was used may hit a false positive within the
> body of the first function.  But using a regex was overkill,
> since we can easily determine when we jumped to a label.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: (no v6-8): hoist from v5 35/46; rebase to master; fix indentation
> botch in gen_visit_union(); polish commit message
> ---
>  scripts/qapi-visit.py | 35 +++++++++++++++++------------------
>  1 file changed, 17 insertions(+), 18 deletions(-)
>
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 8aae8da..91bf350 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -90,7 +90,7 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e
>
>      ret += gen_visit_fields(members, prefix='(*obj)->')
>
> -    if re.search('^ *goto out;', ret, re.MULTILINE):
> +    if base or members:

What if we have an empty base and no members?  Empty base is a
pathological case, admittedly.

>          ret += mcgen('''
>
>  out:
> @@ -221,8 +221,8 @@ def gen_visit_union(name, base, variants):
>      ret = ''
>
>      if base:
> -        members = [m for m in base.members if m != variants.tag_member]
> -        ret += gen_visit_struct_fields(name, None, members)
> +        ret += gen_visit_struct_fields(base.name, base.base,
> +                                       base.local_members)
>
>      for var in variants.variants:
>          # Ugly special case for simple union TODO get rid of it
> @@ -247,31 +247,30 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
>
>      if base:
>          ret += mcgen('''
> -    visit_type_%(c_name)s_fields(v, obj, &err);
> +    visit_type_%(c_name)s_fields(v, (%(c_name)s **)obj, &err);
>  ''',
> -                     c_name=c_name(name))
> -        ret += gen_err_check(label='out_obj')
> -
> -    tag_key = variants.tag_member.name
> -    if not variants.tag_name:
> -        # we pointlessly use a different key for simple unions
> -        tag_key = 'type'
> -    ret += mcgen('''
> +                     c_name=base.c_name())
> +    else:
> +        ret += mcgen('''
>      visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "%(name)s", &err);
> -    if (err) {
> -        goto out_obj;
> -    }
> +''',
> +                     c_type=variants.tag_member.type.c_name(),
> +                     # TODO ugly special case for simple union
> +                     # Use same tag name in C as on the wire to get rid of
> +                     # it, then: c_name=c_name(variants.tag_member.name)
> +                     c_name='kind',
> +                     name=variants.tag_member.name)
> +    ret += gen_err_check(label='out_obj')
> +    ret += mcgen('''
>      if (!visit_start_union(v, !!(*obj)->data, &err) || err) {
>          goto out_obj;
>      }
>      switch ((*obj)->%(c_name)s) {
>  ''',
> -                 c_type=variants.tag_member.type.c_name(),
>                   # TODO ugly special case for simple union
>                   # Use same tag name in C as on the wire to get rid of
>                   # it, then: c_name=c_name(variants.tag_member.name)
> -                 c_name=c_name(variants.tag_name or 'kind'),
> -                 name=tag_key)
> +                 c_name=c_name(variants.tag_name or 'kind'))
>
>      for var in variants.variants:
>          # TODO ugly special case for simple union

Diff is confusing (not your fault).  Let me compare code before and
after real slow.

= Before =

  def gen_visit_union(name, base, variants):
      ret = ''

0. base is None if and only if the union is simple.

1. If it's a flat union, generate its visit_type_NAME_fields().  This
function visits the union's non-variant members *except* the
discriminator.  Since a simple union has no non-variant members other
than the discriminator, generate it only for flat unions.

      if base:
          members = [m for m in base.members if m != variants.tag_member]
          ret += gen_visit_struct_fields(name, None, members)

2. Generate the visit_type_implicit_FOO() we're going to need.

      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_implicit_struct(var.type)

3. Generate its visit_type_NAME().

      ret += mcgen('''

  void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
  {
      Error *err = NULL;

      visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
      if (err) {
          goto out;
      }
      if (!*obj) {
          goto out_obj;
      }
  ''',
                   c_name=c_name(name), name=name)

3.a. If it's a flat union, generate the call of
visit_type_NAME_fields().  Not necessary for simple unions, see 1.

      if base:
          ret += mcgen('''
      visit_type_%(c_name)s_fields(v, obj, &err);
  ''',
                       c_name=c_name(name))
          ret += gen_err_check(label='out_obj')

3.b. Generate visit of discriminator.

      tag_key = variants.tag_member.name
      if not variants.tag_name:
          # we pointlessly use a different key for simple unions
          tag_key = 'type'
      ret += mcgen('''
      visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "%(name)s", &err);
      if (err) {
          goto out_obj;
      }

3.c. Generate visit of the active variant.

      if (!visit_start_union(v, !!(*obj)->data, &err) || err) {
          goto out_obj;
      }
      switch ((*obj)->%(c_name)s) {
  ''',
                   c_type=variants.tag_member.type.c_name(),
                   # TODO ugly special case for simple union
                   # Use same tag name in C as on the wire to get rid of
                   # it, then: c_name=c_name(variants.tag_member.name)
                   c_name=c_name(variants.tag_name or 'kind'),
                   name=tag_key)

[Some stuff the patch doesn't change omitted...]

  out_obj:
      error_propagate(errp, err);
      err = NULL;
      if (*obj) {
          visit_end_union(v, !!(*obj)->data, &err);
      }
      error_propagate(errp, err);
      err = NULL;
      visit_end_struct(v, &err);
  out:
      error_propagate(errp, err);
  }
  ''')

      return ret

= After =

  def gen_visit_union(name, base, variants):
      ret = ''

0. base is None if and only if the union is simple.

1. If it's a flat union, generate its visit_type_NAME_fields().  This
function visits the union's non-variant members *including* the
discriminator.  However, we generate it only for flat unions.  Simple
unions have no non-variant members other than the discriminator.

      if base:
          ret += gen_visit_struct_fields(base.name, base.base,
                                         base.local_members)

2. Generate the visit_type_implicit_FOO() we're going to need.

      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_implicit_struct(var.type)

3. Generate its visit_type_NAME().

      ret += mcgen('''

  void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
  {
      Error *err = NULL;

      visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
      if (err) {
          goto out;
      }
      if (!*obj) {
          goto out_obj;
      }
  ''',
                   c_name=c_name(name), name=name)

3.a. If it's a flat union, generate the call of
visit_type_NAME_fields().  Not done for simple unions, see 1.

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

3.b. If it's a simple union, generate the visit of the sole non-variant
member inline.

      else:
          ret += mcgen('''
      visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "%(name)s", &err);
  ''',
                       c_type=variants.tag_member.type.c_name(),
                       # TODO ugly special case for simple union
                       # Use same tag name in C as on the wire to get rid of
                       # it, then: c_name=c_name(variants.tag_member.name)
                       c_name='kind',
                       name=variants.tag_member.name)

3.a+b. Generate the error check for visit of non-variant members

      ret += gen_err_check(label='out_obj')

3.c. Generate visit of the active variant.

      ret += mcgen('''
      if (!visit_start_union(v, !!(*obj)->data, &err) || err) {
          goto out_obj;
      }
      switch ((*obj)->%(c_name)s) {
  ''',
                   # TODO ugly special case for simple union
                   # Use same tag name in C as on the wire to get rid of
                   # it, then: c_name=c_name(variants.tag_member.name)
                   c_name=c_name(variants.tag_name or 'kind'))

[Some stuff the patch doesn't change omitted...]

  out_obj:
      error_propagate(errp, err);
      err = NULL;
      if (*obj) {
          visit_end_union(v, !!(*obj)->data, &err);
      }
      error_propagate(errp, err);
      err = NULL;
      visit_end_struct(v, &err);
  out:
      error_propagate(errp, err);
  }
  ''')

      return ret

Okay, the change to gen_visit_union() looks sane.

Can we go one step further and generate and use visit_type_NAME_fields()
even for simple unions?

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

* Re: [Qemu-devel] [PATCH v9 06/17] qapi-visit: Remove redundant functions for flat union base
  2015-10-21 17:36   ` Markus Armbruster
@ 2015-10-21 19:01     ` Eric Blake
  2015-10-22  8:32       ` Markus Armbruster
  0 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-21 19:01 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 10/21/2015 11:36 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> The code for visiting the base class of a child struct created
>> visit_type_Base_fields() which covers all fields of Base; while
>> the code for visiting the base class of a flat union created
>> visit_type_Union_fields() covering all fields of the base
>> except the discriminator.  But if the base class were to always
>> visit all its fields, then we wouldn't need a separate visit of
>> the discriminator for a union.  Not only is consistently
>> visiting all fields easier to understand, it lets us share code.
>>
>> Now that gen_visit_struct_fields() can potentially collect more
>> than one function into 'ret', a regular expression searching for
>> whether a label was used may hit a false positive within the
>> body of the first function.  But using a regex was overkill,
>> since we can easily determine when we jumped to a label.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>

>> +++ b/scripts/qapi-visit.py
>> @@ -90,7 +90,7 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e
>>
>>      ret += gen_visit_fields(members, prefix='(*obj)->')
>>
>> -    if re.search('^ *goto out;', ret, re.MULTILINE):
>> +    if base or members:
> 
> What if we have an empty base and no members?  Empty base is a
> pathological case, admittedly.

I'm going to filter the re.search cleanups into its own prereq patch.
But yes, it will need to care for empty base and no members (hmm, I
really ought to add positive tests to qapi-schema-test for an empty
inherited struct, to make sure I'm getting it right - even if we don't
want that patch in the final series).


> Diff is confusing (not your fault).  Let me compare code before and
> after real slow.

I also plan for v10 to include a diff of the generated code in the
commit message, if that will help make the change more obvious.

> 
> = Before =
> 
>   def gen_visit_union(name, base, variants):
>       ret = ''
> 
> 0. base is None if and only if the union is simple.
> 
> 1. If it's a flat union, generate its visit_type_NAME_fields().  This

where NAME is the union name.

> function visits the union's non-variant members *except* the
> discriminator.  Since a simple union has no non-variant members other
> than the discriminator, generate it only for flat unions.
> 
>       if base:
>           members = [m for m in base.members if m != variants.tag_member]
>           ret += gen_visit_struct_fields(name, None, members)
> 
> 2. Generate the visit_type_implicit_FOO() we're going to need.
> 
>       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_implicit_struct(var.type)

Could be made slightly simpler by generating these while we iterate over
cases (but we'd have to be careful to generate into multiple variables,
and then concat together at the end, since we can't generate one
function in the body of the other).

> 
> 3. Generate its visit_type_NAME().
> 

> 
> 3.a. If it's a flat union, generate the call of
> visit_type_NAME_fields().  Not necessary for simple unions, see 1.

Again, important to note that this was visit_type_UNION_fields().

> 3.b. Generate visit of discriminator.
> 

> 
> 3.c. Generate visit of the active variant.
> 

> = After =
> 
>   def gen_visit_union(name, base, variants):
>       ret = ''
> 
> 0. base is None if and only if the union is simple.
> 
> 1. If it's a flat union, generate its visit_type_NAME_fields().  This
> function visits the union's non-variant members *including* the
> discriminator.  However, we generate it only for flat unions.  Simple
> unions have no non-variant members other than the discriminator.
> 
>       if base:
>           ret += gen_visit_struct_fields(base.name, base.base,
>                                          base.local_members)

Note that this creates visit_type_BASE_fields() (a different function).

> 
> 2. Generate the visit_type_implicit_FOO() we're going to need.
> 
>       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_implicit_struct(var.type)
> 
> 3. Generate its visit_type_NAME().
> 

> 
> 3.a. If it's a flat union, generate the call of
> visit_type_NAME_fields().  Not done for simple unions, see 1.

Again, now NAME is the base name rather than the union name.  Subtle but
important difference!

> 
>       if base:
>           ret += mcgen('''
>       visit_type_%(c_name)s_fields(v, (%(c_name)s **)obj, &err);
>   ''',
>                        c_name=base.c_name())
> 
> 3.b. If it's a simple union, generate the visit of the sole non-variant
> member inline.
> 

> 
> 3.a+b. Generate the error check for visit of non-variant members
> 
>       ret += gen_err_check(label='out_obj')
> 
> 3.c. Generate visit of the active variant.
> 

> 
> Okay, the change to gen_visit_union() looks sane.

Yes, you got it all correct.

> 
> Can we go one step further and generate and use visit_type_NAME_fields()
> even for simple unions?

Not easily.  Remember, for flat unions, we are calling
visit_type_BASE_fields, not visit_type_UNION_fields.  There is no base
for a simple union.

What I _am_ planning for future patches is the following:

Change QAPISchemaObjectType for simple unions and alternates to set
.local_members to the one-element implicit discriminator (right now, it
is always an empty array, and we even assert that bool(.local_members)
and bool(.variants) are mutually-exclusive in at least qapi-types.py
visit_object_type()).  Flat unions would keep .local_members as an empty
array (there is no local member, just the inherited members from the
base class, which includes the inherited discriminator).

Then merge gen_visit_struct() and gen_visit_union() to look like:

if base:
    # includes discriminator for flat union
    call visit_type_BASE_fields()
for m in .local_members
    # includes discriminator for simple union
    call visit_type_MEMBER()
if variants
    emit switch statement to visit each branch

But if we want, that 'for m in .local_members' block can indeed be
implemented via a call to visit_type_UNION_fields(), if that is any more
efficient to implement.  I guess it also boils down to deciding if
visit_type_FOO_fields() should continue to be unconditional (either for
all types, or at least for all types with non-empty .local_members).

-- 
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] 57+ messages in thread

* Re: [Qemu-devel] [PATCH v9 05/17] qapi: Unbox base members
  2015-10-21 13:34       ` Markus Armbruster
@ 2015-10-21 21:16         ` Eric Blake
  2015-10-22  6:28           ` Markus Armbruster
  2015-10-23  1:50         ` Eric Blake
  1 sibling, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-21 21:16 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Michael Roth, Gerd Hoffmann, Luiz Capitulino

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

On 10/21/2015 07:34 AM, Markus Armbruster wrote:

>>>> @@ -218,9 +216,11 @@ static void channel_event(int event,
>>>> SpiceChannelEventInfo *info)
>>>>      }
>>>>
>>>>      if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
>>>> -        add_addr_info(client->base, (struct sockaddr *)&info->paddr_ext,
>>>> +        add_addr_info((SpiceBasicInfo *)client,
>>>
>>> Ah, you're already exploiting the ability to cast to the base type!
>>
>> Absolutely :)
>>
>>>
>>> Idea: should we generate a type-safe macro or inline function for this?
>>
>> Hmm. DO_UPCAST() (and its more powerful underlying container_of())
>> doesn't fit here, because we inlined the fields rather than directly
>> including the base.
> 
> Yes, because it results in slightly more readable code: always simply
> p->m instead of things like p->base.base.m when m is inherited (which is
> usually of no concern when it's used).
> 

>> There's also the ugly approach of exposing things in a dual naming
>> system via an anonymous union and struct:
>>
>> struct Child {
>>     union {
>>         struct {
>>             int i;
>>         };
>>         Parent base;
>>     };
>>     bool b;
>> };
>>
>> which would allow 'child->i' to be the same storage as 'child->base.i',
>> so that clients can use the short spelling when accessing fields but
>> also have handy access to the base member for DO_UPCAST().  I'm not sure
>> I want to go there, though.
> 
> Seems to clever for its own sake :)

I agree (and even though I'm using a similar hack in 7/17 for the same
purpose, I get rid of it as soon as possible in 16/17).

>> But while such a representation would add compiler type-safety (hiding
>> the cast in generated code, where we can prove the generator knew it was
>> safe, and so that clients don't have to cast), it also adds verbosity.
>> I can't think of any representation that would be shorter than making
>> the clients do the cast, short of using a container rather than inline
>> approach.  Even foo(qapi_baseof_Child(child), blah) is longer than
>> foo((Parent *)child, blah).
>>
>> Preferences?
> 
> The least verbose naming convention for a conversion function I can
> think of right now is TBase(), where T is the name of a type with a
> base.  Compare:
> 
>     foo((Parent *)child, blah)
>     foo(ChildBase(child), blah)
> 
> Tolerable?  Worthwhile?

The verbosity is then determined by how long the child name is (where
the cast depended on the parent name)

What happens with multiple inheritance?

If we have Grandparent -> Parent -> Child, then it should be possible to
cast to both bases:

(Grandparent *)child
(Parent *)child

with your scheme, getting a child to grandparent would have to look like
either of:

ParentBase(ChildBase(child))
ChildBaseBase(child)

Or, think what happens if we originally have Grandparent -> Child in one
version of qemu, then inject Parent in the middle in another - the QMP
can still be back-compat, and the direct casts and member variable
references in C still work, but any code using ChildBase() no longer
works (returning Parent* instead of Grandparent*).

So the only thing I can think of is to output some name that includes
both the child type and parent type name (to make it obvious which
conversion is being done):

qapi_Child_Grandparent(child)
qapi_Child_Parent(child)

At this point, I'm leaning towards client casts, just because of the
verbosity, but I'll at least try the generated type-safe functions in
v10 to see how bad it really is.  A patch to discuss will make it easier
to decide whether to paint this bikeshed.

-- 
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] 57+ messages in thread

* Re: [Qemu-devel] [PATCH v9 05/17] qapi: Unbox base members
  2015-10-21 21:16         ` Eric Blake
@ 2015-10-22  6:28           ` Markus Armbruster
  0 siblings, 0 replies; 57+ messages in thread
From: Markus Armbruster @ 2015-10-22  6:28 UTC (permalink / raw)
  To: Eric Blake; +Cc: Luiz Capitulino, Gerd Hoffmann, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 10/21/2015 07:34 AM, Markus Armbruster wrote:
>
>>>>> @@ -218,9 +216,11 @@ static void channel_event(int event,
>>>>> SpiceChannelEventInfo *info)
>>>>>      }
>>>>>
>>>>>      if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
>>>>> -        add_addr_info(client->base, (struct sockaddr *)&info->paddr_ext,
>>>>> +        add_addr_info((SpiceBasicInfo *)client,
>>>>
>>>> Ah, you're already exploiting the ability to cast to the base type!
>>>
>>> Absolutely :)
>>>
>>>>
>>>> Idea: should we generate a type-safe macro or inline function for this?
>>>
>>> Hmm. DO_UPCAST() (and its more powerful underlying container_of())
>>> doesn't fit here, because we inlined the fields rather than directly
>>> including the base.
>> 
>> Yes, because it results in slightly more readable code: always simply
>> p->m instead of things like p->base.base.m when m is inherited (which is
>> usually of no concern when it's used).
>> 
>
>>> There's also the ugly approach of exposing things in a dual naming
>>> system via an anonymous union and struct:
>>>
>>> struct Child {
>>>     union {
>>>         struct {
>>>             int i;
>>>         };
>>>         Parent base;
>>>     };
>>>     bool b;
>>> };
>>>
>>> which would allow 'child->i' to be the same storage as 'child->base.i',
>>> so that clients can use the short spelling when accessing fields but
>>> also have handy access to the base member for DO_UPCAST().  I'm not sure
>>> I want to go there, though.
>> 
>> Seems to clever for its own sake :)
>
> I agree (and even though I'm using a similar hack in 7/17 for the same
> purpose, I get rid of it as soon as possible in 16/17).
>
>>> But while such a representation would add compiler type-safety (hiding
>>> the cast in generated code, where we can prove the generator knew it was
>>> safe, and so that clients don't have to cast), it also adds verbosity.
>>> I can't think of any representation that would be shorter than making
>>> the clients do the cast, short of using a container rather than inline
>>> approach.  Even foo(qapi_baseof_Child(child), blah) is longer than
>>> foo((Parent *)child, blah).
>>>
>>> Preferences?
>> 
>> The least verbose naming convention for a conversion function I can
>> think of right now is TBase(), where T is the name of a type with a
>> base.  Compare:
>> 
>>     foo((Parent *)child, blah)
>>     foo(ChildBase(child), blah)
>> 
>> Tolerable?  Worthwhile?
>
> The verbosity is then determined by how long the child name is (where
> the cast depended on the parent name)

Child vs. parent is probably a wash on average.  Cases like
BlockdevOptions inheriting BlockdevOptionsBase become slightly more
concise (but need a rename to avoid the clash), cases like VncServerInfo
inheriting from VncBasicInfo take a few more characters.

> What happens with multiple inheritance?
>
> If we have Grandparent -> Parent -> Child, then it should be possible to
> cast to both bases:
>
> (Grandparent *)child
> (Parent *)child
>
> with your scheme, getting a child to grandparent would have to look like
> either of:
>
> ParentBase(ChildBase(child))
> ChildBaseBase(child)

I'd start with the former.  If it becomes annoyingly verbose, we can
still define additional conversion functions.  I doubt it'll be
necessary.

> Or, think what happens if we originally have Grandparent -> Child in one
> version of qemu, then inject Parent in the middle in another - the QMP
> can still be back-compat, and the direct casts and member variable
> references in C still work, but any code using ChildBase() no longer
> works (returning Parent* instead of Grandparent*).

The compiler will lead us to the places that need updating.

Could be regarded a feature, even: it encourages us to review whether
these places should use the new intemediate type.

> So the only thing I can think of is to output some name that includes
> both the child type and parent type name (to make it obvious which
> conversion is being done):
>
> qapi_Child_Grandparent(child)
> qapi_Child_Parent(child)

We don't feel the need to encode a function's return type in its name
elsewhere, so why do it here?

> At this point, I'm leaning towards client casts, just because of the
> verbosity, but I'll at least try the generated type-safe functions in
> v10 to see how bad it really is.  A patch to discuss will make it easier
> to decide whether to paint this bikeshed.

I try to avoid casts, especially pointer casts, because it's essentially
telling the compiler "shut up, I know what I'm doing".  But I'm
unwilling to trade much readability for fewer Let's see how things work
out here.

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

* Re: [Qemu-devel] [PATCH v9 06/17] qapi-visit: Remove redundant functions for flat union base
  2015-10-21 19:01     ` Eric Blake
@ 2015-10-22  8:32       ` Markus Armbruster
  0 siblings, 0 replies; 57+ messages in thread
From: Markus Armbruster @ 2015-10-22  8:32 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 10/21/2015 11:36 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> The code for visiting the base class of a child struct created
>>> visit_type_Base_fields() which covers all fields of Base; while
>>> the code for visiting the base class of a flat union created
>>> visit_type_Union_fields() covering all fields of the base
>>> except the discriminator.  But if the base class were to always
>>> visit all its fields, then we wouldn't need a separate visit of
>>> the discriminator for a union.  Not only is consistently
>>> visiting all fields easier to understand, it lets us share code.
>>>
>>> Now that gen_visit_struct_fields() can potentially collect more
>>> than one function into 'ret', a regular expression searching for
>>> whether a label was used may hit a false positive within the
>>> body of the first function.  But using a regex was overkill,
>>> since we can easily determine when we jumped to a label.
>>>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>>
>
>>> +++ b/scripts/qapi-visit.py
>>> @@ -90,7 +90,7 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e
>>>
>>>      ret += gen_visit_fields(members, prefix='(*obj)->')
>>>
>>> -    if re.search('^ *goto out;', ret, re.MULTILINE):
>>> +    if base or members:
>> 
>> What if we have an empty base and no members?  Empty base is a
>> pathological case, admittedly.
>
> I'm going to filter the re.search cleanups into its own prereq patch.
> But yes, it will need to care for empty base and no members (hmm, I
> really ought to add positive tests to qapi-schema-test for an empty
> inherited struct, to make sure I'm getting it right - even if we don't
> want that patch in the final series).

Don't take my reluctance to take some positive tests as general
opposition towards positive tests!  Positive tests for corner cases like
"empty base" are valuable.

>> Diff is confusing (not your fault).  Let me compare code before and
>> after real slow.
>
> I also plan for v10 to include a diff of the generated code in the
> commit message, if that will help make the change more obvious.
>
>> 
>> = Before =
>> 
>>   def gen_visit_union(name, base, variants):
>>       ret = ''
>> 
>> 0. base is None if and only if the union is simple.
>> 
>> 1. If it's a flat union, generate its visit_type_NAME_fields().  This
>
> where NAME is the union name.
>
>> function visits the union's non-variant members *except* the
>> discriminator.  Since a simple union has no non-variant members other
>> than the discriminator, generate it only for flat unions.
>> 
>>       if base:
>>           members = [m for m in base.members if m != variants.tag_member]
>>           ret += gen_visit_struct_fields(name, None, members)
>> 
>> 2. Generate the visit_type_implicit_FOO() we're going to need.
>> 
>>       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_implicit_struct(var.type)
>
> Could be made slightly simpler by generating these while we iterate over
> cases (but we'd have to be careful to generate into multiple variables,
> and then concat together at the end, since we can't generate one
> function in the body of the other).

I doubt it would be an improvement.  The loop is trivial, so
de-duplicating it doesn't have much value.  Having generator code
arranged in the same order as the generated code *does* have value.

>> 3. Generate its visit_type_NAME().
>> 
>
>> 
>> 3.a. If it's a flat union, generate the call of
>> visit_type_NAME_fields().  Not necessary for simple unions, see 1.
>
> Again, important to note that this was visit_type_UNION_fields().
>
>> 3.b. Generate visit of discriminator.
>> 
>
>> 
>> 3.c. Generate visit of the active variant.
>> 
>
>> = After =
>> 
>>   def gen_visit_union(name, base, variants):
>>       ret = ''
>> 
>> 0. base is None if and only if the union is simple.
>> 
>> 1. If it's a flat union, generate its visit_type_NAME_fields().  This
>> function visits the union's non-variant members *including* the
>> discriminator.  However, we generate it only for flat unions.  Simple
>> unions have no non-variant members other than the discriminator.
>> 
>>       if base:
>>           ret += gen_visit_struct_fields(base.name, base.base,
>>                                          base.local_members)
>
> Note that this creates visit_type_BASE_fields() (a different function).

Missed this detail, thanks.  The old visit_type_UNION_fields() is
visit_type_BASE_fields() less the tag visit.  Reusing
visit_type_BASE_fields() instead behaves as I described above, so my
analysis remains valid regardless.

visit_type_BASE_fields() should be generated when gen_visit_struct()
processes BASE.  Here, we should only generate a forward declaration, if
necessary.

>> 
>> 2. Generate the visit_type_implicit_FOO() we're going to need.
>> 
>>       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_implicit_struct(var.type)
>> 
>> 3. Generate its visit_type_NAME().
>> 
>
>> 
>> 3.a. If it's a flat union, generate the call of
>> visit_type_NAME_fields().  Not done for simple unions, see 1.
>
> Again, now NAME is the base name rather than the union name.  Subtle but
> important difference!

Indeed.

Until now, having a separate visit_type_NAME_fields() function was
pretty pointless: it was only called from visit_type_NAME().  Now, it's
also called from visit_type_UNION() when NAME is UNION's base.  Bonus:
we don't duplicate the code visiting NAME's members into every union
using it as base (this is what the commit message refers to when it says
"it lets us share code").

Can we do the same for structs, please?

The nice thing about generating code is that you don't have to
copy/paste so much to get all the duplication your heart desires.

>> 
>>       if base:
>>           ret += mcgen('''
>>       visit_type_%(c_name)s_fields(v, (%(c_name)s **)obj, &err);
>>   ''',
>>                        c_name=base.c_name())
>> 
>> 3.b. If it's a simple union, generate the visit of the sole non-variant
>> member inline.
>> 
>
>> 
>> 3.a+b. Generate the error check for visit of non-variant members
>> 
>>       ret += gen_err_check(label='out_obj')
>> 
>> 3.c. Generate visit of the active variant.
>> 
>
>> 
>> Okay, the change to gen_visit_union() looks sane.
>
> Yes, you got it all correct.
>
>> 
>> Can we go one step further and generate and use visit_type_NAME_fields()
>> even for simple unions?
>
> Not easily.  Remember, for flat unions, we are calling
> visit_type_BASE_fields, not visit_type_UNION_fields.  There is no base
> for a simple union.

Scratch the idea, I was confused.

> What I _am_ planning for future patches is the following:
>
> Change QAPISchemaObjectType for simple unions and alternates to set
> .local_members to the one-element implicit discriminator (right now, it
> is always an empty array, and we even assert that bool(.local_members)
> and bool(.variants) are mutually-exclusive in at least qapi-types.py
> visit_object_type()).  Flat unions would keep .local_members as an empty
> array (there is no local member, just the inherited members from the
> base class, which includes the inherited discriminator).
>
> Then merge gen_visit_struct() and gen_visit_union() to look like:
>
> if base:
>     # includes discriminator for flat union
>     call visit_type_BASE_fields()
> for m in .local_members
>     # includes discriminator for simple union
>     call visit_type_MEMBER()
> if variants
>     emit switch statement to visit each branch
>
> But if we want, that 'for m in .local_members' block can indeed be
> implemented via a call to visit_type_UNION_fields(), if that is any more
> efficient to implement.  I guess it also boils down to deciding if
> visit_type_FOO_fields() should continue to be unconditional (either for
> all types, or at least for all types with non-empty .local_members).

Keeping the implicit tag implicit by not including it in local_members
was a conscious design decision, but I'm quite open to revisit it.

But perhaps we don't have to!  We got simple unions because "first past
the post": they solved immediate needs.  When other needs arose, flat
unions got bolted on.  We kept simple unions because the bolted-on flat
unions are bothersome to define.  If we can fuse struct and union types
into a reasonably pleasant variant record type, we may not need a simple
union sugar anymore.

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

* Re: [Qemu-devel] [PATCH v9 07/17] qapi: Start converting to new qapi union layout
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 07/17] qapi: Start converting to new qapi union layout Eric Blake
@ 2015-10-22 13:54   ` Markus Armbruster
  2015-10-22 14:09     ` Eric Blake
  0 siblings, 1 reply; 57+ messages in thread
From: Markus Armbruster @ 2015-10-22 13:54 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> We have two issues with our qapi union layout:
> 1) Even though the QMP wire format spells the tag 'type', the
> C code spells it 'kind', requiring some hacks in the generator.
> 2) The C struct uses an anonymous union, which places all tag
> values in the same namespace as all non-variant members. This
> leads to spurious collisions if a tag value matches a QMP name.
>
> This patch is the front end for a series that converts to a
> saner qapi union layout.  By the end of the series, we will no
> longer have the type/kind mismatch, and all tag values will be
> under a named union, which requires clients to access
> 'obj->u.value' instead of 'obj->value'.  But since the
> conversion touches a number of files, it is easiest if we
> temporarily support BOTH layouts simultaneously.
>
> Given a simple union qapi type:
>
> { 'union':'Foo', 'data': { 'a':'int', 'b':'bool' } }
>
> we make the following changes in generated qapi-types.h:
>
> | struct Foo {
> |-    FooKind kind;
> |-    union { /* union tag is @kind */
> |+    union {
> |+        FooKind kind;
> |+        FooKind type;
> |+    };
> |+    union { /* union tag is @type */
> |         void *data;
> |         int64_t a;
> |         bool b;
> |+        union { /* union tag is @type */
> |+            void *data;
> |+            int64_t a;
> |+            bool b;
> |+        } u;
> |     };
> | };

This is clever and ugly in equal measure.  I respect that.  Fortunately,
it's also temporary.

> Flat unions do not need the anonymous union for the tag member,
> as we already fixed that to use the member name instead of 'kind'
> back in commit 0f61af3e.

Unlike then, we need multiple commits for simple unions, because they're
more widely used?

>                           On the other hand, the duplication
> means that we temporarily cannot support 'u' as a branch name.

Separate paragraph, because now you're talking about the *other*
anonymous union.

> Later, when the conversions are complete, we will remove the
> duplication hacks and restore support for 'u' as a branch name.
>
> Note, however, that we do not rename the generated enum, which
> is still 'FooKind'.  A further patch could generate implicit
> enums as 'FooType', but that causes more churn to C code, and
> gets harder since the generator already reserved the '*Kind'
> namespace, but there are already QMP constructs with '*Type'
> naming which means we cannot easily reserve it for qapi.

Oh, we can reserve whatever we want in QAPI, it's just a lot of churn to
adapt the QAPI-using code.

I'd simply say "but that would cause substantial churn to C code, as
there are already QAPI definitions with '*Type' naming".

> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: new patch, but incorporates parts of v5 31/46 and Markus' RFC:
> http://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02236.html
> ---
>  scripts/qapi-types.py                   | 26 +++++++++++++++++++-------
>  scripts/qapi-visit.py                   | 24 +++++++++---------------
>  tests/qapi-schema/qapi-schema-test.json |  4 +++-
>  tests/qapi-schema/qapi-schema-test.out  |  4 ++--
>  4 files changed, 33 insertions(+), 25 deletions(-)
>

First part: generate C structs as described in the commit message.

> diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
> index bcef39d..0a14451 100644
> --- a/scripts/qapi-types.py
> +++ b/scripts/qapi-types.py
> @@ -136,11 +136,23 @@ struct %(c_name)s {
>      if base:
>          ret += gen_struct_fields([], base)
>      else:
> +        # TODO As a hack, we emit both 'kind' and 'type'. Ultimately, we
> +        # want to use only 'type', but the conversion is large enough to
> +        # require staging over several commits.
>          ret += mcgen('''
> -    %(c_type)s kind;
> +    union {
> +        %(c_type)s kind;
> +        %(c_type)s type;
> +    };
>  ''',
>                       c_type=c_name(variants.tag_member.type.name))
>
> +    # TODO As a hack, we emit the union twice, once as an anonymous union
> +    # and once as a named union.  Ultimately, we want to use only the
> +    # named union version (as it avoids conflicts between tag values as
> +    # branch names competing with non-variant QMP names), but the conversion
> +    # is large enough to require staging over several commits.
> +    tmp = ''
>      # 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
> @@ -149,25 +161,25 @@ struct %(c_name)s {
>      # 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('''
> +    tmp += mcgen('''
>      union { /* union tag is @%(c_name)s */
>          void *data;
>  ''',
> -                 # TODO ugly special case for simple union
> -                 # Use same tag name in C as on the wire to get rid of
> -                 # it, then: c_name=c_name(variants.tag_member.name)
> -                 c_name=c_name(variants.tag_name or 'kind'))
> +                 c_name=c_name(variants.tag_member.name))
>
>      for var in variants.variants:
>          # Ugly special case for simple union TODO get rid of it
>          typ = var.simple_union_type() or var.type
> -        ret += mcgen('''
> +        tmp += mcgen('''
>          %(c_type)s %(c_name)s;
>  ''',
>                       c_type=typ.c_type(),
>                       c_name=c_name(var.name))
>
> +    ret += tmp
> +    ret += '    ' + '\n    '.join(tmp.split('\n'))
>      ret += mcgen('''
> +    } u;
>      };
>  };
>  ''')

It took me some head-scratching to understand why this generates
correctly indented output.  If it wasn't temporary code, I'd ask for
cleanup.

Second part: convert qapi-visit.py.  Not mentioned in commit message.
Separate patch, perhaps?

> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 91bf350..2afe811 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -182,18 +182,18 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
>      if (err) {
>          goto out;
>      }
> -    visit_get_next_type(v, (int*) &(*obj)->kind, %(c_name)s_qtypes, name, &err);
> +    visit_get_next_type(v, (int*) &(*obj)->type, %(c_name)s_qtypes, name, &err);
>      if (err) {
>          goto out_obj;
>      }
> -    switch ((*obj)->kind) {
> +    switch ((*obj)->type) {
>  ''',
>                  c_name=c_name(name))
>
>      for var in variants.variants:
>          ret += mcgen('''
>      case %(case)s:
> -        visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, name, &err);
> +        visit_type_%(c_type)s(v, &(*obj)->u.%(c_name)s, name, &err);
>          break;
>  ''',
>                       case=c_enum_const(variants.tag_member.type.name,
> @@ -255,22 +255,16 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
>      visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "%(name)s", &err);
>  ''',
>                       c_type=variants.tag_member.type.c_name(),
> -                     # TODO ugly special case for simple union
> -                     # Use same tag name in C as on the wire to get rid of
> -                     # it, then: c_name=c_name(variants.tag_member.name)
> -                     c_name='kind',
> +                     c_name=c_name(variants.tag_member.name),
>                       name=variants.tag_member.name)
>      ret += gen_err_check(label='out_obj')
>      ret += mcgen('''
> -    if (!visit_start_union(v, !!(*obj)->data, &err) || err) {
> +    if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
>          goto out_obj;
>      }
>      switch ((*obj)->%(c_name)s) {
>  ''',
> -                 # TODO ugly special case for simple union
> -                 # Use same tag name in C as on the wire to get rid of
> -                 # it, then: c_name=c_name(variants.tag_member.name)
> -                 c_name=c_name(variants.tag_name or 'kind'))
> +                 c_name=c_name(variants.tag_member.name))
>
>      for var in variants.variants:
>          # TODO ugly special case for simple union
> @@ -282,13 +276,13 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
>                                         var.name))
>          if simple_union_type:
>              ret += mcgen('''
> -        visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "data", &err);
> +        visit_type_%(c_type)s(v, &(*obj)->u.%(c_name)s, "data", &err);
>  ''',
>                           c_type=simple_union_type.c_name(),
>                           c_name=c_name(var.name))
>          else:
>              ret += mcgen('''
> -        visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err);
> +        visit_type_implicit_%(c_type)s(v, &(*obj)->u.%(c_name)s, &err);
>  ''',
>                           c_type=var.type.c_name(),
>                           c_name=c_name(var.name))
> @@ -304,7 +298,7 @@ out_obj:
>      error_propagate(errp, err);
>      err = NULL;
>      if (*obj) {
> -        visit_end_union(v, !!(*obj)->data, &err);
> +        visit_end_union(v, !!(*obj)->u.data, &err);
>      }
>      error_propagate(errp, err);
>      err = NULL;

Third part: work around temporary clash with 'u'.  Needs to remain in
this patch, obviously.  Suggest to amend the commit message to say

    On the other hand, the duplication means that we temporarily cannot
    support 'u' as a branch name.  Adapt a few tests that do.

> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index 22e15eb..876ce18 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -113,8 +113,10 @@
>  # should still be valid as a type or union branch name. And although
>  # '*Kind' and '*List' are forbidden as type names, they should not be
>  # forbidden as a member or branch name.
> +# TODO - we temporarily do not support 'u' as branch name, while converting
> +# code to use the new union layout
>  { 'struct': 'has_a', 'data': { 'MyKind': 'int', 'MyList': ['int'] } }
> -{ 'union': 'u', 'data': { 'u': 'uint8', 'myKind': 'has_a',
> +{ 'union': 'u', 'data': { 'u8': 'uint8', 'myKind': 'has_a',
>                            'myList': 'has_a' } }
>
>  # testing commands
> diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
> index feaf20d..cb12435 100644
> --- a/tests/qapi-schema/qapi-schema-test.out
> +++ b/tests/qapi-schema/qapi-schema-test.out
> @@ -202,10 +202,10 @@ object has_a
>      member MyKind: int optional=False
>      member MyList: intList optional=False
>  object u
> -    case u: :obj-uint8-wrapper
> +    case u8: :obj-uint8-wrapper
>      case myKind: :obj-has_a-wrapper
>      case myList: :obj-has_a-wrapper
> -enum uKind ['u', 'myKind', 'myList']
> +enum uKind ['u8', 'myKind', 'myList']
>  command user_def_cmd None -> None
>     gen=True success_response=True
>  command user_def_cmd1 :obj-user_def_cmd1-arg -> None

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

* Re: [Qemu-devel] [PATCH v9 08/17] tests: Convert to new qapi union layout
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 08/17] tests: Convert " Eric Blake
@ 2015-10-22 14:01   ` Markus Armbruster
  2015-10-22 14:22     ` Eric Blake
  0 siblings, 1 reply; 57+ messages in thread
From: Markus Armbruster @ 2015-10-22 14:01 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> We have two issues with our qapi union layout:
> 1) Even though the QMP wire format spells the tag 'type', the
> C code spells it 'kind', requiring some hacks in the generator.
> 2) The C struct uses an anonymous union, which places all tag
> values in the same namespace as all non-variant members. This
> leads to spurious collisions if a tag value matches a QMP name.
>
> Make the conversion to the new layout for testsuite code.
>
> Note that this includes updating an error message regarding a
> collision.  After the conversion to the new union qapi layout
> is complete, there will still be further patches for cleaning
> up collision detection, since the use of a named union can
> completely eliminate the collision wording changed here.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: new patch, but incorporates parts of v5 31/46 and Markus' RFC:
> http://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02236.html
> ---
>  scripts/qapi.py                         |  2 +-
>  tests/qapi-schema/union-clash-type.err  |  2 +-
>  tests/qapi-schema/union-clash-type.json |  6 +--
>  tests/test-qmp-commands.c               |  4 +-
>  tests/test-qmp-input-visitor.c          | 78 ++++++++++++++++-----------------
>  tests/test-qmp-output-visitor.c         | 42 +++++++++---------
>  6 files changed, 66 insertions(+), 68 deletions(-)
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 1e7e08b..aab2b46 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -546,7 +546,7 @@ def check_union(expr, expr_info):
>      base = expr.get('base')
>      discriminator = expr.get('discriminator')
>      members = expr['data']
> -    values = {'MAX': '(automatic)', 'KIND': '(automatic)'}
> +    values = {'MAX': '(automatic)', 'TYPE': '(automatic tag)'}
>
>      # Two types of unions, determined by discriminator.
>

Umm, does this really belong to this patch?

> diff --git a/tests/qapi-schema/union-clash-type.err b/tests/qapi-schema/union-clash-type.err
> index a5dead1..c14bbdd 100644
> --- a/tests/qapi-schema/union-clash-type.err
> +++ b/tests/qapi-schema/union-clash-type.err
> @@ -1 +1 @@
> -tests/qapi-schema/union-clash-type.json:8: Union 'TestUnion' member 'kind' clashes with '(automatic)'
> +tests/qapi-schema/union-clash-type.json:6: Union 'TestUnion' member 'type' clashes with '(automatic tag)'
> diff --git a/tests/qapi-schema/union-clash-type.json b/tests/qapi-schema/union-clash-type.json
> index cfc256b..641b2d5 100644
> --- a/tests/qapi-schema/union-clash-type.json
> +++ b/tests/qapi-schema/union-clash-type.json
> @@ -1,9 +1,7 @@
>  # Union branch 'type'
>  # Reject this, because we would have a clash in generated C, between the
> -# simple union's implicit tag member 'kind' and the branch name 'kind'
> +# simple union's implicit tag member 'type' and the branch name 'type'
>  # within the union.
> -# TODO: Even when the generated C is switched to use 'type' rather than
> -# 'kind', to match the QMP spelling, the collision should still be detected.
> -# Or, we could munge the branch name to allow compilation.
> +# TODO: If desired, we could munge the branch name to allow compilation.

Let's mark it TODO only if we intend to revisit the idea of munging
branch names.

>  { 'union': 'TestUnion',
>    'data': { 'kind': 'int', 'type': 'str' } }
[Mind-numbing mechanical switch to u. and from kind to type...]

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

* Re: [Qemu-devel] [PATCH v9 07/17] qapi: Start converting to new qapi union layout
  2015-10-22 13:54   ` Markus Armbruster
@ 2015-10-22 14:09     ` Eric Blake
  2015-10-22 14:44       ` Markus Armbruster
  0 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-22 14:09 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 10/22/2015 07:54 AM, Markus Armbruster wrote:

> 
> This is clever and ugly in equal measure.  I respect that.  Fortunately,
> it's also temporary.
> 
>> Flat unions do not need the anonymous union for the tag member,
>> as we already fixed that to use the member name instead of 'kind'
>> back in commit 0f61af3e.
> 
> Unlike then, we need multiple commits for simple unions, because they're
> more widely used?

Yes. In fact, both you and I expressed surprise back then that the main
body of qemu didn't need adjusting - our only use of flat unions was
hidden behind QDict manipulations rather than direct generated qapi
struct, explaining why nothing was affected when we converted flat
unions.  But a useful note for the commit message at any rate.

> 
>>                           On the other hand, the duplication
>> means that we temporarily cannot support 'u' as a branch name.
> 
> Separate paragraph, because now you're talking about the *other*
> anonymous union.
> 
>> Later, when the conversions are complete, we will remove the
>> duplication hacks and restore support for 'u' as a branch name.

And based on comments on 3/17, I'm deferring any testsuite changes
related to 'u' collisions until after this conversion to inline base is
complete, so this part of the commit message actually disappears in v10
because I'm no longer touching qapi-schema-test this early.

>>
>> Note, however, that we do not rename the generated enum, which
>> is still 'FooKind'.  A further patch could generate implicit
>> enums as 'FooType', but that causes more churn to C code, and
>> gets harder since the generator already reserved the '*Kind'
>> namespace, but there are already QMP constructs with '*Type'
>> naming which means we cannot easily reserve it for qapi.
> 
> Oh, we can reserve whatever we want in QAPI, it's just a lot of churn to
> adapt the QAPI-using code.
> 
> I'd simply say "but that would cause substantial churn to C code, as
> there are already QAPI definitions with '*Type' naming".

Okay.


>>      for var in variants.variants:
>>          # Ugly special case for simple union TODO get rid of it
>>          typ = var.simple_union_type() or var.type
>> -        ret += mcgen('''
>> +        tmp += mcgen('''
>>          %(c_type)s %(c_name)s;
>>  ''',
>>                       c_type=typ.c_type(),
>>                       c_name=c_name(var.name))
>>
>> +    ret += tmp
>> +    ret += '    ' + '\n    '.join(tmp.split('\n'))
>>      ret += mcgen('''
>> +    } u;
>>      };
>>  };
>>  ''')
> 
> It took me some head-scratching to understand why this generates
> correctly indented output.  If it wasn't temporary code, I'd ask for
> cleanup.

Would a comment help?  It's because we add 4 spaces after each newline,
but need an indent prior to the first line of tmp, and the '} u;' line
picks up four spaces after the last line of tmp.

> 
> Second part: convert qapi-visit.py.  Not mentioned in commit message.
> Separate patch, perhaps?

Sure, I could split.


> 
> Third part: work around temporary clash with 'u'.  Needs to remain in
> this patch, obviously.  Suggest to amend the commit message to say
> 
>     On the other hand, the duplication means that we temporarily cannot
>     support 'u' as a branch name.  Adapt a few tests that do.

Or, rather, dropped entirely, because the tests for collisions with 'u'
will be deferred until after the conversion is complete.

-- 
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] 57+ messages in thread

* Re: [Qemu-devel] [PATCH v9 15/17] tpm: Convert to new qapi union layout
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 15/17] tpm: " Eric Blake
@ 2015-10-22 14:19   ` Markus Armbruster
  2015-10-22 14:26     ` Eric Blake
  0 siblings, 1 reply; 57+ messages in thread
From: Markus Armbruster @ 2015-10-22 14:19 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Luiz Capitulino

Eric Blake <eblake@redhat.com> writes:

> We have two issues with our qapi union layout:
> 1) Even though the QMP wire format spells the tag 'type', the
> C code spells it 'kind', requiring some hacks in the generator.
> 2) The C struct uses an anonymous union, which places all tag
> values in the same namespace as all non-variant members. This
> leads to spurious collisions if a tag value matches a QMP name.
>
> Make the conversion to the new layout for TPM-related code.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>

PATCH 08-15 appear to be a purely mechanical switch to u. and from kind
to type, except for a qapi.py hunk that looks like it should be in PATCH
07, and a comment update to tests/qapi-schema/union-clash-type.json.
Did I miss anything?

Combined diffstat isn't so bad:

 36 files changed, 393 insertions(+), 394 deletions(-)

I've seen worse tree-wide changes, some of them my own.  I'd be tempted
to squash the complete switch together.  But squashing is easy, so we
can keep it separate while we review, and decide when we're done.

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

* Re: [Qemu-devel] [PATCH v9 08/17] tests: Convert to new qapi union layout
  2015-10-22 14:01   ` Markus Armbruster
@ 2015-10-22 14:22     ` Eric Blake
  2015-10-22 14:57       ` Markus Armbruster
  0 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-22 14:22 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 10/22/2015 08:01 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> We have two issues with our qapi union layout:
>> 1) Even though the QMP wire format spells the tag 'type', the
>> C code spells it 'kind', requiring some hacks in the generator.
>> 2) The C struct uses an anonymous union, which places all tag
>> values in the same namespace as all non-variant members. This
>> leads to spurious collisions if a tag value matches a QMP name.
>>
>> Make the conversion to the new layout for testsuite code.
>>
>> Note that this includes updating an error message regarding a
>> collision.  After the conversion to the new union qapi layout
>> is complete, there will still be further patches for cleaning
>> up collision detection, since the use of a named union can
>> completely eliminate the collision wording changed here.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>
>> ---
>> v9: new patch, but incorporates parts of v5 31/46 and Markus' RFC:
>> http://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02236.html
>> ---
>>  scripts/qapi.py                         |  2 +-
>>  tests/qapi-schema/union-clash-type.err  |  2 +-
>>  tests/qapi-schema/union-clash-type.json |  6 +--
>>  tests/test-qmp-commands.c               |  4 +-
>>  tests/test-qmp-input-visitor.c          | 78 ++++++++++++++++-----------------
>>  tests/test-qmp-output-visitor.c         | 42 +++++++++---------
>>  6 files changed, 66 insertions(+), 68 deletions(-)
>>
>> diff --git a/scripts/qapi.py b/scripts/qapi.py
>> index 1e7e08b..aab2b46 100644
>> --- a/scripts/qapi.py
>> +++ b/scripts/qapi.py
>> @@ -546,7 +546,7 @@ def check_union(expr, expr_info):
>>      base = expr.get('base')
>>      discriminator = expr.get('discriminator')
>>      members = expr['data']
>> -    values = {'MAX': '(automatic)', 'KIND': '(automatic)'}
>> +    values = {'MAX': '(automatic)', 'TYPE': '(automatic tag)'}
>>
>>      # Two types of unions, determined by discriminator.
>>
> 
> Umm, does this really belong to this patch?

Yes, because it cleans up the error message in union-clash-type.err, as
mentioned in the commit message. But since I'm already splitting out the
qapi-visit parts of 7/17, maybe it belongs better in that patch (all
changes to the rest of qapi to deal with the qapi-type parts)?

> 
>> diff --git a/tests/qapi-schema/union-clash-type.err b/tests/qapi-schema/union-clash-type.err
>> index a5dead1..c14bbdd 100644
>> --- a/tests/qapi-schema/union-clash-type.err
>> +++ b/tests/qapi-schema/union-clash-type.err
>> @@ -1 +1 @@
>> -tests/qapi-schema/union-clash-type.json:8: Union 'TestUnion' member 'kind' clashes with '(automatic)'
>> +tests/qapi-schema/union-clash-type.json:6: Union 'TestUnion' member 'type' clashes with '(automatic tag)'

At any rate, this is what improved by adjusting that line of qapi.py.

>> diff --git a/tests/qapi-schema/union-clash-type.json b/tests/qapi-schema/union-clash-type.json
>> index cfc256b..641b2d5 100644
>> --- a/tests/qapi-schema/union-clash-type.json
>> +++ b/tests/qapi-schema/union-clash-type.json
>> @@ -1,9 +1,7 @@
>>  # Union branch 'type'
>>  # Reject this, because we would have a clash in generated C, between the
>> -# simple union's implicit tag member 'kind' and the branch name 'kind'
>> +# simple union's implicit tag member 'type' and the branch name 'type'
>>  # within the union.
>> -# TODO: Even when the generated C is switched to use 'type' rather than
>> -# 'kind', to match the QMP spelling, the collision should still be detected.
>> -# Or, we could munge the branch name to allow compilation.
>> +# TODO: If desired, we could munge the branch name to allow compilation.
> 
> Let's mark it TODO only if we intend to revisit the idea of munging
> branch names.

I have a later patch queued that deletes this test altogether, so for
v10 I'll probably just eliminate changes here for less churn.
https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg04111.html

> 
>>  { 'union': 'TestUnion',
>>    'data': { 'kind': 'int', 'type': 'str' } }
> [Mind-numbing mechanical switch to u. and from kind to type...]
> 

Yep.  I wish I knew coccinelle well enough to see if it could do the
conversion for me, but I ended up doing it by hand (basically by
applying 16/17 early, then seeing what failed to compile, fixing it up,
then rebasing it into position).

-- 
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] 57+ messages in thread

* Re: [Qemu-devel] [PATCH v9 15/17] tpm: Convert to new qapi union layout
  2015-10-22 14:19   ` Markus Armbruster
@ 2015-10-22 14:26     ` Eric Blake
  2015-10-22 16:40       ` Eric Blake
  0 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-22 14:26 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Luiz Capitulino

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

On 10/22/2015 08:19 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> We have two issues with our qapi union layout:
>> 1) Even though the QMP wire format spells the tag 'type', the
>> C code spells it 'kind', requiring some hacks in the generator.
>> 2) The C struct uses an anonymous union, which places all tag
>> values in the same namespace as all non-variant members. This
>> leads to spurious collisions if a tag value matches a QMP name.
>>
>> Make the conversion to the new layout for TPM-related code.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
> 
> PATCH 08-15 appear to be a purely mechanical switch to u. and from kind
> to type, except for a qapi.py hunk that looks like it should be in PATCH
> 07, and a comment update to tests/qapi-schema/union-clash-type.json.
> Did I miss anything?
> 
> Combined diffstat isn't so bad:
> 
>  36 files changed, 393 insertions(+), 394 deletions(-)

It already needs a rebase; some of Dan's work has caused more changes to
ui/vnc.c and util/qemu-sockets.c.  So hopefully I post v10 soon.

> 
> I've seen worse tree-wide changes, some of them my own.  I'd be tempted
> to squash the complete switch together.  But squashing is easy, so we
> can keep it separate while we review, and decide when we're done.

Sure, v10 will keep things separate, but squashing won't hurt too much.
 After all, v5 had it all as one patch.

-- 
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] 57+ messages in thread

* Re: [Qemu-devel] [PATCH v9 07/17] qapi: Start converting to new qapi union layout
  2015-10-22 14:09     ` Eric Blake
@ 2015-10-22 14:44       ` Markus Armbruster
  0 siblings, 0 replies; 57+ messages in thread
From: Markus Armbruster @ 2015-10-22 14:44 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 10/22/2015 07:54 AM, Markus Armbruster wrote:
>
>> 
>> This is clever and ugly in equal measure.  I respect that.  Fortunately,
>> it's also temporary.
>> 
>>> Flat unions do not need the anonymous union for the tag member,
>>> as we already fixed that to use the member name instead of 'kind'
>>> back in commit 0f61af3e.
>> 
>> Unlike then, we need multiple commits for simple unions, because they're
>> more widely used?
>
> Yes. In fact, both you and I expressed surprise back then that the main
> body of qemu didn't need adjusting - our only use of flat unions was
> hidden behind QDict manipulations rather than direct generated qapi
> struct, explaining why nothing was affected when we converted flat
> unions.  But a useful note for the commit message at any rate.
>
>> 
>>>                           On the other hand, the duplication
>>> means that we temporarily cannot support 'u' as a branch name.
>> 
>> Separate paragraph, because now you're talking about the *other*
>> anonymous union.
>> 
>>> Later, when the conversions are complete, we will remove the
>>> duplication hacks and restore support for 'u' as a branch name.
>
> And based on comments on 3/17, I'm deferring any testsuite changes
> related to 'u' collisions until after this conversion to inline base is
> complete, so this part of the commit message actually disappears in v10
> because I'm no longer touching qapi-schema-test this early.
>
>>>
>>> Note, however, that we do not rename the generated enum, which
>>> is still 'FooKind'.  A further patch could generate implicit
>>> enums as 'FooType', but that causes more churn to C code, and
>>> gets harder since the generator already reserved the '*Kind'
>>> namespace, but there are already QMP constructs with '*Type'
>>> naming which means we cannot easily reserve it for qapi.
>> 
>> Oh, we can reserve whatever we want in QAPI, it's just a lot of churn to
>> adapt the QAPI-using code.
>> 
>> I'd simply say "but that would cause substantial churn to C code, as
>> there are already QAPI definitions with '*Type' naming".
>
> Okay.
>
>
>>>      for var in variants.variants:
>>>          # Ugly special case for simple union TODO get rid of it
>>>          typ = var.simple_union_type() or var.type
>>> -        ret += mcgen('''
>>> +        tmp += mcgen('''
>>>          %(c_type)s %(c_name)s;
>>>  ''',
>>>                       c_type=typ.c_type(),
>>>                       c_name=c_name(var.name))
>>>
>>> +    ret += tmp
>>> +    ret += '    ' + '\n    '.join(tmp.split('\n'))
>>>      ret += mcgen('''
>>> +    } u;
>>>      };
>>>  };
>>>  ''')
>> 
>> It took me some head-scratching to understand why this generates
>> correctly indented output.  If it wasn't temporary code, I'd ask for
>> cleanup.
>
> Would a comment help?  It's because we add 4 spaces after each newline,
> but need an indent prior to the first line of tmp, and the '} u;' line
> picks up four spaces after the last line of tmp.

Yes.  Let's not worry about it, it's just temporary scaffolding.

>> Second part: convert qapi-visit.py.  Not mentioned in commit message.
>> Separate patch, perhaps?
>
> Sure, I could split.
>
>
>> 
>> Third part: work around temporary clash with 'u'.  Needs to remain in
>> this patch, obviously.  Suggest to amend the commit message to say
>> 
>>     On the other hand, the duplication means that we temporarily cannot
>>     support 'u' as a branch name.  Adapt a few tests that do.
>
> Or, rather, dropped entirely, because the tests for collisions with 'u'
> will be deferred until after the conversion is complete.

If deferring is easy, go for it.

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

* Re: [Qemu-devel] [PATCH v9 16/17] qapi: Finish converting to new qapi union layout
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 16/17] qapi: Finish converting " Eric Blake
@ 2015-10-22 14:50   ` Markus Armbruster
  0 siblings, 0 replies; 57+ messages in thread
From: Markus Armbruster @ 2015-10-22 14:50 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> We have two issues with our qapi union layout:
> 1) Even though the QMP wire format spells the tag 'type', the
> C code spells it 'kind', requiring some hacks in the generator.
> 2) The C struct uses an anonymous union, which places all tag
> values in the same namespace as all non-variant members. This
> leads to spurious collisions if a tag value matches a QMP name.
>
> This patch is the back end for a series that converts to a
> saner qapi union layout.  Now that all clients have been
> converted to use 'type' and 'obj->u.value', we can drop the
> temporary support for 'kind' and 'obj->value', and undo the
> temporary restriction against 'u' as a branch name.
>
> Given a simple union qapi type:
>
> { 'union':'Foo', 'data': { 'a':'int', 'b':'bool' } }
>
> this is the overall effect, when compared to the state before
> this series of patches:
>
> | struct Foo {
> |-    FooKind kind;
> |-    union { /* union tag is @kind */
> |+    FooKind type;
> |+    union { /* union tag is @type */
> |         void *data;
> |         int64_t a;
> |         bool b;
> |-    };
> |+    } u;
> | };
>
> Note, however, that we do not rename the generated enum, which
> is still 'FooKind'.  A further patch could generate implicit
> enums as 'FooType', but that causes more churn to C code, and
> gets harder since the generator already reserved the '*Kind'
> namespace, but there are already QMP constructs with '*Type'
> naming which means we cannot easily reserve it for qapi.
> Signed-off-by: Eric Blake <eblake@redhat.com>

If you edit the commit message of PATCH 07, make sure to edit this one
to match.

Patch looks good.

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

* Re: [Qemu-devel] [PATCH v9 08/17] tests: Convert to new qapi union layout
  2015-10-22 14:22     ` Eric Blake
@ 2015-10-22 14:57       ` Markus Armbruster
  0 siblings, 0 replies; 57+ messages in thread
From: Markus Armbruster @ 2015-10-22 14:57 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 10/22/2015 08:01 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> We have two issues with our qapi union layout:
>>> 1) Even though the QMP wire format spells the tag 'type', the
>>> C code spells it 'kind', requiring some hacks in the generator.
>>> 2) The C struct uses an anonymous union, which places all tag
>>> values in the same namespace as all non-variant members. This
>>> leads to spurious collisions if a tag value matches a QMP name.
>>>
>>> Make the conversion to the new layout for testsuite code.
>>>
>>> Note that this includes updating an error message regarding a
>>> collision.  After the conversion to the new union qapi layout
>>> is complete, there will still be further patches for cleaning
>>> up collision detection, since the use of a named union can
>>> completely eliminate the collision wording changed here.
>>>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>>
>>> ---
>>> v9: new patch, but incorporates parts of v5 31/46 and Markus' RFC:
>>> http://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02236.html
>>> ---
>>>  scripts/qapi.py                         |  2 +-
>>>  tests/qapi-schema/union-clash-type.err  |  2 +-
>>>  tests/qapi-schema/union-clash-type.json |  6 +--
>>>  tests/test-qmp-commands.c               |  4 +-
>>>  tests/test-qmp-input-visitor.c          | 78 ++++++++++++++++-----------------
>>>  tests/test-qmp-output-visitor.c         | 42 +++++++++---------
>>>  6 files changed, 66 insertions(+), 68 deletions(-)
>>>
>>> diff --git a/scripts/qapi.py b/scripts/qapi.py
>>> index 1e7e08b..aab2b46 100644
>>> --- a/scripts/qapi.py
>>> +++ b/scripts/qapi.py
>>> @@ -546,7 +546,7 @@ def check_union(expr, expr_info):
>>>      base = expr.get('base')
>>>      discriminator = expr.get('discriminator')
>>>      members = expr['data']
>>> -    values = {'MAX': '(automatic)', 'KIND': '(automatic)'}
>>> +    values = {'MAX': '(automatic)', 'TYPE': '(automatic tag)'}
>>>
>>>      # Two types of unions, determined by discriminator.
>>>
>> 
>> Umm, does this really belong to this patch?
>
> Yes, because it cleans up the error message in union-clash-type.err, as
> mentioned in the commit message. But since I'm already splitting out the
> qapi-visit parts of 7/17, maybe it belongs better in that patch (all
> changes to the rest of qapi to deal with the qapi-type parts)?

Makes sense, I think.

>>> diff --git a/tests/qapi-schema/union-clash-type.err
>>> b/tests/qapi-schema/union-clash-type.err
>>> index a5dead1..c14bbdd 100644
>>> --- a/tests/qapi-schema/union-clash-type.err
>>> +++ b/tests/qapi-schema/union-clash-type.err
>>> @@ -1 +1 @@
>>> -tests/qapi-schema/union-clash-type.json:8: Union 'TestUnion'
>>> member 'kind' clashes with '(automatic)'
>>> +tests/qapi-schema/union-clash-type.json:6: Union 'TestUnion'
>>> member 'type' clashes with '(automatic tag)'
>
> At any rate, this is what improved by adjusting that line of qapi.py.
>
>>> diff --git a/tests/qapi-schema/union-clash-type.json
>>> b/tests/qapi-schema/union-clash-type.json
>>> index cfc256b..641b2d5 100644
>>> --- a/tests/qapi-schema/union-clash-type.json
>>> +++ b/tests/qapi-schema/union-clash-type.json
>>> @@ -1,9 +1,7 @@
>>>  # Union branch 'type'
>>>  # Reject this, because we would have a clash in generated C, between the
>>> -# simple union's implicit tag member 'kind' and the branch name 'kind'
>>> +# simple union's implicit tag member 'type' and the branch name 'type'
>>>  # within the union.
>>> -# TODO: Even when the generated C is switched to use 'type' rather than
>>> -# 'kind', to match the QMP spelling, the collision should still be detected.
>>> -# Or, we could munge the branch name to allow compilation.
>>> +# TODO: If desired, we could munge the branch name to allow compilation.
>> 
>> Let's mark it TODO only if we intend to revisit the idea of munging
>> branch names.
>
> I have a later patch queued that deletes this test altogether, so for
> v10 I'll probably just eliminate changes here for less churn.
> https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg04111.html
>
>> 
>>>  { 'union': 'TestUnion',
>>>    'data': { 'kind': 'int', 'type': 'str' } }
>> [Mind-numbing mechanical switch to u. and from kind to type...]
>> 
>
> Yep.  I wish I knew coccinelle well enough to see if it could do the
> conversion for me, but I ended up doing it by hand (basically by
> applying 16/17 early, then seeing what failed to compile, fixing it up,
> then rebasing it into position).

I might be able to sledgehammer it into service here, but since you've
done the job already...

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

* Re: [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B')
  2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
                   ` (16 preceding siblings ...)
  2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 17/17] qapi: Simplify gen_struct_field() Eric Blake
@ 2015-10-22 15:13 ` Markus Armbruster
  17 siblings, 0 replies; 57+ messages in thread
From: Markus Armbruster @ 2015-10-22 15:13 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Eduardo Habkost

Eric Blake <eblake@redhat.com> writes:

> Pending prerequisite: Markus' qapi-next branch (which has the first
> half of my subset B patches):
> git://repo.or.cz/qemu/armbru.git pull-qapi-2015-10-15
> https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg03321.html
>
> Pending prerequisite: Paolo's rewrite of qemu-char:
> https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg03238.html
>
> Also available as a tag at this location:
> git fetch git://repo.or.cz/qemu/ericb.git qapi-cleanupv9b
>
> and I plan to eventually forcefully update my branch with the rest
> of the v5 series, at:
> http://repo.or.cz/qemu/ericb.git/shortlog/refs/heads/qapi

I expect some churn in v10 due to fine-tuning of patch order mostly, but
I feel we're getting close.

Meanwhile: on to subset C!

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

* Re: [Qemu-devel] [PATCH v9 15/17] tpm: Convert to new qapi union layout
  2015-10-22 14:26     ` Eric Blake
@ 2015-10-22 16:40       ` Eric Blake
  2015-10-23  6:24         ` Markus Armbruster
  0 siblings, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-22 16:40 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Luiz Capitulino

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

On 10/22/2015 08:26 AM, Eric Blake wrote:

>> PATCH 08-15 appear to be a purely mechanical switch to u. and from kind
>> to type, except for a qapi.py hunk that looks like it should be in PATCH
>> 07, and a comment update to tests/qapi-schema/union-clash-type.json.
>> Did I miss anything?
>>
>> Combined diffstat isn't so bad:
>>
>>  36 files changed, 393 insertions(+), 394 deletions(-)
> 
> It already needs a rebase; some of Dan's work has caused more changes to
> ui/vnc.c and util/qemu-sockets.c.  So hopefully I post v10 soon.
> 
>>
>> I've seen worse tree-wide changes, some of them my own.  I'd be tempted
>> to squash the complete switch together.  But squashing is easy, so we
>> can keep it separate while we review, and decide when we're done.
> 
> Sure, v10 will keep things separate, but squashing won't hurt too much.
>  After all, v5 had it all as one patch.

Just so I'm clear, if we wanted to squash, would it be just 8-15 (just
the mechanical changes, but keeping the front-end scaffolding hack and
backend cleanup, and keeping non-mechanical changes split off of 7 and 8
as a separate patch), or the entire 7-16 (no hack at all, and nothing to
split off of 7 and 8)?

-- 
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] 57+ messages in thread

* Re: [Qemu-devel] [PATCH v9 05/17] qapi: Unbox base members
  2015-10-21 13:34       ` Markus Armbruster
  2015-10-21 21:16         ` Eric Blake
@ 2015-10-23  1:50         ` Eric Blake
  2015-10-23  6:26           ` Markus Armbruster
  1 sibling, 1 reply; 57+ messages in thread
From: Eric Blake @ 2015-10-23  1:50 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Michael Roth, Gerd Hoffmann, Luiz Capitulino

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

On 10/21/2015 07:34 AM, Markus Armbruster wrote:

> 
> The least verbose naming convention for a conversion function I can
> think of right now is TBase(), where T is the name of a type with a
> base.  Compare:
> 
>     foo((Parent *)child, blah)
>     foo(ChildBase(child), blah)
> 
> Tolerable?  Worthwhile?

'TBase' won't work. We already have BlockdevOptionsBase as a subtype of
BlockdevOptions, and using 'TBase' would give us
'BlockdevOptionsBase(options)' which is now ambiguous between the type
name and intended function call.  I'll probably go with
qapi_TYPE_base(), and see how long that looks.

-- 
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] 57+ messages in thread

* Re: [Qemu-devel] [PATCH v9 15/17] tpm: Convert to new qapi union layout
  2015-10-22 16:40       ` Eric Blake
@ 2015-10-23  6:24         ` Markus Armbruster
  0 siblings, 0 replies; 57+ messages in thread
From: Markus Armbruster @ 2015-10-23  6:24 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Luiz Capitulino

Eric Blake <eblake@redhat.com> writes:

> On 10/22/2015 08:26 AM, Eric Blake wrote:
>
>>> PATCH 08-15 appear to be a purely mechanical switch to u. and from kind
>>> to type, except for a qapi.py hunk that looks like it should be in PATCH
>>> 07, and a comment update to tests/qapi-schema/union-clash-type.json.
>>> Did I miss anything?
>>>
>>> Combined diffstat isn't so bad:
>>>
>>>  36 files changed, 393 insertions(+), 394 deletions(-)
>> 
>> It already needs a rebase; some of Dan's work has caused more changes to
>> ui/vnc.c and util/qemu-sockets.c.  So hopefully I post v10 soon.
>> 
>>>
>>> I've seen worse tree-wide changes, some of them my own.  I'd be tempted
>>> to squash the complete switch together.  But squashing is easy, so we
>>> can keep it separate while we review, and decide when we're done.
>> 
>> Sure, v10 will keep things separate, but squashing won't hurt too much.
>>  After all, v5 had it all as one patch.
>
> Just so I'm clear, if we wanted to squash, would it be just 8-15 (just
> the mechanical changes, but keeping the front-end scaffolding hack and
> backend cleanup, and keeping non-mechanical changes split off of 7 and 8
> as a separate patch), or the entire 7-16 (no hack at all, and nothing to
> split off of 7 and 8)?

Either way could work.

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

* Re: [Qemu-devel] [PATCH v9 05/17] qapi: Unbox base members
  2015-10-23  1:50         ` Eric Blake
@ 2015-10-23  6:26           ` Markus Armbruster
  0 siblings, 0 replies; 57+ messages in thread
From: Markus Armbruster @ 2015-10-23  6:26 UTC (permalink / raw)
  To: Eric Blake; +Cc: Luiz Capitulino, Gerd Hoffmann, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 10/21/2015 07:34 AM, Markus Armbruster wrote:
>
>> 
>> The least verbose naming convention for a conversion function I can
>> think of right now is TBase(), where T is the name of a type with a
>> base.  Compare:
>> 
>>     foo((Parent *)child, blah)
>>     foo(ChildBase(child), blah)
>> 
>> Tolerable?  Worthwhile?
>
> 'TBase' won't work. We already have BlockdevOptionsBase as a subtype of
> BlockdevOptions, and using 'TBase' would give us
> 'BlockdevOptionsBase(options)' which is now ambiguous between the type
> name and intended function call.  I'll probably go with
> qapi_TYPE_base(), and see how long that looks.

I'd have no problem with renaming things that don't impact wire ABI.
A quick grep suggests churn would be minimal.

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

* Re: [Qemu-devel] [PATCH v9 04/17] vnc: hoist allocation of VncBasicInfo to callers
  2015-10-21 11:16           ` Daniel P. Berrange
@ 2015-10-23 13:13             ` Markus Armbruster
  0 siblings, 0 replies; 57+ messages in thread
From: Markus Armbruster @ 2015-10-23 13:13 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: qemu-devel, Gerd Hoffmann

"Daniel P. Berrange" <berrange@redhat.com> writes:

> On Tue, Oct 20, 2015 at 04:53:24PM -0600, Eric Blake wrote:
>> On 10/20/2015 08:46 AM, Markus Armbruster wrote:
>> > Gerd Hoffmann <kraxel@redhat.com> writes:
>> > 
>> >>   Hi,
>> >>
>> >>>> -static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa,
>> >>>> -                                        socklen_t salen)
>> >>>> +static void vnc_basic_info_get(struct sockaddr_storage *sa,
>> >>>> +                               socklen_t salen,
>> >>>> +                               VncBasicInfo *info,
>> >>>> +                               Error **errp)
>> >>
>> >>> The function no longer "gets info", it merely initializes it.  Rename it
>> >>> accordingly?  Gerd?
>> >>
>> >> vnc_fill_basic_info maybe?
>> > 
>> > Fine with me.  Could also call it _init_ instead of _fill_.
>> 
>> I like init a bit better than fill.
>> 
>> > 
>> >>> Outside this patch's scope, but since I'm looking at the code already...
>> 
>> I'm guessing that also means that fixing it is outside this series' scope.
>> 
>> >>> When vnc_server_info_get() fails, the event is dropped.  Why is that
>> >>> okay?  Failure seems unlikely, though.
>> >>
>> >> Suggestions what else to do?  I don't wanna crash qemu by calling
>> >> qapi_event_send_vnc_* with a NULL pointer.  And, yes, it should be
>> >> highly unlikely so trying some more sophisticated error handling would
>> >> probably be dead code ...
>> > 
>> > These events signal a state change.  Dropping them make me feel uneasy,
>> > because if a client uses them to track state, it gets out of sync.
>> 
>> Events are already best-effort; clients have to be prepared to miss an
>> event - but that's mainly when reconnecting (such as across libvirtd
>> restarts), and not while the monitor is reliably connected.
>> 
>> > The events need to identify the server to be of any use for state
>> > tracking.  Currently, this is members host, service, family of data
>> > member server.
>> > 
>> > We could avoid failures in vnc_qmp_event() as follows:
>> > 
>> > 1. When we create a server, we obtain its info with getsockname() and
>> >    getnameinfo().  If they fail, we fail server creation.  Else, we
>> >    store the info for vnc_qmp_event().
>> > 
>> > 2. When a client connects, we obtain its info with getpeername() and
>> >    getnameinfo().  If they fail, we refuse the connection.  Else, we
>> >    store the infor for vnc_qmp_event().
>> 
>> Seems reasonable to me, but starts to be out of scope for what I'm
>> currently tackling, so is this something I can hand to you, Gerd?
>
> My pending IO channel patches do exactly this
>
> Take a look at the QIOChannelSocket impl
>
>   https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg03440.html
>
> This caches the results of getpeername & getsockname in the
> QOIChannelSocket object.
>
> So my patches which convert VNC to use QIOChannelSOcket should solve
> this particular failure scenario you're discussing.

Great!  No need to add a FIXME comment then.

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

end of thread, other threads:[~2015-10-23 13:13 UTC | newest]

Thread overview: 57+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 01/17] qapi: Add tests for reserved name abuse Eric Blake
2015-10-19 16:05   ` Markus Armbruster
2015-10-20 16:23     ` Eric Blake
2015-10-21 12:08       ` Markus Armbruster
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 02/17] qapi: Reserve '*List' type names for arrays Eric Blake
2015-10-19 16:14   ` Markus Armbruster
2015-10-20 18:12     ` Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 03/17] qapi: Reserve 'u' and 'has[-_]*' member names Eric Blake
2015-10-19 17:19   ` Markus Armbruster
2015-10-20 21:29     ` Eric Blake
2015-10-21 13:08       ` Markus Armbruster
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 04/17] vnc: hoist allocation of VncBasicInfo to callers Eric Blake
2015-10-20  7:38   ` Markus Armbruster
2015-10-20  8:54     ` Gerd Hoffmann
2015-10-20 14:46       ` Markus Armbruster
2015-10-20 22:53         ` Eric Blake
2015-10-21 11:02           ` Markus Armbruster
2015-10-21 11:16           ` Daniel P. Berrange
2015-10-23 13:13             ` Markus Armbruster
2015-10-20 22:56     ` Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 05/17] qapi: Unbox base members Eric Blake
2015-10-16 19:12   ` [Qemu-devel] [PATCH v9 05.5/17] fixup to " Eric Blake
2015-10-20 12:09   ` [Qemu-devel] [PATCH v9 05/17] " Markus Armbruster
2015-10-20 16:08     ` Eric Blake
2015-10-21 13:34       ` Markus Armbruster
2015-10-21 21:16         ` Eric Blake
2015-10-22  6:28           ` Markus Armbruster
2015-10-23  1:50         ` Eric Blake
2015-10-23  6:26           ` Markus Armbruster
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 06/17] qapi-visit: Remove redundant functions for flat union base Eric Blake
2015-10-21 17:36   ` Markus Armbruster
2015-10-21 19:01     ` Eric Blake
2015-10-22  8:32       ` Markus Armbruster
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 07/17] qapi: Start converting to new qapi union layout Eric Blake
2015-10-22 13:54   ` Markus Armbruster
2015-10-22 14:09     ` Eric Blake
2015-10-22 14:44       ` Markus Armbruster
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 08/17] tests: Convert " Eric Blake
2015-10-22 14:01   ` Markus Armbruster
2015-10-22 14:22     ` Eric Blake
2015-10-22 14:57       ` Markus Armbruster
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 09/17] block: " Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 10/17] nbd: " Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 11/17] net: " Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 12/17] char: " Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 13/17] input: " Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 14/17] memory: " Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 15/17] tpm: " Eric Blake
2015-10-22 14:19   ` Markus Armbruster
2015-10-22 14:26     ` Eric Blake
2015-10-22 16:40       ` Eric Blake
2015-10-23  6:24         ` Markus Armbruster
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 16/17] qapi: Finish converting " Eric Blake
2015-10-22 14:50   ` Markus Armbruster
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 17/17] qapi: Simplify gen_struct_field() Eric Blake
2015-10-22 15:13 ` [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') 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.