All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D)
@ 2015-11-11  6:51 Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 01/28] qapi: Track simple union tag in object.local_members Eric Blake
                   ` (27 more replies)
  0 siblings, 28 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru

No pending prerequisites; based on qemu.git master

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

and will soon be part of my branch with the rest of the v5 series, at:
http://repo.or.cz/qemu/ericb.git/shortlog/refs/heads/qapi

v11 notes:
First half of v10 subset C has been applied, so consolidate the last
half of that along with all of subset D (which was at v9) into one
group.  Address list reviews, in particular, add a new patch 21 that
makes alternate layouts a lot nicer by making qtype_code a builtin
qapi type; and new patches 18-19 that try to reduce confusion on
the use of camel_to_upper() in c_enum_const().

Probably too late to get these into 2.5, in which case 17/28 will need
tweaks to call out 2.6.

backport diff (1-20 against v10c, 21-28 against v9d)

001/28:[0001] [FC] 'qapi: Track simple union tag in object.local_members'
002/28:[----] [--] 'qapi-types: Consolidate gen_struct() and gen_union()'
003/28:[----] [--] 'qapi-types: Simplify gen_struct_field[s]'
004/28:[----] [--] 'qapi: Drop obsolete tag value collision assertions'
005/28:[----] [--] 'qapi: Simplify QAPISchemaObjectTypeMember.check()'
006/28:[0001] [FC] 'qapi: Clean up after previous commit'
007/28:[----] [--] 'qapi: Fix up commit 7618b91's clash sanity checking change'
008/28:[----] [--] 'qapi: Eliminate QAPISchemaObjectType.check() variable members'
009/28:[----] [--] 'qapi: Factor out QAPISchemaObjectTypeMember.check_clash()'
010/28:[0002] [FC] 'qapi: Simplify QAPISchemaObjectTypeVariants.check()'
011/28:[0007] [FC] 'qapi: Check for qapi collisions of flat union branches'
012/28:[0005] [FC] 'qapi: Factor out QAPISchemaObjectType.check_clash()'
013/28:[0005] [FC] 'qapi: Hoist tag collision check to Variants.check()'
014/28:[----] [--] 'qapi: Remove outdated tests related to QMP/branch collisions'
015/28:[0025] [FC] 'qapi: Track owner of each object member'
016/28:[0013] [FC] 'qapi: Detect collisions in C member names'
017/28:[0003] [FC] 'cpu: Convert CpuInfo into flat union'
018/28:[down] 'qerror: more error_setg() usage'
019/28:[down] 'qapi: Change munging of CamelCase enum values'
020/28:[0017] [FC] 'qapi: Forbid case-insensitive clashes'
021/28:[down] 'qapi: Convert qtype_code into qapi enum type'
022/28:[0090] [FC] 'qapi: Simplify visiting of alternate types'
023/28:[0022] [FC] 'qapi: Fix alternates that accept 'number' but not 'int''
024/28:[0015] [FC] 'qapi: Add positive tests to qapi-schema-test'
025/28:[----] [-C] 'qapi: Simplify visits of optional fields'
026/28:[----] [--] 'qapi: Move duplicate member checks to schema check()'
027/28:[0002] [FC] 'qapi: Move duplicate enum value checks to schema check()'
028/28:[0009] [FC] 'qapi: Detect base class loops'


v10 notes:
https://lists.gnu.org/archive/html/qemu-devel/2015-11/msg01249.html
Split several patches, redo the middle patches from Markus to be
back in the order they were first posted, some fallout change to
my patches due to the nicer pattern of minimizing conditionals
inside .check(), by instead calling .check_clash() as needed.
Change data->err magic in tests to instead use a new helper
error_free_or_abort(). Add a patch that would prevent qapi
case-insensitive clashes.

I am redoing my subset boundaries slightly: patches 23-27 of
v9 (updating the alternate layout) will be delayed to subset D,
and 2 other patches previously posted in subset D are now here
(turning qapi clash checking into actual error messages), so
the subject line of this cover letter is slightly different.

Hopefully, we are converging on something that will be ready
for a pull request, especially for the earlier patches of this
subset.

v9 notes:
https://lists.gnu.org/archive/html/qemu-devel/2015-11/msg00652.html
https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg06999.html
More patches added, and several reorganized.  Lots of new patches
from Markus, although not in the order originally proposed.

The first 8 patches are fairly straightforward, and could probably
be taken as-is. Patch 9 is a rewrite of v8 4/17, but in the opposite
direction (document that no sorting is done, rather than attempting
to sort), so it may need further fine-tuning.  Patches 12-21
represents a fusion of Markus' and my attempts to rewrite v5 7/17
into a more-reviewable set of patches, and caused further churn
later in the series.

Patch 23 still uses tag_member.type == None; I ran out of time to
work on Markus' idea of providing an instance of QAPISchemaBuiltinType
to fill the role for 'qtype_code' without being exposed through .json
files and without breaking the invariant of a valid member.type after
check(), and wanted to get the rest of the series started under revew.
So I may need a followup patch or even a v10 of the later half of
this series after exploring that idea more.

v8 notes:
https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg06674.html
Minor changes when rebasing to latest, improved commit messages in
a few places, plus the addition of a few new patches. Markus started
reviewing v7, but hadn't gotten very far.

v7 notes:
https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg04112.html
Patches 1-3 of the previous round have moved to subset B; patch 7
is gone, and a couple of new patches are present in this round. The
latest version of subset B made it much easier to reason about
collision detection (namely, tag values can't collide with QMP values
thanks to a named rather than anonymous union in the C type), and I
like how things turned out.  I suspect the hardest part of the review
will be patches 5/14 and 7/14, although none of this has really
had much review in any earlier versions.

v6 notes:
https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg01980.html
Add some patches and rebase onto work on subset B. Rearrange some
patches from v5 (this set includes 17-20, 23, 25-27). Backport diff
gets a bit confused by one patch title changing.

Not much direct review comments, although some of the changes here
are updated based on comments made on other patches in the v5 series.

Subset D (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 (22):
  qapi: Track simple union tag in object.local_members
  qapi-types: Consolidate gen_struct() and gen_union()
  qapi-types: Simplify gen_struct_field[s]
  qapi: Clean up after previous commit
  qapi: Check for qapi collisions of flat union branches
  qapi: Factor out QAPISchemaObjectType.check_clash()
  qapi: Hoist tag collision check to Variants.check()
  qapi: Remove outdated tests related to QMP/branch collisions
  qapi: Track owner of each object member
  qapi: Detect collisions in C member names
  cpu: Convert CpuInfo into flat union
  qerror: more error_setg() usage
  qapi: Change munging of CamelCase enum values
  qapi: Forbid case-insensitive clashes
  qapi: Convert qtype_code into qapi enum type
  qapi: Simplify visiting of alternate types
  qapi: Fix alternates that accept 'number' but not 'int'
  qapi: Add positive tests to qapi-schema-test
  qapi: Simplify visits of optional fields
  qapi: Move duplicate member checks to schema check()
  qapi: Move duplicate enum value checks to schema check()
  qapi: Detect base class loops

Markus Armbruster (6):
  qapi: Drop obsolete tag value collision assertions
  qapi: Simplify QAPISchemaObjectTypeMember.check()
  qapi: Fix up commit 7618b91's clash sanity checking change
  qapi: Eliminate QAPISchemaObjectType.check() variable members
  qapi: Factor out QAPISchemaObjectTypeMember.check_clash()
  qapi: Simplify QAPISchemaObjectTypeVariants.check()

 backends/rng-egd.c                                 |   2 +-
 balloon.c                                          |   4 +-
 block.c                                            |   5 +-
 block/qapi.c                                       |   4 +-
 blockdev-nbd.c                                     |   2 +-
 blockdev.c                                         |  24 +--
 cpus.c                                             |  31 +--
 docs/qapi-code-gen.txt                             |   7 +-
 docs/writing-qmp-commands.txt                      |  20 +-
 hmp.c                                              |  36 ++--
 hw/i386/pc.c                                       |   2 +-
 hw/input/hid.c                                     |   4 +-
 hw/input/ps2.c                                     |   4 +-
 hw/input/virtio-input-hid.c                        |   4 +-
 hw/net/rocker/rocker.c                             |   6 +-
 hw/net/rocker/rocker_of_dpa.c                      |  12 +-
 include/hw/qdev-core.h                             |   2 +-
 include/qapi/error.h                               |   6 +-
 include/qapi/qmp/qobject.h                         |  19 +-
 include/qapi/visitor-impl.h                        |   8 +-
 include/qapi/visitor.h                             |  19 +-
 include/ui/qemu-spice.h                            |   2 +-
 monitor.c                                          |   8 +-
 net/net.c                                          |   4 +-
 qapi-schema.json                                   | 120 +++++++++--
 qapi/opts-visitor.c                                |   2 +-
 qapi/qapi-visit-core.c                             |  10 +-
 qapi/qmp-dispatch.c                                |   2 +-
 qapi/qmp-input-visitor.c                           |  11 +-
 qapi/string-input-visitor.c                        |   3 +-
 qdev-monitor.c                                     |   4 +-
 qmp-commands.hx                                    |   4 +
 qmp.c                                              |   8 +-
 qobject/qdict.c                                    |   2 +-
 qom/object.c                                       |   8 +-
 scripts/qapi-types.py                              | 115 +++-------
 scripts/qapi-visit.py                              |  34 ++-
 scripts/qapi.py                                    | 234 ++++++++++++---------
 tests/Makefile                                     |   7 +-
 tests/qapi-schema/alternate-clash.err              |   2 +-
 tests/qapi-schema/alternate-empty.out              |   3 +-
 tests/qapi-schema/args-case-clash.err              |   1 +
 ...{union-clash-type.exit => args-case-clash.exit} |   0
 tests/qapi-schema/args-case-clash.json             |   4 +
 .../{union-clash-type.out => args-case-clash.out}  |   0
 tests/qapi-schema/args-name-clash.err              |   1 +
 tests/qapi-schema/args-name-clash.exit             |   2 +-
 tests/qapi-schema/args-name-clash.json             |   5 +-
 tests/qapi-schema/args-name-clash.out              |   6 -
 tests/qapi-schema/base-cycle-direct.err            |   1 +
 ...nion-bad-branch.exit => base-cycle-direct.exit} |   0
 tests/qapi-schema/base-cycle-direct.json           |   2 +
 ...{union-bad-branch.out => base-cycle-direct.out} |   0
 tests/qapi-schema/base-cycle-indirect.err          |   1 +
 ...on-clash-type.exit => base-cycle-indirect.exit} |   0
 tests/qapi-schema/base-cycle-indirect.json         |   3 +
 ...nion-clash-type.out => base-cycle-indirect.out} |   0
 tests/qapi-schema/comments.out                     |   2 +
 tests/qapi-schema/empty.out                        |   2 +
 tests/qapi-schema/enum-clash-member.err            |   2 +-
 tests/qapi-schema/enum-max-member.err              |   2 +-
 tests/qapi-schema/event-case.out                   |   2 +
 tests/qapi-schema/flat-union-clash-branch.err      |   0
 tests/qapi-schema/flat-union-clash-branch.exit     |   1 -
 tests/qapi-schema/flat-union-clash-branch.json     |  18 --
 tests/qapi-schema/flat-union-clash-branch.out      |  14 --
 tests/qapi-schema/flat-union-clash-member.err      |   2 +-
 tests/qapi-schema/flat-union-clash-type.err        |   1 -
 tests/qapi-schema/flat-union-clash-type.json       |  14 --
 tests/qapi-schema/flat-union-empty.out             |   2 +
 tests/qapi-schema/ident-with-escape.out            |   2 +
 tests/qapi-schema/include-relpath.out              |   2 +
 tests/qapi-schema/include-repetition.out           |   2 +
 tests/qapi-schema/include-simple.out               |   2 +
 tests/qapi-schema/indented-expr.out                |   2 +
 tests/qapi-schema/qapi-schema-test.json            |  16 ++
 tests/qapi-schema/qapi-schema-test.out             |  38 +++-
 tests/qapi-schema/struct-base-clash-deep.err       |   2 +-
 tests/qapi-schema/struct-base-clash.err            |   2 +-
 tests/qapi-schema/union-bad-branch.err             |   1 -
 tests/qapi-schema/union-bad-branch.json            |   8 -
 tests/qapi-schema/union-clash-branches.err         |   2 +-
 tests/qapi-schema/union-clash-branches.json        |   2 +-
 tests/qapi-schema/union-clash-data.out             |   3 +
 tests/qapi-schema/union-clash-type.err             |   1 -
 tests/qapi-schema/union-clash-type.json            |   9 -
 tests/qapi-schema/union-empty.out                  |   3 +
 tests/qapi-schema/union-max.err                    |   2 +-
 tests/test-qmp-input-visitor.c                     |  29 ++-
 tests/test-qmp-output-visitor.c                    |   4 +-
 ui/cocoa.m                                         |   4 +-
 ui/gtk.c                                           |   4 +-
 ui/input-legacy.c                                  |   4 +-
 ui/input.c                                         |   2 +-
 ui/sdl.c                                           |   4 +-
 ui/sdl2.c                                          |   4 +-
 ui/spice-input.c                                   |   4 +-
 ui/vnc.c                                           |   4 +-
 util/error.c                                       |   6 +-
 99 files changed, 577 insertions(+), 483 deletions(-)
 create mode 100644 tests/qapi-schema/args-case-clash.err
 rename tests/qapi-schema/{union-clash-type.exit => args-case-clash.exit} (100%)
 create mode 100644 tests/qapi-schema/args-case-clash.json
 rename tests/qapi-schema/{union-clash-type.out => args-case-clash.out} (100%)
 create mode 100644 tests/qapi-schema/base-cycle-direct.err
 rename tests/qapi-schema/{union-bad-branch.exit => base-cycle-direct.exit} (100%)
 create mode 100644 tests/qapi-schema/base-cycle-direct.json
 rename tests/qapi-schema/{union-bad-branch.out => base-cycle-direct.out} (100%)
 create mode 100644 tests/qapi-schema/base-cycle-indirect.err
 rename tests/qapi-schema/{flat-union-clash-type.exit => base-cycle-indirect.exit} (100%)
 create mode 100644 tests/qapi-schema/base-cycle-indirect.json
 rename tests/qapi-schema/{flat-union-clash-type.out => base-cycle-indirect.out} (100%)
 delete mode 100644 tests/qapi-schema/flat-union-clash-branch.err
 delete mode 100644 tests/qapi-schema/flat-union-clash-branch.exit
 delete mode 100644 tests/qapi-schema/flat-union-clash-branch.json
 delete mode 100644 tests/qapi-schema/flat-union-clash-branch.out
 delete mode 100644 tests/qapi-schema/flat-union-clash-type.err
 delete mode 100644 tests/qapi-schema/flat-union-clash-type.json
 delete mode 100644 tests/qapi-schema/union-bad-branch.err
 delete mode 100644 tests/qapi-schema/union-bad-branch.json
 delete mode 100644 tests/qapi-schema/union-clash-type.err
 delete mode 100644 tests/qapi-schema/union-clash-type.json

-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 01/28] qapi: Track simple union tag in object.local_members
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 02/28] qapi-types: Consolidate gen_struct() and gen_union() Eric Blake
                   ` (26 subsequent siblings)
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

We were previously creating all unions with an empty list for
local_members.  However, it will make it easier to unify struct
and union generation if we include the generated tag member in
local_members.  That way, we can have a common code pattern:
visit the base (if any), visit the local members (if any), visit
the variants (if any).  The local_members of a flat union
remains empty (because the discriminator is already visited as
part of the base).  Then, by visiting tag_member.check() during
AlternateType.check(), we no longer need to call it during
Variants.check().

The various front end entities now exist as follows:
struct: optional base, optional local_members, no variants
simple union: no base, one-element local_members, variants with tag_member
  from local_members
flat union: base, no local_members, variants with tag_member from base
alternate: no base, no local_members, variants

With the new local members, we require a bit of finesse to
avoid assertions in the clients.

No change to generated code.

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

---
v11: drop comment churn
v10: no change
v9: improve commit message, add comments, tweak how alternates
check tag_member
v8: new patch
---
 scripts/qapi-types.py                  |  5 ++++-
 scripts/qapi-visit.py                  |  5 ++++-
 scripts/qapi.py                        | 15 ++++++++++-----
 tests/qapi-schema/qapi-schema-test.out |  2 ++
 tests/qapi-schema/union-clash-data.out |  1 +
 tests/qapi-schema/union-empty.out      |  1 +
 6 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index b37900f..946afab 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -269,7 +269,10 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
     def visit_object_type(self, name, info, base, members, variants):
         self._fwdecl += gen_fwd_object_or_array(name)
         if variants:
-            assert not members      # not implemented
+            if members:
+                # Members other than variants.tag_member not implemented
+                assert len(members) == 1
+                assert members[0] == variants.tag_member
             self.decl += gen_union(name, base, variants)
         else:
             self.decl += gen_struct(name, base, members)
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 3ef5c16..94cd113 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -364,7 +364,10 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
     def visit_object_type(self, name, info, base, members, variants):
         self.decl += gen_visit_decl(name)
         if variants:
-            assert not members      # not implemented
+            if members:
+                # Members other than variants.tag_member not implemented
+                assert len(members) == 1
+                assert members[0] == variants.tag_member
             self.defn += gen_visit_union(name, base, variants)
         else:
             self.defn += gen_visit_struct(name, base, members)
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 7c50cc4..687d9dc 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -957,6 +957,9 @@ class QAPISchemaArrayType(QAPISchemaType):

 class QAPISchemaObjectType(QAPISchemaType):
     def __init__(self, name, info, base, local_members, variants):
+        # struct has local_members, optional base, and no variants
+        # flat union has base, variants, and no local_members
+        # simple union has local_members, variants, and no base
         QAPISchemaType.__init__(self, name, info)
         assert base is None or isinstance(base, str)
         for m in local_members:
@@ -1048,10 +1051,10 @@ class QAPISchemaObjectTypeVariants(object):
         self.variants = variants

     def check(self, schema, members, seen):
-        if self.tag_name:
+        if self.tag_name:    # flat union
             self.tag_member = seen[self.tag_name]
-        else:
-            self.tag_member.check(schema, members, seen)
+        if seen:
+            assert self.tag_member in seen.itervalues()
         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
         for v in self.variants:
             vseen = dict(seen)
@@ -1085,6 +1088,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
         self.variants = variants

     def check(self, schema):
+        self.variants.tag_member.check(schema, [], {})
         self.variants.check(schema, [], {})

     def json_type(self):
@@ -1270,13 +1274,14 @@ class QAPISchema(object):
         if tag_name:
             variants = [self._make_variant(key, value)
                         for (key, value) in data.iteritems()]
+            members = []
         else:
             variants = [self._make_simple_variant(key, value, info)
                         for (key, value) in data.iteritems()]
             tag_member = self._make_implicit_tag(name, info, variants)
+            members = [tag_member]
         self._def_entity(
-            QAPISchemaObjectType(name, info, base,
-                                 self._make_members(OrderedDict(), info),
+            QAPISchemaObjectType(name, info, base, members,
                                  QAPISchemaObjectTypeVariants(tag_name,
                                                               tag_member,
                                                               variants)))
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index e20a823..786024e 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -132,6 +132,7 @@ object UserDefFlatUnion2
     case value2: UserDefB
     case value3: UserDefA
 object UserDefNativeListUnion
+    member type: UserDefNativeListUnionKind optional=False
     case integer: :obj-intList-wrapper
     case s8: :obj-int8List-wrapper
     case s16: :obj-int16List-wrapper
@@ -187,6 +188,7 @@ object __org.qemu_x-Struct
 object __org.qemu_x-Struct2
     member array: __org.qemu_x-Union1List optional=False
 object __org.qemu_x-Union1
+    member type: __org.qemu_x-Union1Kind optional=False
     case __org.qemu_x-branch: :obj-str-wrapper
 enum __org.qemu_x-Union1Kind ['__org.qemu_x-branch']
 object __org.qemu_x-Union2
diff --git a/tests/qapi-schema/union-clash-data.out b/tests/qapi-schema/union-clash-data.out
index 6277239..cea8551 100644
--- a/tests/qapi-schema/union-clash-data.out
+++ b/tests/qapi-schema/union-clash-data.out
@@ -2,5 +2,6 @@ object :empty
 object :obj-int-wrapper
     member data: int optional=False
 object TestUnion
+    member type: TestUnionKind optional=False
     case data: :obj-int-wrapper
 enum TestUnionKind ['data']
diff --git a/tests/qapi-schema/union-empty.out b/tests/qapi-schema/union-empty.out
index 8b5a7bf..9c89fd1 100644
--- a/tests/qapi-schema/union-empty.out
+++ b/tests/qapi-schema/union-empty.out
@@ -1,3 +1,4 @@
 object :empty
 object Union
+    member type: UnionKind optional=False
 enum UnionKind []
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 02/28] qapi-types: Consolidate gen_struct() and gen_union()
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 01/28] qapi: Track simple union tag in object.local_members Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 03/28] qapi-types: Simplify gen_struct_field[s] Eric Blake
                   ` (25 subsequent siblings)
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

These two methods are now close enough that we can finally merge
them, relying on the fact that simple unions now provide a
reasonable local_members.  Change gen_struct() to gen_object()
that handles all forms of QAPISchemaObjectType, and rename and
shrink gen_union() to gen_variants() to handle the portion of
gen_object() needed when variants are present.

gen_struct_fields() now has a single caller, so it no longer
needs an optional parameter; however, I did not choose to inline
it into the caller.

No difference to generated code.

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

---
v11: no change
v10: no change
v9: rebase to earlier changes
v8: new patch
---
 scripts/qapi-types.py | 37 +++++++++++--------------------------
 1 file changed, 11 insertions(+), 26 deletions(-)

diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 946afab..403768b 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -51,7 +51,7 @@ def gen_struct_field(member):
     return ret


-def gen_struct_fields(local_members, base=None):
+def gen_struct_fields(local_members, base):
     ret = ''

     if base:
@@ -70,7 +70,7 @@ def gen_struct_fields(local_members, base=None):
     return ret


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

 struct %(c_name)s {
@@ -79,11 +79,14 @@ struct %(c_name)s {

     ret += gen_struct_fields(members, base)

+    if variants:
+        ret += gen_variants(variants)
+
     # 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 base.members) and not members:
+    if not (base and base.members) and not members and not variants:
         ret += mcgen('''
     char qapi_dummy_field_for_empty_struct;
 ''')
@@ -140,17 +143,7 @@ const int %(c_name)s_qtypes[QTYPE_MAX] = {
     return ret


-def gen_union(name, base, variants):
-    ret = mcgen('''
-
-struct %(c_name)s {
-''',
-                c_name=c_name(name))
-    if base:
-        ret += gen_struct_fields([], base)
-    else:
-        ret += gen_struct_field(variants.tag_member)
-
+def gen_variants(variants):
     # FIXME: What purpose does data serve, besides preventing a union that
     # has a branch named 'data'? We use it in qapi-visit.py to decide
     # whether to bypass the switch statement if visiting the discriminator
@@ -159,11 +152,11 @@ 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('''
+    ret = mcgen('''
     union { /* union tag is @%(c_name)s */
         void *data;
 ''',
-                 c_name=c_name(variants.tag_member.name))
+                c_name=c_name(variants.tag_member.name))

     for var in variants.variants:
         # Ugly special case for simple union TODO get rid of it
@@ -176,7 +169,6 @@ struct %(c_name)s {

     ret += mcgen('''
     } u;
-};
 ''')

     return ret
@@ -268,14 +260,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):

     def visit_object_type(self, name, info, base, members, variants):
         self._fwdecl += gen_fwd_object_or_array(name)
-        if variants:
-            if members:
-                # Members other than variants.tag_member not implemented
-                assert len(members) == 1
-                assert members[0] == variants.tag_member
-            self.decl += gen_union(name, base, variants)
-        else:
-            self.decl += gen_struct(name, base, members)
+        self.decl += gen_object(name, base, members, variants)
         if base:
             self.decl += gen_upcast(name, base)
         self._gen_type_cleanup(name)
@@ -283,7 +268,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
     def visit_alternate_type(self, name, info, variants):
         self._fwdecl += gen_fwd_object_or_array(name)
         self._fwdefn += gen_alternate_qtypes(name, variants)
-        self.decl += gen_union(name, None, variants)
+        self.decl += gen_object(name, None, [variants.tag_member], variants)
         self.decl += gen_alternate_qtypes_decl(name)
         self._gen_type_cleanup(name)

-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 03/28] qapi-types: Simplify gen_struct_field[s]
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 01/28] qapi: Track simple union tag in object.local_members Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 02/28] qapi-types: Consolidate gen_struct() and gen_union() Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 04/28] qapi: Drop obsolete tag value collision assertions Eric Blake
                   ` (24 subsequent siblings)
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Simplify gen_struct_fields() back to a single iteration over a
list of fields (like it was prior to commit f87ab7f9), by moving
the generated comments to gen_object().  Then, inline
gen_struct_field() into its only caller.

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

---
v11: no change
v10: no change
v9: new patch
---
 scripts/qapi-types.py | 40 +++++++++++++++-------------------------
 1 file changed, 15 insertions(+), 25 deletions(-)

diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 403768b..2f2f7df 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -36,48 +36,38 @@ struct %(c_name)s {
                  c_name=c_name(name), c_type=element_type.c_type())


-def gen_struct_field(member):
+def gen_struct_fields(members):
     ret = ''
-
-    if member.optional:
-        ret += mcgen('''
+    for memb in members:
+        if memb.optional:
+            ret += mcgen('''
     bool has_%(c_name)s;
 ''',
-                     c_name=c_name(member.name))
-    ret += mcgen('''
+                         c_name=c_name(memb.name))
+        ret += mcgen('''
     %(c_type)s %(c_name)s;
 ''',
-                 c_type=member.type.c_type(), c_name=c_name(member.name))
+                     c_type=memb.type.c_type(), c_name=c_name(memb.name))
     return ret


-def gen_struct_fields(local_members, base):
-    ret = ''
+def gen_object(name, base, members, variants):
+    ret = mcgen('''
+
+struct %(c_name)s {
+''',
+                c_name=c_name(name))

     if base:
         ret += mcgen('''
     /* Members inherited from %(c_name)s: */
 ''',
                      c_name=base.c_name())
-        for memb in base.members:
-            ret += gen_struct_field(memb)
+        ret += gen_struct_fields(base.members)
         ret += mcgen('''
     /* Own members: */
 ''')
-
-    for memb in local_members:
-        ret += gen_struct_field(memb)
-    return ret
-
-
-def gen_object(name, base, members, variants):
-    ret = mcgen('''
-
-struct %(c_name)s {
-''',
-                c_name=c_name(name))
-
-    ret += gen_struct_fields(members, base)
+    ret += gen_struct_fields(members)

     if variants:
         ret += gen_variants(variants)
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 04/28] qapi: Drop obsolete tag value collision assertions
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (2 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 03/28] qapi-types: Simplify gen_struct_field[s] Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 05/28] qapi: Simplify QAPISchemaObjectTypeMember.check() Eric Blake
                   ` (23 subsequent siblings)
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

From: Markus Armbruster <armbru@redhat.com>

Union tag values can't clash with member names in generated C anymore
since commit e4ba22b, but QAPISchemaObjectTypeVariants.check() still
asserts they don't.  Drop it.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1446559499-26984-1-git-send-email-armbru@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>

---
v11: no change
v10: redo closer to Markus' original proposal
v9: new patch
---
 scripts/qapi.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 687d9dc..29377d6 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1057,8 +1057,7 @@ class QAPISchemaObjectTypeVariants(object):
             assert self.tag_member in seen.itervalues()
         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
         for v in self.variants:
-            vseen = dict(seen)
-            v.check(schema, self.tag_member.type, vseen)
+            v.check(schema, self.tag_member.type, {})


 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 05/28] qapi: Simplify QAPISchemaObjectTypeMember.check()
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (3 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 04/28] qapi: Drop obsolete tag value collision assertions Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 06/28] qapi: Clean up after previous commit Eric Blake
                   ` (22 subsequent siblings)
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

From: Markus Armbruster <armbru@redhat.com>

QAPISchemaObjectTypeMember.check() currently does four things:

1. Compute self.type

2. Accumulate members in all_members

   Only one caller cares: QAPISchemaObjectType.check() uses it to
   compute self.members.  The other callers pass a throw-away
   accumulator.

3. Accumulate a map from names to members in seen

   Only one caller cares: QAPISchemaObjectType.check() uses it to
   compute its local variable seen, for self.variants.check(), which
   uses it to compute self.variants.tag_member from
   self.variants.tag_name.  The other callers pass a throw-away
   accumulator.

4. Check for collisions

   This piggybacks on 3: before adding a new entry, we assert it's new.

   Only one caller cares: QAPISchemaObjectType.check() uses it to
   assert non-variant members don't clash.

Simplify QAPISchemaObjectType.check(): move 2.-4. to
QAPISchemaObjectType.check(), and drop parameters all_members and
seen.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1446559499-26984-2-git-send-email-armbru@redhat.com>
[rebase to earlier changes that moved tag_member.check() of
alternate types, commit message typo fix]
Signed-off-by: Eric Blake <eblake@redhat.com>

---
v11: typo fix
v10: redo closer to Markus' original proposal
v9: new patch
---
 scripts/qapi.py | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 29377d6..63d39e4 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -990,7 +990,10 @@ class QAPISchemaObjectType(QAPISchemaType):
             assert c_name(m.name) not in seen
             seen[m.name] = m
         for m in self.local_members:
-            m.check(schema, members, seen)
+            m.check(schema)
+            assert m.name not in seen
+            seen[m.name] = m
+            members.append(m)
         if self.variants:
             self.variants.check(schema, members, seen)
         self.members = members
@@ -1027,12 +1030,9 @@ class QAPISchemaObjectTypeMember(object):
         self.type = None
         self.optional = optional

-    def check(self, schema, all_members, seen):
-        assert self.name not in seen
+    def check(self, schema):
         self.type = schema.lookup_type(self._type_name)
         assert self.type
-        all_members.append(self)
-        seen[self.name] = self


 class QAPISchemaObjectTypeVariants(object):
@@ -1065,7 +1065,7 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
         QAPISchemaObjectTypeMember.__init__(self, name, typ, False)

     def check(self, schema, tag_type, seen):
-        QAPISchemaObjectTypeMember.check(self, schema, [], seen)
+        QAPISchemaObjectTypeMember.check(self, schema)
         assert self.name in tag_type.values

     # This function exists to support ugly simple union special cases
@@ -1087,7 +1087,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
         self.variants = variants

     def check(self, schema):
-        self.variants.tag_member.check(schema, [], {})
+        self.variants.tag_member.check(schema)
         self.variants.check(schema, [], {})

     def json_type(self):
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 06/28] qapi: Clean up after previous commit
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (4 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 05/28] qapi: Simplify QAPISchemaObjectTypeMember.check() Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 07/28] qapi: Fix up commit 7618b91's clash sanity checking change Eric Blake
                   ` (21 subsequent siblings)
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

QAPISchemaObjectTypeVariants.check() parameter members and
QAPISchemaObjectTypeVariant.check() parameter seen are no longer used,
drop them.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1446559499-26984-3-git-send-email-armbru@redhat.com>
[rebase to earlier changes that moved tag_member.check() of
alternate types]
Signed-off-by: Eric Blake <eblake@redhat.com>

---
v11: rebase for less comment churn
v10: redo closer to Markus' original proposal
v9: new patch
---
 scripts/qapi.py | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 63d39e4..0bf8235 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -995,7 +995,7 @@ class QAPISchemaObjectType(QAPISchemaType):
             seen[m.name] = m
             members.append(m)
         if self.variants:
-            self.variants.check(schema, members, seen)
+            self.variants.check(schema, seen)
         self.members = members

     def is_implicit(self):
@@ -1050,21 +1050,21 @@ class QAPISchemaObjectTypeVariants(object):
         self.tag_member = tag_member
         self.variants = variants

-    def check(self, schema, members, seen):
+    def check(self, schema, seen):
         if self.tag_name:    # flat union
             self.tag_member = seen[self.tag_name]
         if seen:
             assert self.tag_member in seen.itervalues()
         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
         for v in self.variants:
-            v.check(schema, self.tag_member.type, {})
+            v.check(schema, self.tag_member.type)


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

-    def check(self, schema, tag_type, seen):
+    def check(self, schema, tag_type):
         QAPISchemaObjectTypeMember.check(self, schema)
         assert self.name in tag_type.values

@@ -1088,7 +1088,7 @@ class QAPISchemaAlternateType(QAPISchemaType):

     def check(self, schema):
         self.variants.tag_member.check(schema)
-        self.variants.check(schema, [], {})
+        self.variants.check(schema, {})

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

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

* [Qemu-devel] [PATCH v11 07/28] qapi: Fix up commit 7618b91's clash sanity checking change
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (5 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 06/28] qapi: Clean up after previous commit Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 08/28] qapi: Eliminate QAPISchemaObjectType.check() variable members Eric Blake
                   ` (20 subsequent siblings)
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

From: Markus Armbruster <armbru@redhat.com>

This hunk

    @@ -964,6 +965,7 @@ class QAPISchemaObjectType(QAPISchemaType):
                 members = []
             seen = {}
             for m in members:
    +            assert c_name(m.name) not in seen
                 seen[m.name] = m
             for m in self.local_members:
                 m.check(schema, members, seen)

is plainly broken.

Asserting the members inherited from base don't clash is somewhat
redundant, because self.base.check() just checked that.  But it
doesn't hurt.

The idea to use c_name(m.name) instead of m.name for collision
checking is sound, because we need to catch clashes between the m.name
and between the c_name(m.name), and when two m.name clash, then their
c_name() also clash.

However, using c_name(m.name) instead of m.name in one of several
places doesn't work.  See the very next line.

Keep the assertion, but drop the c_name() for now.  A future commit
will bring it back.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1446559499-26984-4-git-send-email-armbru@redhat.com>
[change TABs in commit message to space]
Signed-off-by: Eric Blake <eblake@redhat.com>

---
v11: no change
v10: redo closer to Markus' original proposal
v9: new patch
---
 scripts/qapi.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 0bf8235..86d2adc 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -987,7 +987,7 @@ class QAPISchemaObjectType(QAPISchemaType):
             members = []
         seen = {}
         for m in members:
-            assert c_name(m.name) not in seen
+            assert m.name not in seen
             seen[m.name] = m
         for m in self.local_members:
             m.check(schema)
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 08/28] qapi: Eliminate QAPISchemaObjectType.check() variable members
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (6 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 07/28] qapi: Fix up commit 7618b91's clash sanity checking change Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 09/28] qapi: Factor out QAPISchemaObjectTypeMember.check_clash() Eric Blake
                   ` (19 subsequent siblings)
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

From: Markus Armbruster <armbru@redhat.com>

We can use seen.values() instead if we make it an OrderedDict.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1446559499-26984-5-git-send-email-armbru@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>

---
v11: no change
v10: redo closer to Markus' original proposal
v9: new patch
---
 scripts/qapi.py | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 86d2adc..44d08c1 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -977,26 +977,22 @@ class QAPISchemaObjectType(QAPISchemaType):
         if self.members:
             return
         self.members = False                    # mark as being checked
+        seen = OrderedDict()
         if self._base_name:
             self.base = schema.lookup_type(self._base_name)
             assert isinstance(self.base, QAPISchemaObjectType)
             assert not self.base.variants       # not implemented
             self.base.check(schema)
-            members = list(self.base.members)
-        else:
-            members = []
-        seen = {}
-        for m in members:
-            assert m.name not in seen
-            seen[m.name] = m
+            for m in self.base.members:
+                assert m.name not in seen
+                seen[m.name] = m
         for m in self.local_members:
             m.check(schema)
             assert m.name not in seen
             seen[m.name] = m
-            members.append(m)
         if self.variants:
             self.variants.check(schema, seen)
-        self.members = members
+        self.members = seen.values()

     def is_implicit(self):
         # See QAPISchema._make_implicit_object_type()
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 09/28] qapi: Factor out QAPISchemaObjectTypeMember.check_clash()
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (7 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 08/28] qapi: Eliminate QAPISchemaObjectType.check() variable members Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 10/28] qapi: Simplify QAPISchemaObjectTypeVariants.check() Eric Blake
                   ` (18 subsequent siblings)
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

From: Markus Armbruster <armbru@redhat.com>

While there, stick in a TODO change key of seen from QAPI name to C
name.  Can't do it right away, because it would fail the assertion for
tests/qapi-schema/args-has-clash.json.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1446559499-26984-6-git-send-email-armbru@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>

---
v11: no change
v10: redo closer to Markus' original proposal
v9: new patch
---
 scripts/qapi.py | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 44d08c1..2a73b2b 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -984,12 +984,10 @@ class QAPISchemaObjectType(QAPISchemaType):
             assert not self.base.variants       # not implemented
             self.base.check(schema)
             for m in self.base.members:
-                assert m.name not in seen
-                seen[m.name] = m
+                m.check_clash(seen)
         for m in self.local_members:
             m.check(schema)
-            assert m.name not in seen
-            seen[m.name] = m
+            m.check_clash(seen)
         if self.variants:
             self.variants.check(schema, seen)
         self.members = seen.values()
@@ -1030,6 +1028,11 @@ class QAPISchemaObjectTypeMember(object):
         self.type = schema.lookup_type(self._type_name)
         assert self.type

+    def check_clash(self, seen):
+        # TODO change key of seen from QAPI name to C name
+        assert self.name not in seen
+        seen[self.name] = self
+

 class QAPISchemaObjectTypeVariants(object):
     def __init__(self, tag_name, tag_member, variants):
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 10/28] qapi: Simplify QAPISchemaObjectTypeVariants.check()
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (8 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 09/28] qapi: Factor out QAPISchemaObjectTypeMember.check_clash() Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 11/28] qapi: Check for qapi collisions of flat union branches Eric Blake
                   ` (17 subsequent siblings)
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

From: Markus Armbruster <armbru@redhat.com>

Reduce the ugly flat union / simple union conditional by doing just
the essential work here, namely setting self.tag_member.
Move the rest to callers.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1446559499-26984-7-git-send-email-armbru@redhat.com>
[rebase to earlier changes that moved tag_member.check() of
alternate types, and tweak commit title and wording]
Signed-off-by: Eric Blake <eblake@redhat.com>

---
v11: don't drop change of 'if not self.tag_member'
v10: redo closer to Markus' original proposal
v9: new patch
---
 scripts/qapi.py | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 2a73b2b..c6cb17b 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -988,9 +988,10 @@ class QAPISchemaObjectType(QAPISchemaType):
         for m in self.local_members:
             m.check(schema)
             m.check_clash(seen)
+        self.members = seen.values()
         if self.variants:
             self.variants.check(schema, seen)
-        self.members = seen.values()
+            assert self.variants.tag_member in self.members

     def is_implicit(self):
         # See QAPISchema._make_implicit_object_type()
@@ -1050,10 +1051,8 @@ class QAPISchemaObjectTypeVariants(object):
         self.variants = variants

     def check(self, schema, seen):
-        if self.tag_name:    # flat union
+        if not self.tag_member:    # flat union
             self.tag_member = seen[self.tag_name]
-        if seen:
-            assert self.tag_member in seen.itervalues()
         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
         for v in self.variants:
             v.check(schema, self.tag_member.type)
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 11/28] qapi: Check for qapi collisions of flat union branches
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (9 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 10/28] qapi: Simplify QAPISchemaObjectTypeVariants.check() Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11 13:42   ` Markus Armbruster
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 12/28] qapi: Factor out QAPISchemaObjectType.check_clash() Eric Blake
                   ` (16 subsequent siblings)
  27 siblings, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

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

We already ensure that all branches of a flat union are qapi
structs with no variants, at which point those members appear
in the same JSON object as all non-variant members.  And we
already have a map 'seen' of all non-variant members.  Still
missing, until now, was a call to variant.type.check() during
Variants.check() (to populate variant.type.members), and a
new Variants.check_clash() which clones the seen map for each
variant before checking that variant's members for clashes.

Note that cloning 'seen' inside Variants.check_clash()
resembles the one we just removed from Variants.check(); the
difference here is that we are now checking for clashes
among the qapi members of the variant type, rather than for
a single clash with the variant tag name itself.

In general, a non-empy type used as a branch of a flat union
cannot also be the base type of the flat union, so even though
we are adding a call to variant.type.check() in order to
populate variant.type.members, this is merely a case of
gaining topological sorting of how types are visited (and
type.check() is already set up to allow multiple calls due to
base types).

For simple unions, the same code happens to work by design,
because of our synthesized wrapper classes (however, the
wrapper has a single member 'data' which will never collide
with the one non-variant member 'type', so it doesn't really
matter).

There is no impact to alternates, which intentionally do not
need to call variants.check_clash() (there, at most one of
the variant's branches will be an ObjectType, and even if one
exists, we are not inlining the qapi members of that object
into a parent object, the way we do for unions).  However,
since variants can have non-object branches, it did mean that
the addition of v.type.check() in Variants.check() had to be
conditional on visiting an object type.

No change to generated code.

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

---
v11: keep type.check() in check(), add comment to alternate
v10: create new Variants.check_clash() rather than piggybacking
on .check()
v9: new patch, split off from v8 7/17
---
 scripts/qapi.py | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index c6cb17b..b2d071f 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -992,6 +992,7 @@ class QAPISchemaObjectType(QAPISchemaType):
         if self.variants:
             self.variants.check(schema, seen)
             assert self.variants.tag_member in self.members
+            self.variants.check_clash(schema, seen)

     def is_implicit(self):
         # See QAPISchema._make_implicit_object_type()
@@ -1056,6 +1057,18 @@ class QAPISchemaObjectTypeVariants(object):
         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
         for v in self.variants:
             v.check(schema, self.tag_member.type)
+            if isinstance(v.type, QAPISchemaObjectType):
+                v.type.check(schema)
+
+    def check_clash(self, schema, seen):
+        for v in self.variants:
+            # Reset seen map for each variant, since qapi names from one
+            # branch do not affect another branch
+            vseen = dict(seen)
+            assert isinstance(v.type, QAPISchemaObjectType)
+            assert not v.type.variants       # not implemented
+            for m in v.type.members:
+                m.check_clash(vseen)


 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
@@ -1086,6 +1099,8 @@ class QAPISchemaAlternateType(QAPISchemaType):

     def check(self, schema):
         self.variants.tag_member.check(schema)
+        # Not calling self.variants.check_clash(), because there's nothing
+        # to clash with
         self.variants.check(schema, {})

     def json_type(self):
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 12/28] qapi: Factor out QAPISchemaObjectType.check_clash()
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (10 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 11/28] qapi: Check for qapi collisions of flat union branches Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 13/28] qapi: Hoist tag collision check to Variants.check() Eric Blake
                   ` (15 subsequent siblings)
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Consolidate two common sequences of clash detection into a
new QAPISchemaObjectType.check_clash() helper method.

No change to generated code.

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

---
v11: don't lose isinstance check, and don't hide type.check()
inside check_clash()
v10: rebase on new Variants.check_clash()
v9: new patch, split off from v8 7/17
---
 scripts/qapi.py | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index b2d071f..296b9bb 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -981,10 +981,8 @@ class QAPISchemaObjectType(QAPISchemaType):
         if self._base_name:
             self.base = schema.lookup_type(self._base_name)
             assert isinstance(self.base, QAPISchemaObjectType)
-            assert not self.base.variants       # not implemented
             self.base.check(schema)
-            for m in self.base.members:
-                m.check_clash(seen)
+            self.base.check_clash(schema, seen)
         for m in self.local_members:
             m.check(schema)
             m.check_clash(seen)
@@ -994,6 +992,11 @@ class QAPISchemaObjectType(QAPISchemaType):
             assert self.variants.tag_member in self.members
             self.variants.check_clash(schema, seen)

+    def check_clash(self, schema, seen):
+        assert not self.variants       # not implemented
+        for m in self.members:
+            m.check_clash(seen)
+
     def is_implicit(self):
         # See QAPISchema._make_implicit_object_type()
         return self.name[0] == ':'
@@ -1064,11 +1067,8 @@ class QAPISchemaObjectTypeVariants(object):
         for v in self.variants:
             # Reset seen map for each variant, since qapi names from one
             # branch do not affect another branch
-            vseen = dict(seen)
             assert isinstance(v.type, QAPISchemaObjectType)
-            assert not v.type.variants       # not implemented
-            for m in v.type.members:
-                m.check_clash(vseen)
+            v.type.check_clash(schema, dict(seen))


 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 13/28] qapi: Hoist tag collision check to Variants.check()
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (11 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 12/28] qapi: Factor out QAPISchemaObjectType.check_clash() Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11 13:56   ` Markus Armbruster
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 14/28] qapi: Remove outdated tests related to QMP/branch collisions Eric Blake
                   ` (14 subsequent siblings)
  27 siblings, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Checking that a given QAPISchemaObjectTypeVariant.name is a
member of the corresponding QAPISchemaEnumType of the owning
QAPISchemaObjectTypeVariants.tag_member ensures that there are
no collisions in the generated C union for those tag values
(since the enum itself should have no collisions).

However, this check was the only thing that Variant.check() was
doing beyond the work of the superclass ObjectTypeMember.check(),
and resulted in a difference of the .check() signatures just to
pass the enum type down.

Simplify things by instead doing the tag name check as part of
Variants.check(), at which point we can rely on inheritance
instead of overriding Variant.check().

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

---
v11: don't use tag_type local variable, rebase to v.type.check()
v10: new patch
---
 scripts/qapi.py | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 296b9bb..c6f3fce 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1059,7 +1059,8 @@ class QAPISchemaObjectTypeVariants(object):
             self.tag_member = seen[self.tag_name]
         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
         for v in self.variants:
-            v.check(schema, self.tag_member.type)
+            v.check(schema)
+            assert v.name in self.tag_member.type.values
             if isinstance(v.type, QAPISchemaObjectType):
                 v.type.check(schema)

@@ -1075,10 +1076,6 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
     def __init__(self, name, typ):
         QAPISchemaObjectTypeMember.__init__(self, name, typ, False)

-    def check(self, schema, tag_type):
-        QAPISchemaObjectTypeMember.check(self, schema)
-        assert self.name in tag_type.values
-
     # This function exists to support ugly simple union special cases
     # TODO get rid of them, and drop the function
     def simple_union_type(self):
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 14/28] qapi: Remove outdated tests related to QMP/branch collisions
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (12 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 13/28] qapi: Hoist tag collision check to Variants.check() Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 15/28] qapi: Track owner of each object member Eric Blake
                   ` (13 subsequent siblings)
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Now that branches are in a separate C namespace, we can remove
the restrictions in the parser that claim a branch name would
collide with QMP, and delete the negative tests that are no
longer problematic.  A separate patch can then add positive
tests to qapi-schema-test to test that any corner cases will
compile correctly.

This reverts the scripts/qapi.py portion of commit 7b2a5c2,
now that the assertions that it plugged are no longer possible.

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

---
v11: no change
v10: no change
v9: no change
v8: retitle and update commit message, finish reversion of 7b2a5c2
v7: new patch (also temporarily appeared in subset B v10)
---
 scripts/qapi.py                                | 11 ++---------
 tests/Makefile                                 |  3 ---
 tests/qapi-schema/flat-union-clash-branch.err  |  0
 tests/qapi-schema/flat-union-clash-branch.exit |  1 -
 tests/qapi-schema/flat-union-clash-branch.json | 18 ------------------
 tests/qapi-schema/flat-union-clash-branch.out  | 14 --------------
 tests/qapi-schema/flat-union-clash-type.err    |  1 -
 tests/qapi-schema/flat-union-clash-type.exit   |  1 -
 tests/qapi-schema/flat-union-clash-type.json   | 14 --------------
 tests/qapi-schema/flat-union-clash-type.out    |  0
 tests/qapi-schema/union-clash-type.err         |  1 -
 tests/qapi-schema/union-clash-type.exit        |  1 -
 tests/qapi-schema/union-clash-type.json        |  9 ---------
 tests/qapi-schema/union-clash-type.out         |  0
 14 files changed, 2 insertions(+), 72 deletions(-)
 delete mode 100644 tests/qapi-schema/flat-union-clash-branch.err
 delete mode 100644 tests/qapi-schema/flat-union-clash-branch.exit
 delete mode 100644 tests/qapi-schema/flat-union-clash-branch.json
 delete mode 100644 tests/qapi-schema/flat-union-clash-branch.out
 delete mode 100644 tests/qapi-schema/flat-union-clash-type.err
 delete mode 100644 tests/qapi-schema/flat-union-clash-type.exit
 delete mode 100644 tests/qapi-schema/flat-union-clash-type.json
 delete mode 100644 tests/qapi-schema/flat-union-clash-type.out
 delete mode 100644 tests/qapi-schema/union-clash-type.err
 delete mode 100644 tests/qapi-schema/union-clash-type.exit
 delete mode 100644 tests/qapi-schema/union-clash-type.json
 delete mode 100644 tests/qapi-schema/union-clash-type.out

diff --git a/scripts/qapi.py b/scripts/qapi.py
index c6f3fce..6fc14be 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -548,8 +548,7 @@ def check_union(expr, expr_info):
     base = expr.get('base')
     discriminator = expr.get('discriminator')
     members = expr['data']
-    values = {'MAX': '(automatic)', 'KIND': '(automatic)',
-              'TYPE': '(automatic)'}
+    values = {'MAX': '(automatic)'}

     # Two types of unions, determined by discriminator.

@@ -607,19 +606,13 @@ def check_union(expr, expr_info):
                                " of branch '%s'" % key)

         # If the discriminator names an enum type, then all members
-        # of 'data' must also be members of the enum type, which in turn
-        # must not collide with the discriminator name.
+        # of 'data' must also be members of the enum type.
         if enum_define:
             if key not in enum_define['enum_values']:
                 raise QAPIExprError(expr_info,
                                     "Discriminator value '%s' is not found in "
                                     "enum '%s'" %
                                     (key, enum_define["enum_name"]))
-            if discriminator in enum_define['enum_values']:
-                raise QAPIExprError(expr_info,
-                                    "Discriminator name '%s' collides with "
-                                    "enum value in '%s'" %
-                                    (discriminator, enum_define["enum_name"]))

         # Otherwise, check for conflicts in the generated enum
         else:
diff --git a/tests/Makefile b/tests/Makefile
index 92969e8..c84c6cb 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -277,9 +277,7 @@ qapi-schema += flat-union-bad-base.json
 qapi-schema += flat-union-bad-discriminator.json
 qapi-schema += flat-union-base-any.json
 qapi-schema += flat-union-base-union.json
-qapi-schema += flat-union-clash-branch.json
 qapi-schema += flat-union-clash-member.json
-qapi-schema += flat-union-clash-type.json
 qapi-schema += flat-union-empty.json
 qapi-schema += flat-union-inline.json
 qapi-schema += flat-union-int-branch.json
@@ -341,7 +339,6 @@ qapi-schema += union-bad-branch.json
 qapi-schema += union-base-no-discriminator.json
 qapi-schema += union-clash-branches.json
 qapi-schema += union-clash-data.json
-qapi-schema += union-clash-type.json
 qapi-schema += union-empty.json
 qapi-schema += union-invalid-base.json
 qapi-schema += union-max.json
diff --git a/tests/qapi-schema/flat-union-clash-branch.err b/tests/qapi-schema/flat-union-clash-branch.err
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/qapi-schema/flat-union-clash-branch.exit b/tests/qapi-schema/flat-union-clash-branch.exit
deleted file mode 100644
index 573541a..0000000
--- a/tests/qapi-schema/flat-union-clash-branch.exit
+++ /dev/null
@@ -1 +0,0 @@
-0
diff --git a/tests/qapi-schema/flat-union-clash-branch.json b/tests/qapi-schema/flat-union-clash-branch.json
deleted file mode 100644
index e593336..0000000
--- a/tests/qapi-schema/flat-union-clash-branch.json
+++ /dev/null
@@ -1,18 +0,0 @@
-# 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.
-{ 'enum': 'TestEnum',
-  'data': [ 'base', 'c-d' ] }
-{ 'struct': 'Base',
-  'data': { 'enum1': 'TestEnum', '*c_d': 'str' } }
-{ 'struct': 'Branch1',
-  'data': { 'string': 'str' } }
-{ 'struct': 'Branch2',
-  'data': { 'value': 'int' } }
-{ 'union': 'TestUnion',
-  'base': 'Base',
-  'discriminator': 'enum1',
-  'data': { 'base': 'Branch1',
-            'c-d': 'Branch2' } }
diff --git a/tests/qapi-schema/flat-union-clash-branch.out b/tests/qapi-schema/flat-union-clash-branch.out
deleted file mode 100644
index 8e0da73..0000000
--- a/tests/qapi-schema/flat-union-clash-branch.out
+++ /dev/null
@@ -1,14 +0,0 @@
-object :empty
-object Base
-    member enum1: TestEnum optional=False
-    member c_d: str optional=True
-object Branch1
-    member string: str optional=False
-object Branch2
-    member value: int optional=False
-enum TestEnum ['base', 'c-d']
-object TestUnion
-    base Base
-    tag enum1
-    case base: Branch1
-    case c-d: Branch2
diff --git a/tests/qapi-schema/flat-union-clash-type.err b/tests/qapi-schema/flat-union-clash-type.err
deleted file mode 100644
index b44dd40..0000000
--- a/tests/qapi-schema/flat-union-clash-type.err
+++ /dev/null
@@ -1 +0,0 @@
-tests/qapi-schema/flat-union-clash-type.json:11: Discriminator name 'type' collides with enum value in 'TestEnum'
diff --git a/tests/qapi-schema/flat-union-clash-type.exit b/tests/qapi-schema/flat-union-clash-type.exit
deleted file mode 100644
index d00491f..0000000
--- a/tests/qapi-schema/flat-union-clash-type.exit
+++ /dev/null
@@ -1 +0,0 @@
-1
diff --git a/tests/qapi-schema/flat-union-clash-type.json b/tests/qapi-schema/flat-union-clash-type.json
deleted file mode 100644
index 8f710f0..0000000
--- a/tests/qapi-schema/flat-union-clash-type.json
+++ /dev/null
@@ -1,14 +0,0 @@
-# Flat union branch 'type'
-# Reject this, because we would have a clash in generated C, between the
-# outer tag 'type' and the branch name 'type' within the union.
-# TODO: We could munge the generated C branch name to let it compile.
-{ 'enum': 'TestEnum',
-  'data': [ 'type' ] }
-{ 'struct': 'Base',
-  'data': { 'type': 'TestEnum' } }
-{ 'struct': 'Branch1',
-  'data': { 'string': 'str' } }
-{ 'union': 'TestUnion',
-  'base': 'Base',
-  'discriminator': 'type',
-  'data': { 'type': 'Branch1' } }
diff --git a/tests/qapi-schema/flat-union-clash-type.out b/tests/qapi-schema/flat-union-clash-type.out
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/qapi-schema/union-clash-type.err b/tests/qapi-schema/union-clash-type.err
deleted file mode 100644
index a5dead1..0000000
--- a/tests/qapi-schema/union-clash-type.err
+++ /dev/null
@@ -1 +0,0 @@
-tests/qapi-schema/union-clash-type.json:8: Union 'TestUnion' member 'kind' clashes with '(automatic)'
diff --git a/tests/qapi-schema/union-clash-type.exit b/tests/qapi-schema/union-clash-type.exit
deleted file mode 100644
index d00491f..0000000
--- a/tests/qapi-schema/union-clash-type.exit
+++ /dev/null
@@ -1 +0,0 @@
-1
diff --git a/tests/qapi-schema/union-clash-type.json b/tests/qapi-schema/union-clash-type.json
deleted file mode 100644
index cfc256b..0000000
--- a/tests/qapi-schema/union-clash-type.json
+++ /dev/null
@@ -1,9 +0,0 @@
-# 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'
-# 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.
-{ 'union': 'TestUnion',
-  'data': { 'kind': 'int', 'type': 'str' } }
diff --git a/tests/qapi-schema/union-clash-type.out b/tests/qapi-schema/union-clash-type.out
deleted file mode 100644
index e69de29..0000000
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 15/28] qapi: Track owner of each object member
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (13 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 14/28] qapi: Remove outdated tests related to QMP/branch collisions Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 16/28] qapi: Detect collisions in C member names Eric Blake
                   ` (12 subsequent siblings)
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Future commits will migrate semantic checking away from parsing
and over to the various QAPISchema*.check() methods.  But to
report an error message about an incorrect semantic use of a
member of an object type, it helps to know which type, command,
or event owns the member.  In particular, when a member is
inherited from a base type, it is desirable to associate the
member name with the base type (and not the type calling
member.check()).

Rather than packing additional information into the seen array
passed to each member.check() (as in seen[m.name] = {'member':m,
'owner':type}), it is easier to have each member track the name
of the owner type in the first place (keeping things simpler
with the existing seen[m.name] = m).  The new member.owner field
is set via a new set_owner() method, called when registering
the members and variants arrays with an object or variant type.
Track only a name, and not the actual type object, to avoid
creating a circular python reference chain.

Note that Variants.set_owner() method does not set the owner
for the tag_member field; this field is set earlier either as
part of an object's non-variant members, or explicitly by
alternates.

The source information is intended for human consumption in
error messages, and a new describe() method is added to access
the resulting information.  For example, given the qapi:
  { 'command': 'foo', 'data': { 'string': 'str' } }
an implementation of visit_command() that calls
  arg_type.members[0].describe()
will see "'string' (parameter of foo)".

To make the human-readable name of implicit types work without
duplicating efforts, the describe() method has to reverse the
name of implicit types, via the helper _pretty_owner().

No change to generated code.

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

---
v11: set alternate tag_member owner during init, tweak comments,
keep events with '-args' instead of '-data', prefer '(parameter
of foo)'
v10 (now in subset C): rebase to latest, set alternate tag_member
owner from alternate
v9 (now in subset D): rebase to earlier changes, hoist 'role' to top
of class, split out _pretty_helper(), manage owner when tag_member
appears as part of local_members for unions
v8: don't munge implicit type names [except for event data], and
instead make describe() create nicer messages. Add set_owner(), and
use field 'role' instead of method _describe()
v7: total rewrite: rework implicit object names, assign owner
when initializing owner type rather than when creating member
python object
v6: rebase on new lazy array creation and simple union 'type'
motion; tweak commit message
---
 scripts/qapi.py | 39 +++++++++++++++++++++++++++++++++++++--
 1 file changed, 37 insertions(+), 2 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 6fc14be..79038a8 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -957,8 +957,10 @@ class QAPISchemaObjectType(QAPISchemaType):
         assert base is None or isinstance(base, str)
         for m in local_members:
             assert isinstance(m, QAPISchemaObjectTypeMember)
-        assert (variants is None or
-                isinstance(variants, QAPISchemaObjectTypeVariants))
+            m.set_owner(name)
+        if variants is not None:
+            assert isinstance(variants, QAPISchemaObjectTypeVariants)
+            variants.set_owner(name)
         self._base_name = base
         self.base = None
         self.local_members = local_members
@@ -1013,6 +1015,8 @@ class QAPISchemaObjectType(QAPISchemaType):


 class QAPISchemaObjectTypeMember(object):
+    role = 'member'
+
     def __init__(self, name, typ, optional):
         assert isinstance(name, str)
         assert isinstance(typ, str)
@@ -1021,8 +1025,14 @@ class QAPISchemaObjectTypeMember(object):
         self._type_name = typ
         self.type = None
         self.optional = optional
+        self.owner = None
+
+    def set_owner(self, name):
+        assert not self.owner
+        self.owner = name

     def check(self, schema):
+        assert self.owner
         self.type = schema.lookup_type(self._type_name)
         assert self.type

@@ -1031,6 +1041,22 @@ class QAPISchemaObjectTypeMember(object):
         assert self.name not in seen
         seen[self.name] = self

+    def _pretty_owner(self):
+        owner = self.owner
+        if owner.startswith(':obj-'):
+            # See QAPISchema._make_implicit_object_type() - reverse the
+            # mapping there to create a nice human-readable description
+            owner = owner[5:]
+            if owner.endswith('-arg'):
+                return '(parameter of %s)' % owner[:-4]
+            else:
+                assert owner.endswith('-wrapper')
+                return '(branch of %s)' % owner[:-8]
+        return '(%s of %s)' % (self.role, owner)
+
+    def describe(self):
+        return "'%s' %s" % (self.name, self._pretty_owner())
+

 class QAPISchemaObjectTypeVariants(object):
     def __init__(self, tag_name, tag_member, variants):
@@ -1047,6 +1073,10 @@ class QAPISchemaObjectTypeVariants(object):
         self.tag_member = tag_member
         self.variants = variants

+    def set_owner(self, name):
+        for v in self.variants:
+            v.set_owner(name)
+
     def check(self, schema, seen):
         if not self.tag_member:    # flat union
             self.tag_member = seen[self.tag_name]
@@ -1066,6 +1096,8 @@ class QAPISchemaObjectTypeVariants(object):


 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
+    role = 'branch'
+
     def __init__(self, name, typ):
         QAPISchemaObjectTypeMember.__init__(self, name, typ, False)

@@ -1085,6 +1117,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
         QAPISchemaType.__init__(self, name, info)
         assert isinstance(variants, QAPISchemaObjectTypeVariants)
         assert not variants.tag_name
+        variants.set_owner(name)
+        variants.tag_member.set_owner(self.name)
         self.variants = variants

     def check(self, schema):
@@ -1217,6 +1251,7 @@ class QAPISchema(object):
     def _make_implicit_object_type(self, name, info, role, members):
         if not members:
             return None
+        # See also QAPISchemaObjectTypeMember._pretty_owner()
         name = ':obj-%s-%s' % (name, role)
         if not self.lookup_entity(name, QAPISchemaObjectType):
             self._def_entity(QAPISchemaObjectType(name, info, None,
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 16/28] qapi: Detect collisions in C member names
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (14 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 15/28] qapi: Track owner of each object member Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 17/28] cpu: Convert CpuInfo into flat union Eric Blake
                   ` (11 subsequent siblings)
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Detect attempts to declare two object members that would result
in the same C member name, by keying the 'seen' dictionary off
of the C name rather than the qapi name.  It also requires passing
info through the check_clash() methods.

This addresses a TODO and fixes the previously-broken
args-name-clash test.  The resulting error message demonstrates
the utility of the .describe() method added previously.  No change
to generated code.

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

---
v11: rebase to earlier changes, use 'cname' local variable, shorter
message in test file
v10 (now in subset C): rebase to latest; update commit message
v9 (now in subset D): rebase to earlier changes, now only one test
affected
v8: rebase to earlier changes
v7: split out error reporting prep and member.c_name() addition
v6: rebase to earlier testsuite and info improvements
---
 scripts/qapi.py                        | 31 +++++++++++++++++++------------
 tests/qapi-schema/args-name-clash.err  |  1 +
 tests/qapi-schema/args-name-clash.exit |  2 +-
 tests/qapi-schema/args-name-clash.json |  5 ++---
 tests/qapi-schema/args-name-clash.out  |  6 ------
 5 files changed, 23 insertions(+), 22 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 79038a8..b068141 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -977,20 +977,23 @@ class QAPISchemaObjectType(QAPISchemaType):
             self.base = schema.lookup_type(self._base_name)
             assert isinstance(self.base, QAPISchemaObjectType)
             self.base.check(schema)
-            self.base.check_clash(schema, seen)
+            self.base.check_clash(schema, self.info, seen)
         for m in self.local_members:
             m.check(schema)
-            m.check_clash(seen)
+            m.check_clash(self.info, seen)
         self.members = seen.values()
         if self.variants:
             self.variants.check(schema, seen)
             assert self.variants.tag_member in self.members
-            self.variants.check_clash(schema, seen)
+            self.variants.check_clash(schema, self.info, seen)

-    def check_clash(self, schema, seen):
+    # Check that the members of this type do not cause duplicate JSON fields,
+    # and update seen to track the members seen so far. Report any errors
+    # on behalf of info, which is not necessarily self.info
+    def check_clash(self, schema, info, seen):
         assert not self.variants       # not implemented
         for m in self.members:
-            m.check_clash(seen)
+            m.check_clash(info, seen)

     def is_implicit(self):
         # See QAPISchema._make_implicit_object_type()
@@ -1036,10 +1039,13 @@ class QAPISchemaObjectTypeMember(object):
         self.type = schema.lookup_type(self._type_name)
         assert self.type

-    def check_clash(self, seen):
-        # TODO change key of seen from QAPI name to C name
-        assert self.name not in seen
-        seen[self.name] = self
+    def check_clash(self, info, seen):
+        cname = c_name(self.name)
+        if cname in seen:
+            raise QAPIExprError(info,
+                                "%s collides with %s"
+                                % (self.describe(), seen[cname].describe()))
+        seen[cname] = self

     def _pretty_owner(self):
         owner = self.owner
@@ -1079,7 +1085,8 @@ class QAPISchemaObjectTypeVariants(object):

     def check(self, schema, seen):
         if not self.tag_member:    # flat union
-            self.tag_member = seen[self.tag_name]
+            self.tag_member = seen[c_name(self.tag_name)]
+            assert self.tag_name == self.tag_member.name
         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
         for v in self.variants:
             v.check(schema)
@@ -1087,12 +1094,12 @@ class QAPISchemaObjectTypeVariants(object):
             if isinstance(v.type, QAPISchemaObjectType):
                 v.type.check(schema)

-    def check_clash(self, schema, seen):
+    def check_clash(self, schema, info, seen):
         for v in self.variants:
             # Reset seen map for each variant, since qapi names from one
             # branch do not affect another branch
             assert isinstance(v.type, QAPISchemaObjectType)
-            v.type.check_clash(schema, dict(seen))
+            v.type.check_clash(schema, info, dict(seen))


 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
diff --git a/tests/qapi-schema/args-name-clash.err b/tests/qapi-schema/args-name-clash.err
index e69de29..d953e8d 100644
--- a/tests/qapi-schema/args-name-clash.err
+++ b/tests/qapi-schema/args-name-clash.err
@@ -0,0 +1 @@
+tests/qapi-schema/args-name-clash.json:4: 'a_b' (parameter of oops) collides with 'a-b' (parameter of oops)
diff --git a/tests/qapi-schema/args-name-clash.exit b/tests/qapi-schema/args-name-clash.exit
index 573541a..d00491f 100644
--- a/tests/qapi-schema/args-name-clash.exit
+++ b/tests/qapi-schema/args-name-clash.exit
@@ -1 +1 @@
-0
+1
diff --git a/tests/qapi-schema/args-name-clash.json b/tests/qapi-schema/args-name-clash.json
index 9e8f889..61423cb 100644
--- a/tests/qapi-schema/args-name-clash.json
+++ b/tests/qapi-schema/args-name-clash.json
@@ -1,5 +1,4 @@
 # C member name collision
-# FIXME - This parses, but fails to compile, because the C struct is given
-# two 'a_b' members.  Either reject this at parse time, or munge the C names
-# to avoid the collision.
+# Reject members that clash when mapped to C names (we would have two 'a_b'
+# members).
 { 'command': 'oops', 'data': { 'a-b': 'str', 'a_b': 'str' } }
diff --git a/tests/qapi-schema/args-name-clash.out b/tests/qapi-schema/args-name-clash.out
index 9b2f6e4..e69de29 100644
--- a/tests/qapi-schema/args-name-clash.out
+++ b/tests/qapi-schema/args-name-clash.out
@@ -1,6 +0,0 @@
-object :empty
-object :obj-oops-arg
-    member a-b: str optional=False
-    member a_b: str optional=False
-command oops :obj-oops-arg -> None
-   gen=True success_response=True
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 17/28] cpu: Convert CpuInfo into flat union
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (15 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 16/28] qapi: Detect collisions in C member names Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11 14:13   ` Markus Armbruster
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 18/28] qerror: more error_setg() usage Eric Blake
                   ` (10 subsequent siblings)
  27 siblings, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, armbru, Luiz Capitulino

The CpuInfo struct is used only by the 'query-cpus' output
command, so we are free to modify it by adding fields (clients
are already supposed to ignore unknown output fields), or by
changing optional members to mandatory, while still keeping
QMP wire compatibility with older versions of qemu.

When qapi type CpuInfo was originally created for 0.14, we had
no notion of a flat union, and instead just listed a bunch of
optional fields with documentation about the mutually-exclusive
choice of which instruction pointer field(s) would be provided
for a given architecture.  But now that we have flat unions and
introspection, it is better to segregate off which fields will
be provided according to the actual architecture.  With this in
place, we no longer need the fields to be optional, because the
choice of the new 'arch' discriminator serves that role.

This has an additional benefit: the old all-in-one struct was
the only place in the code base that had a case-sensitive
naming of members 'pc' vs. 'PC'.  Separating these spellings
into different branches of the flat union will allow us to add
restrictions against future case-insensitive collisions, since
that is generally a poor interface practice.

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

---
v11: also fix qmp-commands.hx, improve commit message. If this
misses 2.5 (likely), it will need updates to call out 2.6
v10: new patch
---
 cpus.c           |  31 ++++++++------
 hmp.c            |  30 +++++++++-----
 qapi-schema.json | 120 ++++++++++++++++++++++++++++++++++++++++++++++---------
 qmp-commands.hx  |   4 ++
 4 files changed, 143 insertions(+), 42 deletions(-)

diff --git a/cpus.c b/cpus.c
index 877bd70..77ba15b 100644
--- a/cpus.c
+++ b/cpus.c
@@ -1556,22 +1556,29 @@ CpuInfoList *qmp_query_cpus(Error **errp)
         info->value->qom_path = object_get_canonical_path(OBJECT(cpu));
         info->value->thread_id = cpu->thread_id;
 #if defined(TARGET_I386)
-        info->value->has_pc = true;
-        info->value->pc = env->eip + env->segs[R_CS].base;
+        info->value->arch = CPU_INFO_ARCH_X86;
+        info->value->u.x86 = g_new0(CpuInfoX86, 1);
+        info->value->u.x86->pc = env->eip + env->segs[R_CS].base;
 #elif defined(TARGET_PPC)
-        info->value->has_nip = true;
-        info->value->nip = env->nip;
+        info->value->arch = CPU_INFO_ARCH_PPC;
+        info->value->u.ppc = g_new0(CpuInfoPpc, 1);
+        info->value->u.ppc->nip = env->nip;
 #elif defined(TARGET_SPARC)
-        info->value->has_pc = true;
-        info->value->pc = env->pc;
-        info->value->has_npc = true;
-        info->value->npc = env->npc;
+        info->value->arch = CPU_INFO_ARCH_SPARC;
+        info->value->u.sparc = g_new0(CpuInfoSPARC, 1);
+        info->value->u.sparc->pc = env->pc;
+        info->value->u.sparc->npc = env->npc;
 #elif defined(TARGET_MIPS)
-        info->value->has_PC = true;
-        info->value->PC = env->active_tc.PC;
+        info->value->arch = CPU_INFO_ARCH_MIPS;
+        info->value->u.mips = g_new0(CpuInfoMips, 1);
+        info->value->u.mips->PC = env->active_tc.PC;
 #elif defined(TARGET_TRICORE)
-        info->value->has_PC = true;
-        info->value->PC = env->PC;
+        info->value->arch = CPU_INFO_ARCH_TRICORE;
+        info->value->u.tricore = g_new0(CpuInfoTricore, 1);
+        info->value->u.tricore->PC = env->PC;
+#else
+        info->value->arch = CPU_INFO_ARCH_OTHER;
+        info->value->u.other = g_new0(CpuInfoOther, 1);
 #endif

         /* XXX: waiting for the qapi to support GSList */
diff --git a/hmp.c b/hmp.c
index e1f854a..b992a29 100644
--- a/hmp.c
+++ b/hmp.c
@@ -310,17 +310,25 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict)

         monitor_printf(mon, "%c CPU #%" PRId64 ":", active, cpu->value->CPU);

-        if (cpu->value->has_pc) {
-            monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->pc);
-        }
-        if (cpu->value->has_nip) {
-            monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->nip);
-        }
-        if (cpu->value->has_npc) {
-            monitor_printf(mon, " npc=0x%016" PRIx64, cpu->value->npc);
-        }
-        if (cpu->value->has_PC) {
-            monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->PC);
+        switch (cpu->value->arch) {
+        case CPU_INFO_ARCH_X86:
+            monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->u.x86->pc);
+            break;
+        case CPU_INFO_ARCH_PPC:
+            monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->u.ppc->nip);
+            break;
+        case CPU_INFO_ARCH_SPARC:
+            monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->u.sparc->pc);
+            monitor_printf(mon, " npc=0x%016" PRIx64, cpu->value->u.sparc->npc);
+            break;
+        case CPU_INFO_ARCH_MIPS:
+            monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.mips->PC);
+            break;
+        case CPU_INFO_ARCH_TRICORE:
+            monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.tricore->PC);
+            break;
+        default:
+            break;
         }

         if (cpu->value->halted) {
diff --git a/qapi-schema.json b/qapi-schema.json
index 8c3a42a..30bcedf 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -742,43 +742,125 @@
 { 'command': 'query-mice', 'returns': ['MouseInfo'] }

 ##
-# @CpuInfo:
+# @CpuInfoArch:
 #
-# Information about a virtual CPU
+# An enumeration of cpu types that enable additional information during
+# @query-cpus.
+#
+# Since: 2.5
+##
+{ 'enum': 'CpuInfoArch',
+  'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 'other' ] }
+
+##
+# @CpuInfoBase:
+#
+# Common information about a virtual CPU
 #
 # @CPU: the index of the virtual CPU
 #
-# @current: this only exists for backwards compatible and should be ignored
+# @current: this only exists for backwards compatibility and should be ignored
 #
 # @halted: true if the virtual CPU is in the halt state.  Halt usually refers
 #          to a processor specific low power mode.
 #
 # @qom_path: path to the CPU object in the QOM tree (since 2.4)
 #
-# @pc: #optional If the target is i386 or x86_64, this is the 64-bit instruction
-#                pointer.
-#                If the target is Sparc, this is the PC component of the
-#                instruction pointer.
-#
-# @nip: #optional If the target is PPC, the instruction pointer
-#
-# @npc: #optional If the target is Sparc, the NPC component of the instruction
-#                 pointer
-#
-# @PC: #optional If the target is MIPS, the instruction pointer
-#
 # @thread_id: ID of the underlying host thread
 #
+# @arch: architecture of the cpu, which determines which additional fields
+#        will be listed (since 2.5)
+#
 # Since: 0.14.0
 #
 # Notes: @halted is a transient state that changes frequently.  By the time the
 #        data is sent to the client, the guest may no longer be halted.
 ##
-{ 'struct': 'CpuInfo',
+{ 'struct': 'CpuInfoBase',
   'data': {'CPU': 'int', 'current': 'bool', 'halted': 'bool',
-           'qom_path': 'str',
-           '*pc': 'int', '*nip': 'int', '*npc': 'int', '*PC': 'int',
-           'thread_id': 'int'} }
+           'qom_path': 'str', 'thread_id': 'int', 'arch': 'CpuInfoArch' } }
+
+##
+# @CpuInfo:
+#
+# Information about a virtual CPU
+#
+# Since: 0.14.0
+##
+{ 'union': 'CpuInfo', 'base': 'CpuInfoBase', 'discriminator': 'arch',
+  'data': { 'x86': 'CpuInfoX86',
+            'sparc': 'CpuInfoSparc',
+            'ppc': 'CpuInfoPpc',
+            'mips': 'CpuInfoMips',
+            'tricore': 'CpuInfoTricore',
+            'other': 'CpuInfoOther' } }
+
+##
+# @CpuInfoX86:
+#
+# Additional information about a virtual i386 or x86_64 CPU
+#
+# @pc: the 64-bit instruction pointer
+#
+# Since 2.5
+##
+{ 'struct': 'CpuInfoX86', 'data': { 'pc': 'int' } }
+
+##
+# @CpuInfoSparc:
+#
+# Additional information about a virtual Sparc CPU
+#
+# @pc: the PC component of the instruction pointer
+#
+# @npc: the NPC component of the instruction pointer
+#
+# Since 2.5
+##
+{ 'struct': 'CpuInfoSparc', 'data': { 'pc': 'int', 'npc': 'int' } }
+
+##
+# @CpuInfoPpc:
+#
+# Additional information about a virtual PPC CPU
+#
+# @nip: the instruction pointer
+#
+# Since 2.5
+##
+{ 'struct': 'CpuInfoPpc', 'data': { 'nip': 'int' } }
+
+##
+# @CpuInfoMips:
+#
+# Additional information about a virtual MIPS CPU
+#
+# @PC: the instruction pointer
+#
+# Since 2.5
+##
+{ 'struct': 'CpuInfoMips', 'data': { 'PC': 'int' } }
+
+##
+# @CpuInfoMips:
+#
+# Additional information about a virtual Tricore CPU
+#
+# @PC: the instruction pointer
+#
+# Since 2.5
+##
+{ 'struct': 'CpuInfoTricore', 'data': { 'PC': 'int' } }
+
+##
+# @CpuInfoOther:
+#
+# No additional information is available about the virtual CPU
+#
+# Since 2.5
+#
+##
+{ 'struct': 'CpuInfoOther', 'data': { } }

 ##
 # @query-cpus:
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 7f85d40..66e0c39 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2657,6 +2657,8 @@ Return a json-array. Each CPU is represented by a json-object, which contains:
 - "current": true if this is the current CPU, false otherwise (json-bool)
 - "halted": true if the cpu is halted, false otherwise (json-bool)
 - "qom_path": path to the CPU object in the QOM tree (json-str)
+- "arch": architecture of the cpu, which determines what additional
+          keys will be present (json-str)
 - Current program counter. The key's name depends on the architecture:
      "pc": i386/x86_64 (json-int)
      "nip": PPC (json-int)
@@ -2674,6 +2676,7 @@ Example:
             "current":true,
             "halted":false,
             "qom_path":"/machine/unattached/device[0]",
+            "arch":"x86",
             "pc":3227107138,
             "thread_id":3134
          },
@@ -2682,6 +2685,7 @@ Example:
             "current":false,
             "halted":true,
             "qom_path":"/machine/unattached/device[2]",
+            "arch":"x86",
             "pc":7108165,
             "thread_id":3135
          }
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 18/28] qerror: more error_setg() usage
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (16 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 17/28] cpu: Convert CpuInfo into flat union Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11 13:26   ` Andreas Färber
  2015-11-11 14:21   ` Markus Armbruster
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values Eric Blake
                   ` (9 subsequent siblings)
  27 siblings, 2 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Jiri Pirko, Eduardo Habkost,
	open list:Block layer core, Michael S. Tsirkin, Jason Wang,
	armbru, Scott Feldman, Paolo Bonzini, Andreas Färber,
	Richard Henderson

A few uses of error_set(ERROR_CLASS_GENERIC_ERROR) have snuck in
since c6bd8c706.  Nuke them.

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

---
v11: new patch
---
 block.c                       |  3 +--
 docs/writing-qmp-commands.txt | 20 +++++++++-----------
 hw/i386/pc.c                  |  2 +-
 hw/net/rocker/rocker.c        |  6 ++----
 hw/net/rocker/rocker_of_dpa.c | 12 ++++--------
 qom/object.c                  |  4 ++--
 6 files changed, 19 insertions(+), 28 deletions(-)

diff --git a/block.c b/block.c
index e9f40dc..53a978a 100644
--- a/block.c
+++ b/block.c
@@ -1795,8 +1795,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,

     ret = bdrv_flush(reopen_state->bs);
     if (ret) {
-        error_set(errp, ERROR_CLASS_GENERIC_ERROR, "Error (%s) flushing drive",
-                  strerror(-ret));
+        error_setg_errno(errp, -ret, "Error flushing drive");
         goto error;
     }

diff --git a/docs/writing-qmp-commands.txt b/docs/writing-qmp-commands.txt
index 8647cac..59aa77a 100644
--- a/docs/writing-qmp-commands.txt
+++ b/docs/writing-qmp-commands.txt
@@ -210,7 +210,7 @@ if you don't see these strings, then something went wrong.
 === Errors ===

 QMP commands should use the error interface exported by the error.h header
-file. Basically, errors are set by calling the error_set() function.
+file. Basically, most errors are set by calling the error_setg() function.

 Let's say we don't accept the string "message" to contain the word "love". If
 it does contain it, we want the "hello-world" command to return an error:
@@ -219,8 +219,7 @@ void qmp_hello_world(bool has_message, const char *message, Error **errp)
 {
     if (has_message) {
         if (strstr(message, "love")) {
-            error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-                      "the word 'love' is not allowed");
+            error_setg(errp, "the word 'love' is not allowed");
             return;
         }
         printf("%s\n", message);
@@ -229,10 +228,8 @@ void qmp_hello_world(bool has_message, const char *message, Error **errp)
     }
 }

-The first argument to the error_set() function is the Error pointer to pointer,
-which is passed to all QMP functions. The second argument is a ErrorClass
-value, which should be ERROR_CLASS_GENERIC_ERROR most of the time (more
-details about error classes are given below). The third argument is a human
+The first argument to the error_setg() function is the Error pointer
+to pointer, which is passed to all QMP functions. The next argument is a human
 description of the error, this is a free-form printf-like string.

 Let's test the example above. Build qemu, run it as defined in the "Testing"
@@ -249,8 +246,9 @@ The QMP server's response should be:
     }
 }

-As a general rule, all QMP errors should use ERROR_CLASS_GENERIC_ERROR. There
-are two exceptions to this rule:
+As a general rule, all QMP errors should use ERROR_CLASS_GENERIC_ERROR
+(done by default when using error_setg()). There are two exceptions to
+this rule:

  1. A non-generic ErrorClass value exists* for the failure you want to report
     (eg. DeviceNotFound)
@@ -259,8 +257,8 @@ are two exceptions to this rule:
     want to report, hence you have to add a new ErrorClass value so that they
     can check for it

-If the failure you want to report doesn't fall in one of the two cases above,
-just report ERROR_CLASS_GENERIC_ERROR.
+If the failure you want to report falls into one of the two cases above,
+use error_set() with a second argument of an ErrorClass value.

  * All existing ErrorClass values are defined in the qapi-schema.json file

diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 0cb8afd..dfb57a8 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1795,7 +1795,7 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
         return;
     }
     if (value > (1ULL << 32)) {
-        error_set(&error, ERROR_CLASS_GENERIC_ERROR,
+        error_setg(&error,
                   "Machine option 'max-ram-below-4g=%"PRIu64
                   "' expects size less than or equal to 4G", value);
         error_propagate(errp, error);
diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c
index bb6fdc3..c57f1a6 100644
--- a/hw/net/rocker/rocker.c
+++ b/hw/net/rocker/rocker.c
@@ -101,8 +101,7 @@ RockerSwitch *qmp_query_rocker(const char *name, Error **errp)

     r = rocker_find(name);
     if (!r) {
-        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-                  "rocker %s not found", name);
+        error_setg(errp, "rocker %s not found", name);
         return NULL;
     }

@@ -122,8 +121,7 @@ RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp)

     r = rocker_find(name);
     if (!r) {
-        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-                  "rocker %s not found", name);
+        error_setg(errp, "rocker %s not found", name);
         return NULL;
     }

diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c
index 1ad2791..3cf1d61 100644
--- a/hw/net/rocker/rocker_of_dpa.c
+++ b/hw/net/rocker/rocker_of_dpa.c
@@ -2462,15 +2462,13 @@ RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name,

     r = rocker_find(name);
     if (!r) {
-        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-                  "rocker %s not found", name);
+        error_setg(errp, "rocker %s not found", name);
         return NULL;
     }

     w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA);
     if (!w) {
-        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-                  "rocker %s doesn't have OF-DPA world", name);
+        error_setg(errp, "rocker %s doesn't have OF-DPA world", name);
         return NULL;
     }

@@ -2597,15 +2595,13 @@ RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name,

     r = rocker_find(name);
     if (!r) {
-        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-                  "rocker %s not found", name);
+        error_setg(errp, "rocker %s not found", name);
         return NULL;
     }

     w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA);
     if (!w) {
-        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-                  "rocker %s doesn't have OF-DPA world", name);
+        error_setg(errp, "rocker %s doesn't have OF-DPA world", name);
         return NULL;
     }

diff --git a/qom/object.c b/qom/object.c
index fc6e161..c0decb6 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1330,8 +1330,8 @@ static Object *object_resolve_link(Object *obj, const char *name,
     target = object_resolve_path_type(path, target_type, &ambiguous);

     if (ambiguous) {
-        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-                  "Path '%s' does not uniquely identify an object", path);
+        error_setg(errp, "Path '%s' does not uniquely identify an object",
+                   path);
     } else if (!target) {
         target = object_resolve_path(path, &ambiguous);
         if (target || ambiguous) {
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (17 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 18/28] qerror: more error_setg() usage Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11 13:29   ` Andreas Färber
                     ` (2 more replies)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 20/28] qapi: Forbid case-insensitive clashes Eric Blake
                   ` (8 subsequent siblings)
  27 siblings, 3 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Peter Maydell, Michael Roth,
	open list:Block layer core, Michael S. Tsirkin, Jason Wang,
	armbru, Luiz Capitulino, Gerd Hoffmann, Amit Shah,
	Andreas Färber

When munging enum values, the fact that we were passing the entire
prefix + value through camel_to_upper() meant that enum values
spelled with CamelCase could be turned into CAMEL_CASE.  However,
this provides a potential collision (both OneTwo and One-Two would
munge into ONE_TWO).  By changing the generation of enum constants
to always be prefix + '_' + c_name(value).upper(), we can avoid
any risk of collisions (if we can also ensure no case collisions,
in the next patch) without having to think about what the
heuristics in camel_to_upper() will actually do to the value.

Thankfully, only two enums are affected: ErrorClass and InputButton.

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

---
v11: new patch
---
 backends/rng-egd.c            |  2 +-
 balloon.c                     |  4 ++--
 block.c                       |  2 +-
 blockdev-nbd.c                |  2 +-
 blockdev.c                    | 24 ++++++++++++------------
 docs/writing-qmp-commands.txt |  2 +-
 hmp.c                         |  6 +++---
 hw/input/hid.c                |  4 ++--
 hw/input/ps2.c                |  4 ++--
 hw/input/virtio-input-hid.c   |  4 ++--
 include/qapi/error.h          |  6 +++---
 include/ui/qemu-spice.h       |  2 +-
 monitor.c                     |  8 ++++----
 net/net.c                     |  4 ++--
 qapi/qmp-dispatch.c           |  2 +-
 qdev-monitor.c                |  4 ++--
 qmp.c                         |  8 ++++----
 qom/object.c                  |  4 ++--
 scripts/qapi.py               |  2 +-
 ui/cocoa.m                    |  4 ++--
 ui/gtk.c                      |  4 ++--
 ui/input-legacy.c             |  4 ++--
 ui/input.c                    |  2 +-
 ui/sdl.c                      |  4 ++--
 ui/sdl2.c                     |  4 ++--
 ui/spice-input.c              |  4 ++--
 ui/vnc.c                      |  4 ++--
 util/error.c                  |  6 +++---
 28 files changed, 65 insertions(+), 65 deletions(-)

diff --git a/backends/rng-egd.c b/backends/rng-egd.c
index 6c13409..d92af31 100644
--- a/backends/rng-egd.c
+++ b/backends/rng-egd.c
@@ -147,7 +147,7 @@ static void rng_egd_opened(RngBackend *b, Error **errp)

     s->chr = qemu_chr_find(s->chr_name);
     if (s->chr == NULL) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", s->chr_name);
         return;
     }
diff --git a/balloon.c b/balloon.c
index 0f45d1b..8aab0b8 100644
--- a/balloon.c
+++ b/balloon.c
@@ -51,12 +51,12 @@ void qemu_balloon_inhibit(bool state)
 static bool have_balloon(Error **errp)
 {
     if (kvm_enabled() && !kvm_has_sync_mmu()) {
-        error_set(errp, ERROR_CLASS_KVM_MISSING_CAP,
+        error_set(errp, ERROR_CLASS_KVMMISSINGCAP,
                   "Using KVM without synchronous MMU, balloon unavailable");
         return false;
     }
     if (!balloon_event_fn) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE,
+        error_set(errp, ERROR_CLASS_DEVICENOTACTIVE,
                   "No balloon device has been activated");
         return false;
     }
diff --git a/block.c b/block.c
index 53a978a..0e0058d 100644
--- a/block.c
+++ b/block.c
@@ -2584,7 +2584,7 @@ void bdrv_add_key(BlockDriverState *bs, const char *key, Error **errp)
         }
     } else {
         if (bdrv_key_required(bs)) {
-            error_set(errp, ERROR_CLASS_DEVICE_ENCRYPTED,
+            error_set(errp, ERROR_CLASS_DEVICEENCRYPTED,
                       "'%s' (%s) is encrypted",
                       bdrv_get_device_or_node_name(bs),
                       bdrv_get_encrypted_filename(bs));
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index bcdd18b..6184eef 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -90,7 +90,7 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,

     blk = blk_by_name(device);
     if (!blk) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", device);
         return;
     }
diff --git a/blockdev.c b/blockdev.c
index 97be42f..115f46e 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1215,7 +1215,7 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,

     blk = blk_by_name(device);
     if (!blk) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", device);
         return NULL;
     }
@@ -1411,7 +1411,7 @@ static void internal_snapshot_prepare(BlkTransactionState *common,
     /* 2. check for validation */
     blk = blk_by_name(device);
     if (!blk) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", device);
         return;
     }
@@ -1697,7 +1697,7 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp)

     blk = blk_by_name(backup->device);
     if (!blk) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", backup->device);
         return;
     }
@@ -1984,7 +1984,7 @@ void qmp_eject(const char *device, bool has_force, bool force, Error **errp)

     blk = blk_by_name(device);
     if (!blk) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", device);
         return;
     }
@@ -2052,7 +2052,7 @@ void qmp_change_blockdev(const char *device, const char *filename,

     blk = blk_by_name(device);
     if (!blk) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", device);
         return;
     }
@@ -2118,7 +2118,7 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,

     blk = blk_by_name(device);
     if (!blk) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", device);
         return;
     }
@@ -2444,7 +2444,7 @@ void qmp_block_stream(const char *device,

     blk = blk_by_name(device);
     if (!blk) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", device);
         return;
     }
@@ -2524,7 +2524,7 @@ void qmp_block_commit(const char *device,
      *  scenario in which all optional arguments are omitted. */
     blk = blk_by_name(device);
     if (!blk) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", device);
         return;
     }
@@ -2639,7 +2639,7 @@ void qmp_drive_backup(const char *device, const char *target,

     blk = blk_by_name(device);
     if (!blk) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", device);
         return;
     }
@@ -2864,7 +2864,7 @@ void qmp_drive_mirror(const char *device, const char *target,

     blk = blk_by_name(device);
     if (!blk) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", device);
         return;
     }
@@ -3028,7 +3028,7 @@ static BlockJob *find_block_job(const char *device, AioContext **aio_context,
     return bs->job;

 notfound:
-    error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE,
+    error_set(errp, ERROR_CLASS_DEVICENOTACTIVE,
               "No active block job on device '%s'", device);
     if (*aio_context) {
         aio_context_release(*aio_context);
@@ -3136,7 +3136,7 @@ void qmp_change_backing_file(const char *device,

     blk = blk_by_name(device);
     if (!blk) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", device);
         return;
     }
diff --git a/docs/writing-qmp-commands.txt b/docs/writing-qmp-commands.txt
index 59aa77a..21b8686 100644
--- a/docs/writing-qmp-commands.txt
+++ b/docs/writing-qmp-commands.txt
@@ -246,7 +246,7 @@ The QMP server's response should be:
     }
 }

-As a general rule, all QMP errors should use ERROR_CLASS_GENERIC_ERROR
+As a general rule, all QMP errors should use ERROR_CLASS_GENERICERROR
 (done by default when using error_setg()). There are two exceptions to
 this rule:

diff --git a/hmp.c b/hmp.c
index b992a29..ad3eb44 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1364,7 +1364,7 @@ void hmp_change(Monitor *mon, const QDict *qdict)

     qmp_change(device, target, !!arg, arg, &err);
     if (err &&
-        error_get_class(err) == ERROR_CLASS_DEVICE_ENCRYPTED) {
+        error_get_class(err) == ERROR_CLASS_DEVICEENCRYPTED) {
         error_free(err);
         monitor_read_block_device_key(mon, device, NULL, NULL);
         return;
@@ -1907,7 +1907,7 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
     if (blk) {
         qemuio_command(blk, command);
     } else {
-        error_set(&err, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(&err, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", device);
     }

@@ -2049,7 +2049,7 @@ void hmp_qom_set(Monitor *mon, const QDict *qdict)

     obj = object_resolve_path(path, &ambiguous);
     if (obj == NULL) {
-        error_set(&err, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(&err, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", path);
     } else {
         if (ambiguous) {
diff --git a/hw/input/hid.c b/hw/input/hid.c
index e39269f..e68c3b1 100644
--- a/hw/input/hid.c
+++ b/hw/input/hid.c
@@ -139,9 +139,9 @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
     case INPUT_EVENT_KIND_BTN:
         if (evt->u.btn->down) {
             e->buttons_state |= bmap[evt->u.btn->button];
-            if (evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) {
+            if (evt->u.btn->button == INPUT_BUTTON_WHEELUP) {
                 e->dz--;
-            } else if (evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+            } else if (evt->u.btn->button == INPUT_BUTTON_WHEELDOWN) {
                 e->dz++;
             }
         } else {
diff --git a/hw/input/ps2.c b/hw/input/ps2.c
index 3d6d496..aaa4479 100644
--- a/hw/input/ps2.c
+++ b/hw/input/ps2.c
@@ -405,9 +405,9 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
     case INPUT_EVENT_KIND_BTN:
         if (evt->u.btn->down) {
             s->mouse_buttons |= bmap[evt->u.btn->button];
-            if (evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) {
+            if (evt->u.btn->button == INPUT_BUTTON_WHEELUP) {
                 s->mouse_dz--;
-            } else if (evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+            } else if (evt->u.btn->button == INPUT_BUTTON_WHEELDOWN) {
                 s->mouse_dz++;
             }
         } else {
diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c
index bdd479c..8838ea5 100644
--- a/hw/input/virtio-input-hid.c
+++ b/hw/input/virtio-input-hid.c
@@ -142,8 +142,8 @@ static const unsigned int keymap_button[INPUT_BUTTON_MAX] = {
     [INPUT_BUTTON_LEFT]              = BTN_LEFT,
     [INPUT_BUTTON_RIGHT]             = BTN_RIGHT,
     [INPUT_BUTTON_MIDDLE]            = BTN_MIDDLE,
-    [INPUT_BUTTON_WHEEL_UP]          = BTN_GEAR_UP,
-    [INPUT_BUTTON_WHEEL_DOWN]        = BTN_GEAR_DOWN,
+    [INPUT_BUTTON_WHEELUP]           = BTN_GEAR_UP,
+    [INPUT_BUTTON_WHEELDOWN]         = BTN_GEAR_DOWN,
 };

 static const unsigned int axismap_rel[INPUT_AXIS_MAX] = {
diff --git a/include/qapi/error.h b/include/qapi/error.h
index 4d42cdc..30b6634 100644
--- a/include/qapi/error.h
+++ b/include/qapi/error.h
@@ -97,7 +97,7 @@ const char *error_get_pretty(Error *err);

 /*
  * Get @err's error class.
- * Note: use of error classes other than ERROR_CLASS_GENERIC_ERROR is
+ * Note: use of error classes other than ERROR_CLASS_GENERICERROR is
  * strongly discouraged.
  */
 ErrorClass error_get_class(const Error *err);
@@ -109,7 +109,7 @@ ErrorClass error_get_class(const Error *err);
  * If @errp is &error_abort, print a suitable message and abort().
  * If @errp is &error_fatal, print a suitable message and exit(1).
  * If @errp is anything else, *@errp must be NULL.
- * The new error's class is ERROR_CLASS_GENERIC_ERROR, and its
+ * The new error's class is ERROR_CLASS_GENERICERROR, and its
  * human-readable error message is made from printf-style @fmt, ...
  */
 #define error_setg(errp, fmt, ...)                              \
@@ -205,7 +205,7 @@ void error_report_err(Error *);

 /*
  * Just like error_setg(), except you get to specify the error class.
- * Note: use of error classes other than ERROR_CLASS_GENERIC_ERROR is
+ * Note: use of error classes other than ERROR_CLASS_GENERICERROR is
  * strongly discouraged.
  */
 #define error_set(errp, err_class, fmt, ...)                    \
diff --git a/include/ui/qemu-spice.h b/include/ui/qemu-spice.h
index f9ce357..02847c8 100644
--- a/include/ui/qemu-spice.h
+++ b/include/ui/qemu-spice.h
@@ -81,7 +81,7 @@ static inline int qemu_spice_display_add_client(int csock, int skipauth,
 static inline bool qemu_using_spice(Error **errp)
 {
     if (!using_spice) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE,
+        error_set(errp, ERROR_CLASS_DEVICENOTACTIVE,
                   "SPICE is not in use");
         return false;
     }
diff --git a/monitor.c b/monitor.c
index 3295840..bd1e194 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1375,7 +1375,7 @@ static void hmp_mouse_move(Monitor *mon, const QDict *qdict)
     if (dz_str) {
         dz = strtol(dz_str, NULL, 0);
         if (dz != 0) {
-            button = (dz > 0) ? INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN;
+            button = (dz > 0) ? INPUT_BUTTON_WHEELUP : INPUT_BUTTON_WHEELDOWN;
             qemu_input_queue_btn(NULL, button, true);
             qemu_input_event_sync();
             qemu_input_queue_btn(NULL, button, false);
@@ -3578,13 +3578,13 @@ static bool invalid_qmp_mode(const Monitor *mon, const mon_cmd_t *cmd,
     bool is_cap = cmd->mhandler.cmd_new == qmp_capabilities;

     if (is_cap && mon->qmp.in_command_mode) {
-        error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_COMMANDNOTFOUND,
                   "Capabilities negotiation is already complete, command "
                   "'%s' ignored", cmd->name);
         return true;
     }
     if (!is_cap && !mon->qmp.in_command_mode) {
-        error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_COMMANDNOTFOUND,
                   "Expecting capabilities negotiation with "
                   "'qmp_capabilities' before command '%s'", cmd->name);
         return true;
@@ -3873,7 +3873,7 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
     trace_handle_qmp_command(mon, cmd_name);
     cmd = qmp_find_cmd(cmd_name);
     if (!cmd) {
-        error_set(&local_err, ERROR_CLASS_COMMAND_NOT_FOUND,
+        error_set(&local_err, ERROR_CLASS_COMMANDNOTFOUND,
                   "The command %s has not been found", cmd_name);
         goto err_out;
     }
diff --git a/net/net.c b/net/net.c
index ade6051..0d2a8b2 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1170,7 +1170,7 @@ void qmp_netdev_del(const char *id, Error **errp)

     nc = qemu_find_netdev(id);
     if (!nc) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", id);
         return;
     }
@@ -1300,7 +1300,7 @@ void qmp_set_link(const char *name, bool up, Error **errp)
                                           MAX_QUEUE_NUM);

     if (queues == 0) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", name);
         return;
     }
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
index 7bcc860..e969bc2 100644
--- a/qapi/qmp-dispatch.c
+++ b/qapi/qmp-dispatch.c
@@ -76,7 +76,7 @@ static QObject *do_qmp_dispatch(QObject *request, Error **errp)
     command = qdict_get_str(dict, "execute");
     cmd = qmp_find_command(command);
     if (cmd == NULL) {
-        error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_COMMANDNOTFOUND,
                   "The command %s has not been found", command);
         return NULL;
     }
diff --git a/qdev-monitor.c b/qdev-monitor.c
index a35098f..1c39432 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -461,7 +461,7 @@ static BusState *qbus_find(const char *path, Error **errp)
         pos += len;
         dev = qbus_find_dev(bus, elem);
         if (!dev) {
-            error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+            error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                       "Device '%s' not found", elem);
             qbus_list_dev(bus, errp);
             return NULL;
@@ -788,7 +788,7 @@ void qmp_device_del(const char *id, Error **errp)
     }

     if (!obj) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", id);
         return;
     }
diff --git a/qmp.c b/qmp.c
index ff54e5a..2e39e04 100644
--- a/qmp.c
+++ b/qmp.c
@@ -216,7 +216,7 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp)
         if (ambiguous) {
             error_setg(errp, "Path '%s' is ambiguous", path);
         } else {
-            error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+            error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                       "Device '%s' not found", path);
         }
         return NULL;
@@ -243,7 +243,7 @@ void qmp_qom_set(const char *path, const char *property, QObject *value,

     obj = object_resolve_path(path, NULL);
     if (!obj) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", path);
         return;
     }
@@ -257,7 +257,7 @@ QObject *qmp_qom_get(const char *path, const char *property, Error **errp)

     obj = object_resolve_path(path, NULL);
     if (!obj) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", path);
         return NULL;
     }
@@ -506,7 +506,7 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,

     klass = object_class_by_name(typename);
     if (klass == NULL) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", typename);
         return NULL;
     }
diff --git a/qom/object.c b/qom/object.c
index c0decb6..425b7c3 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1018,7 +1018,7 @@ Object *object_property_get_link(Object *obj, const char *name,
     if (str && *str) {
         target = object_resolve_path(str, NULL);
         if (!target) {
-            error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+            error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                       "Device '%s' not found", str);
         }
     }
@@ -1337,7 +1337,7 @@ static Object *object_resolve_link(Object *obj, const char *name,
         if (target || ambiguous) {
             error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, target_type);
         } else {
-            error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+            error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                       "Device '%s' not found", path);
         }
         target = NULL;
diff --git a/scripts/qapi.py b/scripts/qapi.py
index b068141..d0f2308 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1439,7 +1439,7 @@ def camel_to_upper(value):
 def c_enum_const(type_name, const_name, prefix=None):
     if prefix is not None:
         type_name = prefix
-    return camel_to_upper(type_name + '_' + const_name)
+    return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()

 c_name_trans = string.maketrans('.-', '__')

diff --git a/ui/cocoa.m b/ui/cocoa.m
index c0d6bb2..e7964ae 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -729,8 +729,8 @@ QemuCocoaView *cocoaView;
                 [INPUT_BUTTON_LEFT]       = MOUSE_EVENT_LBUTTON,
                 [INPUT_BUTTON_MIDDLE]     = MOUSE_EVENT_MBUTTON,
                 [INPUT_BUTTON_RIGHT]      = MOUSE_EVENT_RBUTTON,
-                [INPUT_BUTTON_WHEEL_UP]   = MOUSE_EVENT_WHEELUP,
-                [INPUT_BUTTON_WHEEL_DOWN] = MOUSE_EVENT_WHEELDN,
+                [INPUT_BUTTON_WHEELUP]    = MOUSE_EVENT_WHEELUP,
+                [INPUT_BUTTON_WHEELDOWN]  = MOUSE_EVENT_WHEELDN,
             };
             qemu_input_update_buttons(dcl->con, bmap, last_buttons, buttons);
             last_buttons = buttons;
diff --git a/ui/gtk.c b/ui/gtk.c
index 47b37e1..40e78c5 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -965,9 +965,9 @@ static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
     InputButton btn;

     if (scroll->direction == GDK_SCROLL_UP) {
-        btn = INPUT_BUTTON_WHEEL_UP;
+        btn = INPUT_BUTTON_WHEELUP;
     } else if (scroll->direction == GDK_SCROLL_DOWN) {
-        btn = INPUT_BUTTON_WHEEL_DOWN;
+        btn = INPUT_BUTTON_WHEELDOWN;
     } else {
         return TRUE;
     }
diff --git a/ui/input-legacy.c b/ui/input-legacy.c
index e0a39f0..ec851d0 100644
--- a/ui/input-legacy.c
+++ b/ui/input-legacy.c
@@ -157,7 +157,7 @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src,
         } else {
             s->buttons &= ~bmap[evt->u.btn->button];
         }
-        if (evt->u.btn->down && evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) {
+        if (evt->u.btn->down && evt->u.btn->button == INPUT_BUTTON_WHEELUP) {
             s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque,
                                     s->axis[INPUT_AXIS_X],
                                     s->axis[INPUT_AXIS_Y],
@@ -165,7 +165,7 @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src,
                                     s->buttons);
         }
         if (evt->u.btn->down &&
-            evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+            evt->u.btn->button == INPUT_BUTTON_WHEELDOWN) {
             s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque,
                                     s->axis[INPUT_AXIS_X],
                                     s->axis[INPUT_AXIS_Y],
diff --git a/ui/input.c b/ui/input.c
index a0f9873..ebe8532 100644
--- a/ui/input.c
+++ b/ui/input.c
@@ -86,7 +86,7 @@ void qemu_input_handler_bind(QemuInputHandlerState *s,

     dev = qdev_find_recursive(sysbus_get_default(), device_id);
     if (dev == NULL) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
                   "Device '%s' not found", device_id);
         return;
     }
diff --git a/ui/sdl.c b/ui/sdl.c
index 570cb99..2678611 100644
--- a/ui/sdl.c
+++ b/ui/sdl.c
@@ -469,8 +469,8 @@ static void sdl_send_mouse_event(int dx, int dy, int x, int y, int state)
         [INPUT_BUTTON_LEFT]       = SDL_BUTTON(SDL_BUTTON_LEFT),
         [INPUT_BUTTON_MIDDLE]     = SDL_BUTTON(SDL_BUTTON_MIDDLE),
         [INPUT_BUTTON_RIGHT]      = SDL_BUTTON(SDL_BUTTON_RIGHT),
-        [INPUT_BUTTON_WHEEL_UP]   = SDL_BUTTON(SDL_BUTTON_WHEELUP),
-        [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
+        [INPUT_BUTTON_WHEELUP]    = SDL_BUTTON(SDL_BUTTON_WHEELUP),
+        [INPUT_BUTTON_WHEELDOWN]  = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
     };
     static uint32_t prev_state;

diff --git a/ui/sdl2.c b/ui/sdl2.c
index 5cb75aa..ce19e54 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -504,9 +504,9 @@ static void handle_mousewheel(SDL_Event *ev)
     InputButton btn;

     if (wev->y > 0) {
-        btn = INPUT_BUTTON_WHEEL_UP;
+        btn = INPUT_BUTTON_WHEELUP;
     } else if (wev->y < 0) {
-        btn = INPUT_BUTTON_WHEEL_DOWN;
+        btn = INPUT_BUTTON_WHEELDOWN;
     } else {
         return;
     }
diff --git a/ui/spice-input.c b/ui/spice-input.c
index c342e0d..e76c3e3 100644
--- a/ui/spice-input.c
+++ b/ui/spice-input.c
@@ -111,8 +111,8 @@ static void spice_update_buttons(QemuSpicePointer *pointer,
         [INPUT_BUTTON_LEFT]        = 0x01,
         [INPUT_BUTTON_MIDDLE]      = 0x04,
         [INPUT_BUTTON_RIGHT]       = 0x02,
-        [INPUT_BUTTON_WHEEL_UP]    = 0x10,
-        [INPUT_BUTTON_WHEEL_DOWN]  = 0x20,
+        [INPUT_BUTTON_WHEELUP]     = 0x10,
+        [INPUT_BUTTON_WHEELDOWN]   = 0x20,
     };

     if (wheel < 0) {
diff --git a/ui/vnc.c b/ui/vnc.c
index f20c08d..363a8ab 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1650,8 +1650,8 @@ static void pointer_event(VncState *vs, int button_mask, int x, int y)
         [INPUT_BUTTON_LEFT]       = 0x01,
         [INPUT_BUTTON_MIDDLE]     = 0x02,
         [INPUT_BUTTON_RIGHT]      = 0x04,
-        [INPUT_BUTTON_WHEEL_UP]   = 0x08,
-        [INPUT_BUTTON_WHEEL_DOWN] = 0x10,
+        [INPUT_BUTTON_WHEELUP]    = 0x08,
+        [INPUT_BUTTON_WHEELDOWN]  = 0x10,
     };
     QemuConsole *con = vs->vd->dcl.con;
     int width = pixman_image_get_width(vs->vd->server);
diff --git a/util/error.c b/util/error.c
index 80c89a2..2730106 100644
--- a/util/error.c
+++ b/util/error.c
@@ -85,7 +85,7 @@ void error_setg_internal(Error **errp,
     va_list ap;

     va_start(ap, fmt);
-    error_setv(errp, src, line, func, ERROR_CLASS_GENERIC_ERROR, fmt, ap);
+    error_setv(errp, src, line, func, ERROR_CLASS_GENERICERROR, fmt, ap);
     va_end(ap);
 }

@@ -102,7 +102,7 @@ void error_setg_errno_internal(Error **errp,
     }

     va_start(ap, fmt);
-    error_setv(errp, src, line, func, ERROR_CLASS_GENERIC_ERROR, fmt, ap);
+    error_setv(errp, src, line, func, ERROR_CLASS_GENERICERROR, fmt, ap);
     va_end(ap);

     if (os_errno != 0) {
@@ -158,7 +158,7 @@ void error_setg_win32_internal(Error **errp,
     }

     va_start(ap, fmt);
-    error_setv(errp, src, line, func, ERROR_CLASS_GENERIC_ERROR, fmt, ap);
+    error_setv(errp, src, line, func, ERROR_CLASS_GENERICERROR, fmt, ap);
     va_end(ap);

     if (win32_err != 0) {
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 20/28] qapi: Forbid case-insensitive clashes
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (18 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11 14:53   ` Markus Armbruster
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 21/28] qapi: Convert qtype_code into qapi enum type Eric Blake
                   ` (7 subsequent siblings)
  27 siblings, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

In general, designing user interfaces that rely on case
distinction is poor practice.  Another benefit of enforcing
a restriction against case-insensitive clashes is that we
no longer have to worry about the situation of enum values
that could be distinguished by case if mapped by c_name(),
but which cannot be distinguished when mapped to C as
ALL_CAPS by camel_to_upper().  Thus, having the generator
look for case collisions up front will prevent developers
from worrying whether different munging rules for member
names compared to enum values as a discriminator will cause
any problems in qapi unions.

There is also the possibility that we may want to add a
future extension to QMP of teaching it to be case-insensitive
(the user could request command 'Quit' instead of 'quit', or
could spell a struct field as 'CPU' instead of 'cpu').  But
for that to be a practical extension, we cannot break
backwards compatibility with any existing struct that was
already relying on case sensitivity.  Fortunately, after the
previous patch cleaned up CpuInfo, there are no such existing
qapi structs.  Of course, the idea of a future extension is
not as strong of a reason to make the change.

At any rate, it is easier to be strict now, and relax things
later if we find a reason to need case-sensitive QMP members,
than it would be to wish we had the restriction in place.

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

---
v11: rebase to latest, don't focus so hard on future case-insensitive
extensions, adjust commit message
v10: new patch
---
 docs/qapi-code-gen.txt                 | 3 +++
 scripts/qapi.py                        | 4 ++--
 tests/Makefile                         | 1 +
 tests/qapi-schema/args-case-clash.err  | 1 +
 tests/qapi-schema/args-case-clash.exit | 1 +
 tests/qapi-schema/args-case-clash.json | 4 ++++
 tests/qapi-schema/args-case-clash.out  | 0
 7 files changed, 12 insertions(+), 2 deletions(-)
 create mode 100644 tests/qapi-schema/args-case-clash.err
 create mode 100644 tests/qapi-schema/args-case-clash.exit
 create mode 100644 tests/qapi-schema/args-case-clash.json
 create mode 100644 tests/qapi-schema/args-case-clash.out

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index f9fa6f3..54a6a7b 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -102,6 +102,9 @@ single-dimension array of that type; multi-dimension arrays are not
 directly supported (although an array of a complex struct that
 contains an array member is possible).

+Client JSON Protocol is case-sensitive.  However, the generator
+rejects attempts to create entities that differ only in case.
+
 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
diff --git a/scripts/qapi.py b/scripts/qapi.py
index d0f2308..ed7a32b 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1040,7 +1040,7 @@ class QAPISchemaObjectTypeMember(object):
         assert self.type

     def check_clash(self, info, seen):
-        cname = c_name(self.name)
+        cname = c_name(self.name).upper()
         if cname in seen:
             raise QAPIExprError(info,
                                 "%s collides with %s"
@@ -1085,7 +1085,7 @@ class QAPISchemaObjectTypeVariants(object):

     def check(self, schema, seen):
         if not self.tag_member:    # flat union
-            self.tag_member = seen[c_name(self.tag_name)]
+            self.tag_member = seen[c_name(self.tag_name).upper()]
             assert self.tag_name == self.tag_member.name
         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
         for v in self.variants:
diff --git a/tests/Makefile b/tests/Makefile
index c84c6cb..d1c6817 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -239,6 +239,7 @@ qapi-schema += args-alternate.json
 qapi-schema += args-any.json
 qapi-schema += args-array-empty.json
 qapi-schema += args-array-unknown.json
+qapi-schema += args-case-clash.json
 qapi-schema += args-int.json
 qapi-schema += args-invalid.json
 qapi-schema += args-member-array-bad.json
diff --git a/tests/qapi-schema/args-case-clash.err b/tests/qapi-schema/args-case-clash.err
new file mode 100644
index 0000000..0fafe75
--- /dev/null
+++ b/tests/qapi-schema/args-case-clash.err
@@ -0,0 +1 @@
+tests/qapi-schema/args-case-clash.json:4: 'A' (parameter of oops) collides with 'a' (parameter of oops)
diff --git a/tests/qapi-schema/args-case-clash.exit b/tests/qapi-schema/args-case-clash.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/args-case-clash.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/args-case-clash.json b/tests/qapi-schema/args-case-clash.json
new file mode 100644
index 0000000..e6f0625
--- /dev/null
+++ b/tests/qapi-schema/args-case-clash.json
@@ -0,0 +1,4 @@
+# C member name collision
+# Reject members that clash case-insensitively, even though our mapping to
+# C names preserves case.
+{ 'command': 'oops', 'data': { 'a': 'str', 'A': 'int' } }
diff --git a/tests/qapi-schema/args-case-clash.out b/tests/qapi-schema/args-case-clash.out
new file mode 100644
index 0000000..e69de29
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 21/28] qapi: Convert qtype_code into qapi enum type
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (19 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 20/28] qapi: Forbid case-insensitive clashes Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11 16:42   ` Markus Armbruster
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 22/28] qapi: Simplify visiting of alternate types Eric Blake
                   ` (6 subsequent siblings)
  27 siblings, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Luiz Capitulino, armbru, open list:Block layer core,
	Michael Roth

What's more meta than using qapi to define qapi? :)

Convert qtype_code into a full-fledged[*] builtin qapi enum type,
so that a subsequent patch can then use it as the discriminator
type of qapi alternate types.  Doing so is easiest when renaming
it to qapi conventions, as QTypeCode.  Fortunately, there are not
many places in the tree that were actually spelling the type name
out, and the judicious use of 'prefix' in the qapi defintion
avoids churn to the spelling of the enum constants.

To avoid circular definitions, we have to flip the order of
inclusion between "qobject.h" vs. "qapi-types.h".  Back in commit
28770e0, we had the latter include the former, so that we could
use 'QObject *' for our implementation of 'any'.  But that usage
also works with only a forward declaration, whereas the
definition of QType requires QTypeCode to be a complete type.

[*] The type has to be builtin, rather than declared in
qapi/common.json, because we want to use it for alternates even
when common.json is not included. But since it is the first
builtin enum type, we have to add special cases to qapi-types
and qapi-visit to only emit definitions once, even when two
qapi files are being compiled into the same binary (the way we
already handled builtin list types like 'intList').  We may
need to revisit how multiple qapi files share common types,
but that's a project for another day.

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

---
v11: new patch
---
 block/qapi.c                             |  4 ++--
 docs/qapi-code-gen.txt                   |  1 +
 include/hw/qdev-core.h                   |  2 +-
 include/qapi/qmp/qobject.h               | 19 +++----------------
 qobject/qdict.c                          |  2 +-
 scripts/qapi-types.py                    | 13 ++++++++++---
 scripts/qapi-visit.py                    | 10 ++++++++--
 scripts/qapi.py                          |  7 ++++++-
 tests/qapi-schema/alternate-empty.out    |  2 ++
 tests/qapi-schema/comments.out           |  2 ++
 tests/qapi-schema/empty.out              |  2 ++
 tests/qapi-schema/event-case.out         |  2 ++
 tests/qapi-schema/flat-union-empty.out   |  2 ++
 tests/qapi-schema/ident-with-escape.out  |  2 ++
 tests/qapi-schema/include-relpath.out    |  2 ++
 tests/qapi-schema/include-repetition.out |  2 ++
 tests/qapi-schema/include-simple.out     |  2 ++
 tests/qapi-schema/indented-expr.out      |  2 ++
 tests/qapi-schema/qapi-schema-test.out   |  2 ++
 tests/qapi-schema/union-clash-data.out   |  2 ++
 tests/qapi-schema/union-empty.out        |  2 ++
 21 files changed, 58 insertions(+), 26 deletions(-)

diff --git a/block/qapi.c b/block/qapi.c
index ec0f513..4211f11 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -539,7 +539,7 @@ static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation,
     int i = 0;

     for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) {
-        qtype_code type = qobject_type(entry->value);
+        QTypeCode type = qobject_type(entry->value);
         bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
         const char *format = composite ? "%*s[%i]:\n" : "%*s[%i]: ";

@@ -557,7 +557,7 @@ static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
     const QDictEntry *entry;

     for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) {
-        qtype_code type = qobject_type(entry->value);
+        QTypeCode type = qobject_type(entry->value);
         bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
         const char *format = composite ? "%*s%s:\n" : "%*s%s: ";
         char key[strlen(entry->key) + 1];
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 54a6a7b..35301c5 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -163,6 +163,7 @@ The following types are predefined, and map to C as follows:
                        accepts size suffixes
   bool      bool       JSON true or false
   any       QObject *  any JSON value
+  QTypeCode QTypeCode  JSON string of enum QTypeCode values


 === Includes ===
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 8057aed..8e7df8e 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -239,7 +239,7 @@ struct Property {
     PropertyInfo *info;
     int          offset;
     uint8_t      bitnr;
-    qtype_code   qtype;
+    QTypeCode    qtype;
     int64_t      defval;
     int          arrayoffset;
     PropertyInfo *arrayinfo;
diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h
index 4b96ed5..8d6322b 100644
--- a/include/qapi/qmp/qobject.h
+++ b/include/qapi/qmp/qobject.h
@@ -34,23 +34,10 @@

 #include <stddef.h>
 #include <assert.h>
-
-typedef enum {
-    QTYPE_NONE,    /* sentinel value, no QObject has this type code */
-    QTYPE_QNULL,
-    QTYPE_QINT,
-    QTYPE_QSTRING,
-    QTYPE_QDICT,
-    QTYPE_QLIST,
-    QTYPE_QFLOAT,
-    QTYPE_QBOOL,
-    QTYPE_MAX,
-} qtype_code;
-
-struct QObject;
+#include "qapi-types.h"

 typedef struct QType {
-    qtype_code code;
+    QTypeCode code;
     void (*destroy)(struct QObject *);
 } QType;

@@ -101,7 +88,7 @@ static inline void qobject_decref(QObject *obj)
 /**
  * qobject_type(): Return the QObject's type
  */
-static inline qtype_code qobject_type(const QObject *obj)
+static inline QTypeCode qobject_type(const QObject *obj)
 {
     assert(obj->type != NULL);
     return obj->type->code;
diff --git a/qobject/qdict.c b/qobject/qdict.c
index 2d67bf1..92915f4 100644
--- a/qobject/qdict.c
+++ b/qobject/qdict.c
@@ -185,7 +185,7 @@ size_t qdict_size(const QDict *qdict)
  * qdict_get_obj(): Get a QObject of a specific type
  */
 static QObject *qdict_get_obj(const QDict *qdict, const char *key,
-                              qtype_code type)
+                              QTypeCode type)
 {
     QObject *obj;

diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 2f2f7df..93e905a 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -233,8 +233,14 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
         self.defn += gen_type_cleanup(name)

     def visit_enum_type(self, name, info, values, prefix):
-        self._fwdecl += gen_enum(name, values, prefix)
-        self._fwdefn += gen_enum_lookup(name, values, prefix)
+        # Special case for our lone builtin enum type
+        if name == 'QTypeCode':
+            self._btin += gen_enum(name, values, prefix)
+            if do_builtins:
+                self.defn += gen_enum_lookup(name, values, prefix)
+        else:
+            self._fwdecl += gen_enum(name, values, prefix)
+            self._fwdefn += gen_enum_lookup(name, values, prefix)

     def visit_array_type(self, name, info, element_type):
         if isinstance(element_type, QAPISchemaBuiltinType):
@@ -319,7 +325,8 @@ fdef.write(mcgen('''
 fdecl.write(mcgen('''
 #include <stdbool.h>
 #include <stdint.h>
-#include "qapi/qmp/qobject.h"
+
+typedef struct QObject QObject;
 '''))

 schema = QAPISchema(input_file)
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 94cd113..6f0b4e1 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -347,8 +347,14 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
                     isinstance(entity, QAPISchemaObjectType))

     def visit_enum_type(self, name, info, values, prefix):
-        self.decl += gen_visit_decl(name, scalar=True)
-        self.defn += gen_visit_enum(name)
+        # Special case for our lone builtin enum type
+        if name == 'QTypeCode':
+            self._btin += gen_visit_decl(name, scalar=True)
+            if do_builtins:
+                self.defn += gen_visit_enum(name)
+        else:
+            self.decl += gen_visit_decl(name, scalar=True)
+            self.defn += gen_visit_enum(name)

     def visit_array_type(self, name, info, element_type):
         decl = gen_visit_decl(name)
diff --git a/scripts/qapi.py b/scripts/qapi.py
index ed7a32b..d4ef08e 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -33,7 +33,7 @@ builtin_types = {
     'uint32':   'QTYPE_QINT',
     'uint64':   'QTYPE_QINT',
     'size':     'QTYPE_QINT',
-    'any':      None,           # any qtype_code possible, actually
+    'any':      None,           # any QTypeCode possible, actually
 }

 # Whitelist of commands allowed to return a non-dictionary
@@ -1243,6 +1243,11 @@ class QAPISchema(object):
         self.the_empty_object_type = QAPISchemaObjectType(':empty', None, None,
                                                           [], None)
         self._def_entity(self.the_empty_object_type)
+        self._def_entity(QAPISchemaEnumType('QTypeCode', None,
+                                            ['none', 'qnull', 'qint',
+                                             'qstring', 'qdict', 'qlist',
+                                             'qfloat', 'qbool'],
+                                            'QTYPE'))

     def _make_implicit_enum_type(self, name, info, values):
         name = name + 'Kind'   # Use namespace reserved by add_name()
diff --git a/tests/qapi-schema/alternate-empty.out b/tests/qapi-schema/alternate-empty.out
index 0f153b6..ab5a629 100644
--- a/tests/qapi-schema/alternate-empty.out
+++ b/tests/qapi-schema/alternate-empty.out
@@ -2,3 +2,5 @@ object :empty
 alternate Alt
     case i: int
 enum AltKind ['i']
+enum QTypeCode ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
+    prefix QTYPE
diff --git a/tests/qapi-schema/comments.out b/tests/qapi-schema/comments.out
index 9e2c656..59904ef 100644
--- a/tests/qapi-schema/comments.out
+++ b/tests/qapi-schema/comments.out
@@ -1,2 +1,4 @@
 object :empty
+enum QTypeCode ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
+    prefix QTYPE
 enum Status ['good', 'bad', 'ugly']
diff --git a/tests/qapi-schema/empty.out b/tests/qapi-schema/empty.out
index 272b161..42121b5 100644
--- a/tests/qapi-schema/empty.out
+++ b/tests/qapi-schema/empty.out
@@ -1 +1,3 @@
 object :empty
+enum QTypeCode ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
+    prefix QTYPE
diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out
index cdfd264..5e4d10e 100644
--- a/tests/qapi-schema/event-case.out
+++ b/tests/qapi-schema/event-case.out
@@ -1,2 +1,4 @@
 object :empty
+enum QTypeCode ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
+    prefix QTYPE
 event oops None
diff --git a/tests/qapi-schema/flat-union-empty.out b/tests/qapi-schema/flat-union-empty.out
index 0e0665a..2fd07b9 100644
--- a/tests/qapi-schema/flat-union-empty.out
+++ b/tests/qapi-schema/flat-union-empty.out
@@ -2,6 +2,8 @@ object :empty
 object Base
     member type: Empty optional=False
 enum Empty []
+enum QTypeCode ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
+    prefix QTYPE
 object Union
     base Base
     tag type
diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out
index f4542b1..523eec9 100644
--- a/tests/qapi-schema/ident-with-escape.out
+++ b/tests/qapi-schema/ident-with-escape.out
@@ -1,5 +1,7 @@
 object :empty
 object :obj-fooA-arg
     member bar1: str optional=False
+enum QTypeCode ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
+    prefix QTYPE
 command fooA :obj-fooA-arg -> None
    gen=True success_response=True
diff --git a/tests/qapi-schema/include-relpath.out b/tests/qapi-schema/include-relpath.out
index 9e2c656..59904ef 100644
--- a/tests/qapi-schema/include-relpath.out
+++ b/tests/qapi-schema/include-relpath.out
@@ -1,2 +1,4 @@
 object :empty
+enum QTypeCode ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
+    prefix QTYPE
 enum Status ['good', 'bad', 'ugly']
diff --git a/tests/qapi-schema/include-repetition.out b/tests/qapi-schema/include-repetition.out
index 9e2c656..59904ef 100644
--- a/tests/qapi-schema/include-repetition.out
+++ b/tests/qapi-schema/include-repetition.out
@@ -1,2 +1,4 @@
 object :empty
+enum QTypeCode ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
+    prefix QTYPE
 enum Status ['good', 'bad', 'ugly']
diff --git a/tests/qapi-schema/include-simple.out b/tests/qapi-schema/include-simple.out
index 9e2c656..59904ef 100644
--- a/tests/qapi-schema/include-simple.out
+++ b/tests/qapi-schema/include-simple.out
@@ -1,2 +1,4 @@
 object :empty
+enum QTypeCode ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
+    prefix QTYPE
 enum Status ['good', 'bad', 'ugly']
diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out
index 226d300..b7e49fc 100644
--- a/tests/qapi-schema/indented-expr.out
+++ b/tests/qapi-schema/indented-expr.out
@@ -1,4 +1,6 @@
 object :empty
+enum QTypeCode ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
+    prefix QTYPE
 command eins None -> None
    gen=True success_response=True
 command zwei None -> None
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 786024e..d05fc37 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -101,6 +101,8 @@ object NestedEnumsOne
     member enum4: EnumOne optional=True
 enum QEnumTwo ['value1', 'value2']
     prefix QENUM_TWO
+enum QTypeCode ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
+    prefix QTYPE
 object TestStruct
     member integer: int optional=False
     member boolean: bool optional=False
diff --git a/tests/qapi-schema/union-clash-data.out b/tests/qapi-schema/union-clash-data.out
index cea8551..e4a2932 100644
--- a/tests/qapi-schema/union-clash-data.out
+++ b/tests/qapi-schema/union-clash-data.out
@@ -1,6 +1,8 @@
 object :empty
 object :obj-int-wrapper
     member data: int optional=False
+enum QTypeCode ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
+    prefix QTYPE
 object TestUnion
     member type: TestUnionKind optional=False
     case data: :obj-int-wrapper
diff --git a/tests/qapi-schema/union-empty.out b/tests/qapi-schema/union-empty.out
index 9c89fd1..5e7764c 100644
--- a/tests/qapi-schema/union-empty.out
+++ b/tests/qapi-schema/union-empty.out
@@ -1,4 +1,6 @@
 object :empty
+enum QTypeCode ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
+    prefix QTYPE
 object Union
     member type: UnionKind optional=False
 enum UnionKind []
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 22/28] qapi: Simplify visiting of alternate types
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (20 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 21/28] qapi: Convert qtype_code into qapi enum type Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-12 14:21   ` Markus Armbruster
                     ` (2 more replies)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 23/28] qapi: Fix alternates that accept 'number' but not 'int' Eric Blake
                   ` (5 subsequent siblings)
  27 siblings, 3 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Previously, working with alternates required two lookup arrays
and some indirection: for type Foo, we created Foo_qtypes[]
which maps each qtype to a value of the generated FooKind enum,
then look up that value in FooKind_lookup[] like we do for other
union types.

This has a couple of subtle bugs.  First, the generator was
creating a call with a parameter '(int *) &(*obj)->type' where
type is an enum type; this is unsafe if the compiler chooses
to store the enum type in a different size than int, where
assigning through the wrong size pointer can corrupt data or
cause a SIGBUS.

Second, since the values of the FooKind enum start at zero, all
entries of the Foo_qtypes[] array that were not explicitly
initialized will map to the same branch of the union as the
first member of the alternate, rather than triggering a desired
failure in visit_get_next_type().  Fortunately, the bug seldom
bites; the very next thing the input visitor does is try to
parse the incoming JSON with the wrong parser, which normally
fails; the output visitor is not used with a C struct in that
state, and the dealloc visitor has nothing to clean up (so
there is no leak).

However, the second bug IS observable in one case: parsing an
integer causes unusual behavior in an alternate that contains
at least a 'number' member but no 'int' member, because the
'number' parser accepts QTYPE_QINT in addition to the expected
QTYPE_QFLOAT (that is, since 'int' is not a member, the type
QTYPE_QINT accidentally maps to FooKind 0; if this enum value
is the 'number' branch the integer parses successfully, but if
the 'number' branch is not first, some other branch tries to
parse the integer and rejects it).  A later patch will worry
about fixing alternates to always parse all inputs that a
non-alternate 'number' would accept, for now this is still
marked FIXME in the updated test-qmp-input-visitor.c, to
merely point out that new undesired behavior of 'ans' matches
the existing undesired behavior of 'asn'.

This patch fixes the default-initialization bug by deleting the
indirection, and modifying get_next_type() to directly assign a
QTypeCode parameter.  This in turn fixes the type-casting bug,
as we are no longer casting a pointer to enum to a questionable
size. There is no longer a need to generate an implicit FooKind
enum associated with the alternate type (since the QMP wire
format never uses the stringized counterparts of the C union
member names); that also means we no longer have a collision
with an alternate branch named 'max'.  Since the updated
visit_get_next_type() does not know which qtypes are expected,
the generated visitor is modified to generate an error statement
if an unexpected type is encountered.

Callers now have to know the QTYPE_* mapping when looking at the
discriminator; but so far, only the testsuite was even using the
C struct of an alternate types.  I considered the possibility of
keeping the internal enum FooKind, but initialized differently
than most generated arrays, as in:
  typedef enum FooKind {
      FOO_KIND_A = QTYPE_QDICT,
      FOO_KIND_B = QTYPE_QINT,
  } FooKind;
to create nicer aliases for knowing when to use foo->a or foo->b
when inspecting foo->type; but it turned out to add too much
complexity, especially without a client.

There is a user-visible side effect to this change, but I
consider it to be an improvement. Previously,
the invalid QMP command:
  {"execute":"blockdev-add", "arguments":{"options":
    {"driver":"raw", "id":"a", "file":true}}}
failed with:
  {"error": {"class": "GenericError",
    "desc": "Invalid parameter type for 'file', expected: QDict"}}
(visit_get_next_type() succeeded, and the error comes from the
visit_type_BlockdevOptions() expecting {}; there is no mention of
the fact that a string would also work).  Now it fails with:
  {"error": {"class": "GenericError",
    "desc": "Invalid parameter type for 'file', expected: BlockdevRef"}}
(the error when the next type doesn't match any expected types for
the overall alternate).

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

---
v11 (no v10): rebase to new QTypeCode, with fewer special cases; tweak
commit message to match
v9: rebase to earlier changes, rework commit message to mention second
bug fix; move positive test in qapi-schema-test to later patch
v8: no change
v7: rebase onto earlier changes, rework how subtype makes things work
v6: rebase onto tag_member subclass, testsuite, gen_err_check(),
and info improvements
---
 docs/qapi-code-gen.txt                 |  3 ---
 include/qapi/visitor-impl.h            |  3 ++-
 include/qapi/visitor.h                 |  8 +++++++-
 qapi/qapi-visit-core.c                 |  4 ++--
 qapi/qmp-input-visitor.c               |  4 ++--
 scripts/qapi-types.py                  | 34 ----------------------------------
 scripts/qapi-visit.py                  | 14 +++++++++-----
 scripts/qapi.py                        | 20 ++++++++++++++------
 tests/qapi-schema/alternate-empty.out  |  1 -
 tests/qapi-schema/qapi-schema-test.out |  8 --------
 tests/test-qmp-input-visitor.c         | 31 ++++++++++++++++---------------
 tests/test-qmp-output-visitor.c        |  4 ++--
 12 files changed, 54 insertions(+), 80 deletions(-)

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 35301c5..4a4dfcf 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -387,9 +387,6 @@ where each branch of the union names a QAPI type.  For example:
    'data': { 'definition': 'BlockdevOptions',
              'reference': 'str' } }

-Just like for a simple union, an implicit C enum 'NameKind' is created
-to enumerate the branches for the alternate 'Name'.
-
 Unlike a union, the discriminator string is never passed on the wire
 for the Client JSON Protocol.  Instead, the value's JSON type serves
 as an implicit discriminator, which in turn means that an alternate
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 8c0ba57..e36da60 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -32,7 +32,8 @@ struct Visitor

     void (*type_enum)(Visitor *v, int *obj, const char * const strings[],
                       const char *kind, const char *name, Error **errp);
-    void (*get_next_type)(Visitor *v, int *kind, const int *qobjects,
+    /* May be NULL; only needed for input visitors. */
+    void (*get_next_type)(Visitor *v, QTypeCode *type,
                           const char *name, Error **errp);

     void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp);
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index cfc19a6..4fa6d50 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -41,7 +41,13 @@ GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
 void visit_end_list(Visitor *v, Error **errp);
 void visit_optional(Visitor *v, bool *present, const char *name,
                     Error **errp);
-void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
+
+/**
+ * Determine the qtype of the item @name in the current object visit.
+ * For input visitors, set *@type to the correct qtype of a qapi
+ * alternate type; for other visitors, leave *@type unchanged.
+ */
+void visit_get_next_type(Visitor *v, QTypeCode *type,
                          const char *name, Error **errp);
 void visit_type_enum(Visitor *v, int *obj, const char * const strings[],
                      const char *kind, const char *name, Error **errp);
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 59ed506..ddb3a15 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -81,11 +81,11 @@ void visit_optional(Visitor *v, bool *present, const char *name,
     }
 }

-void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
+void visit_get_next_type(Visitor *v, QTypeCode *type,
                          const char *name, Error **errp)
 {
     if (v->get_next_type) {
-        v->get_next_type(v, obj, qtypes, name, errp);
+        v->get_next_type(v, type, name, errp);
     }
 }

diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index eb6e110..2141ff5 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -208,7 +208,7 @@ static void qmp_input_end_list(Visitor *v, Error **errp)
     qmp_input_pop(qiv, errp);
 }

-static void qmp_input_get_next_type(Visitor *v, int *kind, const int *qobjects,
+static void qmp_input_get_next_type(Visitor *v, QTypeCode *type,
                                     const char *name, Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
@@ -218,7 +218,7 @@ static void qmp_input_get_next_type(Visitor *v, int *kind, const int *qobjects,
         error_setg(errp, QERR_MISSING_PARAMETER, name ? name : "null");
         return;
     }
-    *kind = qobjects[qobject_type(qobj)];
+    *type = qobject_type(qobj);
 }

 static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name,
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 93e905a..29ba83f 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -101,38 +101,6 @@ static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj)
                  c_name=c_name(name), base=base.c_name())


-def gen_alternate_qtypes_decl(name):
-    return mcgen('''
-
-extern const int %(c_name)s_qtypes[];
-''',
-                 c_name=c_name(name))
-
-
-def gen_alternate_qtypes(name, variants):
-    ret = mcgen('''
-
-const int %(c_name)s_qtypes[QTYPE_MAX] = {
-''',
-                c_name=c_name(name))
-
-    for var in variants.variants:
-        qtype = var.type.alternate_qtype()
-        assert qtype
-
-        ret += mcgen('''
-    [%(qtype)s] = %(enum_const)s,
-''',
-                     qtype=qtype,
-                     enum_const=c_enum_const(variants.tag_member.type.name,
-                                             var.name))
-
-    ret += mcgen('''
-};
-''')
-    return ret
-
-
 def gen_variants(variants):
     # FIXME: What purpose does data serve, besides preventing a union that
     # has a branch named 'data'? We use it in qapi-visit.py to decide
@@ -263,9 +231,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):

     def visit_alternate_type(self, name, info, variants):
         self._fwdecl += gen_fwd_object_or_array(name)
-        self._fwdefn += gen_alternate_qtypes(name, variants)
         self.decl += gen_object(name, None, [variants.tag_member], variants)
-        self.decl += gen_alternate_qtypes_decl(name)
         self._gen_type_cleanup(name)

 # If you link code generated from multiple schemata, you want only one
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 6f0b4e1..ad98685 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -193,7 +193,7 @@ 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)->type, %(c_name)s_qtypes, name, &err);
+    visit_get_next_type(v, &(*obj)->type, name, &err);
     if (err) {
         goto out_obj;
     }
@@ -201,20 +201,22 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
 ''',
                 c_name=c_name(name))

+    # FIXME: When 'number' but not 'int' is present in the alternate, we
+    # should allow QTYPE_INT to promote to QTYPE_FLOAT.
     for var in variants.variants:
         ret += mcgen('''
     case %(case)s:
         visit_type_%(c_type)s(v, &(*obj)->u.%(c_name)s, name, &err);
         break;
 ''',
-                     case=c_enum_const(variants.tag_member.type.name,
-                                       var.name),
+                     case=var.type.alternate_qtype(),
                      c_type=var.type.c_name(),
                      c_name=c_name(var.name))

     ret += mcgen('''
     default:
-        abort();
+        error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+                   "%(name)s");
     }
 out_obj:
     error_propagate(errp, err);
@@ -223,7 +225,8 @@ out_obj:
 out:
     error_propagate(errp, err);
 }
-''')
+''',
+                 name=name)

     return ret

@@ -436,6 +439,7 @@ fdef.write(mcgen('''

 fdecl.write(mcgen('''
 #include "qapi/visitor.h"
+#include "qapi/qmp/qerror.h"
 #include "%(prefix)sqapi-types.h"

 ''',
diff --git a/scripts/qapi.py b/scripts/qapi.py
index d4ef08e..1790e8f 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -627,15 +627,15 @@ def check_union(expr, expr_info):
 def check_alternate(expr, expr_info):
     name = expr['alternate']
     members = expr['data']
-    values = {'MAX': '(automatic)'}
+    values = {}
     types_seen = {}

     # Check every branch
     for (key, value) in members.items():
         check_name(expr_info, "Member of alternate '%s'" % name, key)

-        # Check for conflicts in the generated enum
-        c_key = camel_to_upper(key)
+        # Check for conflicts in the branch names
+        c_key = c_name(key)
         if c_key in values:
             raise QAPIExprError(expr_info,
                                 "Alternate '%s' member '%s' clashes with '%s'"
@@ -1090,8 +1090,11 @@ class QAPISchemaObjectTypeVariants(object):
         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
         for v in self.variants:
             v.check(schema)
-            assert v.name in self.tag_member.type.values
-            if isinstance(v.type, QAPISchemaObjectType):
+            # Union names must match enum values; alternate names are
+            # checked separately. Use 'seen' to tell the two apart.
+            if seen:
+                assert v.name in self.tag_member.type.values
+                assert isinstance(v.type, QAPISchemaObjectType)
                 v.type.check(schema)

     def check_clash(self, schema, info, seen):
@@ -1133,6 +1136,11 @@ class QAPISchemaAlternateType(QAPISchemaType):
         # Not calling self.variants.check_clash(), because there's nothing
         # to clash with
         self.variants.check(schema, {})
+        # Alternate branch names have no relation to the tag enum values;
+        # so we have to check for potential name collisions ourselves.
+        cases = {}
+        for var in self.variants.variants:
+            var.check_clash(self.info, cases)

     def json_type(self):
         return 'value'
@@ -1340,7 +1348,7 @@ class QAPISchema(object):
         data = expr['data']
         variants = [self._make_variant(key, value)
                     for (key, value) in data.iteritems()]
-        tag_member = self._make_implicit_tag(name, info, variants)
+        tag_member = QAPISchemaObjectTypeMember('type', 'QTypeCode', False)
         self._def_entity(
             QAPISchemaAlternateType(name, info,
                                     QAPISchemaObjectTypeVariants(None,
diff --git a/tests/qapi-schema/alternate-empty.out b/tests/qapi-schema/alternate-empty.out
index ab5a629..fab3aad 100644
--- a/tests/qapi-schema/alternate-empty.out
+++ b/tests/qapi-schema/alternate-empty.out
@@ -1,6 +1,5 @@
 object :empty
 alternate Alt
     case i: int
-enum AltKind ['i']
 enum QTypeCode ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
     prefix QTYPE
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index d05fc37..6f2edcc 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -56,27 +56,21 @@ object :obj-user_def_cmd2-arg
 alternate AltIntNum
     case i: int
     case n: number
-enum AltIntNumKind ['i', 'n']
 alternate AltNumInt
     case n: number
     case i: int
-enum AltNumIntKind ['n', 'i']
 alternate AltNumStr
     case n: number
     case s: str
-enum AltNumStrKind ['n', 's']
 alternate AltStrBool
     case s: str
     case b: bool
-enum AltStrBoolKind ['s', 'b']
 alternate AltStrInt
     case s: str
     case i: int
-enum AltStrIntKind ['s', 'i']
 alternate AltStrNum
     case s: str
     case n: number
-enum AltStrNumKind ['s', 'n']
 event EVENT_A None
 event EVENT_B None
 event EVENT_C :obj-EVENT_C-arg
@@ -114,7 +108,6 @@ alternate UserDefAlternate
     case uda: UserDefA
     case s: str
     case i: int
-enum UserDefAlternateKind ['uda', 's', 'i']
 object UserDefB
     member intb: int optional=False
     member a-b: bool optional=True
@@ -180,7 +173,6 @@ event __ORG.QEMU_X-EVENT __org.qemu_x-Struct
 alternate __org.qemu_x-Alt
     case __org.qemu_x-branch: str
     case b: __org.qemu_x-Base
-enum __org.qemu_x-AltKind ['__org.qemu_x-branch', 'b']
 object __org.qemu_x-Base
     member __org.qemu_x-member1: __org.qemu_x-Enum optional=False
 enum __org.qemu_x-Enum ['__org.qemu_x-value']
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index d48ebdd..43b9e18 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -312,13 +312,13 @@ 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->type, ==, USER_DEF_ALTERNATE_KIND_I);
+    g_assert_cmpint(tmp->type, ==, QTYPE_QINT);
     g_assert_cmpint(tmp->u.i, ==, 42);
     qapi_free_UserDefAlternate(tmp);

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

@@ -347,36 +347,37 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data,
     error_free_or_abort(&err);
     qapi_free_AltStrBool(asb);

-    /* FIXME: Order of alternate should not affect semantics; asn should
-     * parse the same as ans */
+    /* FIXME: integer should parse as number */
     v = visitor_input_test_init(data, "42");
     visit_type_AltStrNum(v, &asn, NULL, &err);
-    /* FIXME g_assert_cmpint(asn->type, == ALT_STR_NUM_KIND_N); */
+    /* FIXME g_assert_cmpint(asn->type, ==, QTYPE_QFLOAT); */
     /* FIXME g_assert_cmpfloat(asn->u.n, ==, 42); */
     error_free_or_abort(&err);
     qapi_free_AltStrNum(asn);

+    /* FIXME: integer should parse as number */
     v = visitor_input_test_init(data, "42");
-    visit_type_AltNumStr(v, &ans, NULL, &error_abort);
-    g_assert_cmpint(ans->type, ==, ALT_NUM_STR_KIND_N);
-    g_assert_cmpfloat(ans->u.n, ==, 42);
+    visit_type_AltNumStr(v, &ans, NULL, &err);
+    /* FIXME g_assert_cmpint(ans->type, ==, QTYPE_QFLOAT); */
+    /* FIXME g_assert_cmpfloat(ans->u.n, ==, 42); */
+    error_free_or_abort(&err);
     qapi_free_AltNumStr(ans);

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

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

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

@@ -389,13 +390,13 @@ 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->type, ==, ALT_STR_NUM_KIND_N);
+    g_assert_cmpint(asn->type, ==, QTYPE_QFLOAT);
     g_assert_cmpfloat(asn->u.n, ==, 42.5);
     qapi_free_AltStrNum(asn);

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

@@ -406,13 +407,13 @@ 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->type, ==, ALT_INT_NUM_KIND_N);
+    g_assert_cmpint(ain->type, ==, QTYPE_QFLOAT);
     g_assert_cmpfloat(ain->u.n, ==, 42.5);
     qapi_free_AltIntNum(ain);

     v = visitor_input_test_init(data, "42.5");
     visit_type_AltNumInt(v, &ani, NULL, &error_abort);
-    g_assert_cmpint(ani->type, ==, ALT_NUM_INT_KIND_N);
+    g_assert_cmpint(ani->type, ==, QTYPE_QFLOAT);
     g_assert_cmpfloat(ani->u.n, ==, 42.5);
     qapi_free_AltNumInt(ani);
 }
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 0d0c859..b962900 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -428,7 +428,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     UserDefAlternate *tmp;

     tmp = g_new0(UserDefAlternate, 1);
-    tmp->type = USER_DEF_ALTERNATE_KIND_I;
+    tmp->type = QTYPE_QINT;
     tmp->u.i = 42;

     visit_type_UserDefAlternate(data->ov, &tmp, NULL, &error_abort);
@@ -441,7 +441,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     qobject_decref(arg);

     tmp = g_new0(UserDefAlternate, 1);
-    tmp->type = USER_DEF_ALTERNATE_KIND_S;
+    tmp->type = QTYPE_QSTRING;
     tmp->u.s = g_strdup("hello");

     visit_type_UserDefAlternate(data->ov, &tmp, NULL, &error_abort);
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 23/28] qapi: Fix alternates that accept 'number' but not 'int'
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (21 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 22/28] qapi: Simplify visiting of alternate types Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-12 15:01   ` Markus Armbruster
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 24/28] qapi: Add positive tests to qapi-schema-test Eric Blake
                   ` (4 subsequent siblings)
  27 siblings, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

The QMP input visitor allows integral values to be assigned by
promotion to a QTYPE_QFLOAT.  However, when parsing an alternate,
we did not take this into account, such that an alternate that
accepts 'number' and some other type, but not 'int', would reject
integral values.

With this patch, we now have the following desirable table:

    alternate has      case selected for
    'int'  'number'    QTYPE_QINT  QTYPE_QFLOAT
      no        no     error       error
      no       yes     'number'    'number'
     yes        no     'int'       error
     yes       yes     'int'       'number'

While it is unlikely that we will ever use 'number' in an
alternate other than in the testsuite, it never hurts to be
more precise in what we allow.

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

---
v11 (no v10): slight commit message tweak, rebase to earlier changes
v9: rebase to earlier changes
v8: no change
v7: rebase to named .u union
v6: rebase onto earlier testsuite and gen_err_check() improvements
---
 include/qapi/visitor-impl.h    |  2 +-
 include/qapi/visitor.h         |  3 ++-
 qapi/qapi-visit-core.c         |  4 ++--
 qapi/qmp-input-visitor.c       |  4 ++++
 scripts/qapi-visit.py          | 11 +++++++----
 tests/test-qmp-input-visitor.c | 16 ++++++----------
 6 files changed, 22 insertions(+), 18 deletions(-)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index e36da60..ac6c17e 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -33,7 +33,7 @@ struct Visitor
     void (*type_enum)(Visitor *v, int *obj, const char * const strings[],
                       const char *kind, const char *name, Error **errp);
     /* May be NULL; only needed for input visitors. */
-    void (*get_next_type)(Visitor *v, QTypeCode *type,
+    void (*get_next_type)(Visitor *v, QTypeCode *type, bool promote_int,
                           const char *name, Error **errp);

     void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp);
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 4fa6d50..250e8e1 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -46,8 +46,9 @@ void visit_optional(Visitor *v, bool *present, const char *name,
  * Determine the qtype of the item @name in the current object visit.
  * For input visitors, set *@type to the correct qtype of a qapi
  * alternate type; for other visitors, leave *@type unchanged.
+ * If @promote_int, treat integers as QTYPE_FLOAT.
  */
-void visit_get_next_type(Visitor *v, QTypeCode *type,
+void visit_get_next_type(Visitor *v, QTypeCode *type, bool promote_int,
                          const char *name, Error **errp);
 void visit_type_enum(Visitor *v, int *obj, const char * const strings[],
                      const char *kind, const char *name, Error **errp);
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index ddb3a15..52c132a 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -81,11 +81,11 @@ void visit_optional(Visitor *v, bool *present, const char *name,
     }
 }

-void visit_get_next_type(Visitor *v, QTypeCode *type,
+void visit_get_next_type(Visitor *v, QTypeCode *type, bool promote_int,
                          const char *name, Error **errp)
 {
     if (v->get_next_type) {
-        v->get_next_type(v, type, name, errp);
+        v->get_next_type(v, type, promote_int, name, errp);
     }
 }

diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 2141ff5..4238faf 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -209,6 +209,7 @@ static void qmp_input_end_list(Visitor *v, Error **errp)
 }

 static void qmp_input_get_next_type(Visitor *v, QTypeCode *type,
+                                    bool promote_int,
                                     const char *name, Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
@@ -219,6 +220,9 @@ static void qmp_input_get_next_type(Visitor *v, QTypeCode *type,
         return;
     }
     *type = qobject_type(qobj);
+    if (promote_int && *type == QTYPE_QINT) {
+        *type = QTYPE_QFLOAT;
+    }
 }

 static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name,
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index ad98685..4c9d739 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -183,6 +183,11 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s *obj, const char *name, Error


 def gen_visit_alternate(name, variants):
+    promote_int = 'true'
+    for var in variants.variants:
+        if var.type.alternate_qtype() == 'QTYPE_QINT':
+            promote_int = 'false'
+
     ret = mcgen('''

 void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
@@ -193,16 +198,14 @@ 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, &(*obj)->type, name, &err);
+    visit_get_next_type(v, &(*obj)->type, %(promote_int)s, name, &err);
     if (err) {
         goto out_obj;
     }
     switch ((*obj)->type) {
 ''',
-                c_name=c_name(name))
+                c_name=c_name(name), promote_int=promote_int)

-    # FIXME: When 'number' but not 'int' is present in the alternate, we
-    # should allow QTYPE_INT to promote to QTYPE_FLOAT.
     for var in variants.variants:
         ret += mcgen('''
     case %(case)s:
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index 43b9e18..b4a5bee 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -347,20 +347,16 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data,
     error_free_or_abort(&err);
     qapi_free_AltStrBool(asb);

-    /* FIXME: integer should parse as number */
     v = visitor_input_test_init(data, "42");
-    visit_type_AltStrNum(v, &asn, NULL, &err);
-    /* FIXME g_assert_cmpint(asn->type, ==, QTYPE_QFLOAT); */
-    /* FIXME g_assert_cmpfloat(asn->u.n, ==, 42); */
-    error_free_or_abort(&err);
+    visit_type_AltStrNum(v, &asn, NULL, &error_abort);
+    g_assert_cmpint(asn->type, ==, QTYPE_QFLOAT);
+    g_assert_cmpfloat(asn->u.n, ==, 42);
     qapi_free_AltStrNum(asn);

-    /* FIXME: integer should parse as number */
     v = visitor_input_test_init(data, "42");
-    visit_type_AltNumStr(v, &ans, NULL, &err);
-    /* FIXME g_assert_cmpint(ans->type, ==, QTYPE_QFLOAT); */
-    /* FIXME g_assert_cmpfloat(ans->u.n, ==, 42); */
-    error_free_or_abort(&err);
+    visit_type_AltNumStr(v, &ans, NULL, &error_abort);
+    g_assert_cmpint(ans->type, ==, QTYPE_QFLOAT);
+    g_assert_cmpfloat(ans->u.n, ==, 42);
     qapi_free_AltNumStr(ans);

     v = visitor_input_test_init(data, "42");
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 24/28] qapi: Add positive tests to qapi-schema-test
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (22 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 23/28] qapi: Fix alternates that accept 'number' but not 'int' Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 25/28] qapi: Simplify visits of optional fields Eric Blake
                   ` (3 subsequent siblings)
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Add positive tests to qapi-schema-test for things that were
made possible by recent patches but which caused compile errors
due to collisions prior to that point.  The focus is mainly on
collisions due to names we have reserved for qapi, even though
it is unlikely that anyone will want to abuse these names in
actual .json files.

The added tests includes:
Use of a member name ending in 'Kind' or 'List' [1, 3]
Use of a type name starting with 'has_' [1, 4]
Use of a type named 'u' [1, 5]
Use of a union branch name of 'u' [2, 5]
Use of a union branch name starting with 'has_' [2, 4]

[1] Never broken, but could break if reservations are too strict
[2] Broken prior to commit e4ba22b
[3] See reservations in commit 4dc2e69 and 255960d
[4] See reservations in commit 9fb081e
[5] See reservation in commit 5e59baf

Not worth testing here: we no longer have a collision with a
member named 'base' (commit ddf2190), with a branch named
'type' (commit e4ba22b), or with an alternate member named
'max' (previous commit); these names were more accidental
namespace pollutions than intentional reservations.

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

---
v11 (no v10): drop test of 'max'
v9: reorder in series (was 9/17); fewer tests of 'base' and
'type' non-collision; fold in alternate 'max' test; update commit
message
v8: new, but collects portions of subset B v10 patches 2, 3, and
16 and subset C v7 patch 6 that were deferred to later.

It might be worth dropping or simplifying this patch, depending
on how many corner cases we actually want to test.
---
 tests/qapi-schema/qapi-schema-test.json | 16 ++++++++++++++++
 tests/qapi-schema/qapi-schema-test.out  | 26 ++++++++++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 44638da..0f0b5ce 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -113,6 +113,22 @@
             'sizes': ['size'],
             'any': ['any'] } }

+# 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.  Flat union branches do not
+# collide with base members.
+{ 'enum': 'EnumName', 'data': [ 'value1', 'has_a', 'u' ] }
+{ 'struct': 'has_a', 'data': { 'MyKind': 'int', 'MyList': ['int'],
+                               'value1': 'EnumName' } }
+{ 'union': 'u', 'data': { 'u': 'uint8', 'myKind': 'has_a',
+                          'myList': 'has_a', 'has_a': 'has_a' } }
+{ 'union': 'UnionName', 'base': 'has_a', 'discriminator': 'value1',
+  'data': { 'value1': 'UserDefZero', 'has_a': 'UserDefZero',
+            'u': 'UserDefZero' } }
+{ 'alternate': 'AltName', 'data': { 'type': 'int', 'u': 'bool',
+                                    'myKind': '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 6f2edcc..74da413 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
@@ -56,6 +60,10 @@ object :obj-user_def_cmd2-arg
 alternate AltIntNum
     case i: int
     case n: number
+alternate AltName
+    case type: int
+    case u: bool
+    case myKind: has_a
 alternate AltNumInt
     case n: number
     case i: int
@@ -78,6 +86,7 @@ event EVENT_D :obj-EVENT_D-arg
 object Empty1
 object Empty2
     base Empty1
+enum EnumName ['value1', 'has_a', 'u']
 enum EnumOne ['value1', 'value2', 'value3']
 object EventStructOne
     member struct1: UserDefOne optional=False
@@ -101,6 +110,12 @@ object TestStruct
     member integer: int optional=False
     member boolean: bool optional=False
     member string: str optional=False
+object UnionName
+    base has_a
+    tag value1
+    case value1: UserDefZero
+    case has_a: UserDefZero
+    case u: UserDefZero
 object UserDefA
     member boolean: bool optional=False
     member a_b: int optional=True
@@ -195,6 +210,17 @@ 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
+    member value1: EnumName optional=False
+object u
+    member type: uKind optional=False
+    case u: :obj-uint8-wrapper
+    case myKind: :obj-has_a-wrapper
+    case myList: :obj-has_a-wrapper
+    case has_a: :obj-has_a-wrapper
+enum uKind ['u', 'myKind', 'myList', 'has_a']
 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] 72+ messages in thread

* [Qemu-devel] [PATCH v11 25/28] qapi: Simplify visits of optional fields
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (23 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 24/28] qapi: Add positive tests to qapi-schema-test Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-12 15:11   ` Markus Armbruster
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 26/28] qapi: Move duplicate member checks to schema check() Eric Blake
                   ` (2 subsequent siblings)
  27 siblings, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

None of the visitor callbacks would set an error when testing
if an optional field was present; make this part of the interface
contract by eliminating the errp argument.  Then, for less code,
reflect the determined boolean value back to the caller instead
of making the caller read the boolean after the fact.

The resulting generated code has a nice diff:

|-    visit_optional(v, &has_fdset_id, "fdset-id", &err);
|-    if (err) {
|-        goto out;
|-    }
|-    if (has_fdset_id) {
|+    if (visit_optional(v, &has_fdset_id, "fdset-id")) {
|         visit_type_int(v, &fdset_id, "fdset-id", &err);
|         if (err) {
|             goto out;
|         }
|     }

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

---
v11 (no v10): no change
v9: no change
v8: no change
v7: rebase to no member.c_name()
v6: rebase onto earlier testsuite and gen_err_check() improvements
---
 include/qapi/visitor-impl.h |  5 ++---
 include/qapi/visitor.h      | 10 ++++++++--
 qapi/opts-visitor.c         |  2 +-
 qapi/qapi-visit-core.c      |  6 +++---
 qapi/qmp-input-visitor.c    |  3 +--
 qapi/string-input-visitor.c |  3 +--
 scripts/qapi.py             |  7 +------
 7 files changed, 17 insertions(+), 19 deletions(-)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index ac6c17e..81d1aa0 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -44,9 +44,8 @@ struct Visitor
     void (*type_any)(Visitor *v, QObject **obj, const char *name,
                      Error **errp);

-    /* May be NULL */
-    void (*optional)(Visitor *v, bool *present, const char *name,
-                     Error **errp);
+    /* May be NULL; most useful for input visitors. */
+    void (*optional)(Visitor *v, bool *present, const char *name);

     void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp);
     void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp);
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 250e8e1..dd4f0b3 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -39,8 +39,14 @@ void visit_end_implicit_struct(Visitor *v, Error **errp);
 void visit_start_list(Visitor *v, const char *name, Error **errp);
 GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
 void visit_end_list(Visitor *v, Error **errp);
-void visit_optional(Visitor *v, bool *present, const char *name,
-                    Error **errp);
+
+/**
+ * Check if an optional member @name of an object needs visiting.
+ * For input visitors, set *@present according to whether the
+ * corresponding visit_type_*() needs calling; for other visitors,
+ * leave *@present unchanged.  Return *@present for convenience.
+ */
+bool visit_optional(Visitor *v, bool *present, const char *name);

 /**
  * Determine the qtype of the item @name in the current object visit.
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index cd10392..ef5fb8b 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -488,7 +488,7 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)


 static void
-opts_optional(Visitor *v, bool *present, const char *name, Error **errp)
+opts_optional(Visitor *v, bool *present, const char *name)
 {
     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);

diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 52c132a..daaa3af 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -73,12 +73,12 @@ void visit_end_union(Visitor *v, bool data_present, Error **errp)
     }
 }

-void visit_optional(Visitor *v, bool *present, const char *name,
-                    Error **errp)
+bool visit_optional(Visitor *v, bool *present, const char *name)
 {
     if (v->optional) {
-        v->optional(v, present, name, errp);
+        v->optional(v, present, name);
     }
+    return *present;
 }

 void visit_get_next_type(Visitor *v, QTypeCode *type, bool promote_int,
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 4238faf..9f0ed69 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -304,8 +304,7 @@ static void qmp_input_type_any(Visitor *v, QObject **obj, const char *name,
     *obj = qobj;
 }

-static void qmp_input_optional(Visitor *v, bool *present, const char *name,
-                               Error **errp)
+static void qmp_input_optional(Visitor *v, bool *present, const char *name)
 {
     QmpInputVisitor *qiv = to_qiv(v);
     QObject *qobj = qmp_input_get_object(qiv, name, true);
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index bbd6a54..dee780a 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -299,8 +299,7 @@ static void parse_type_number(Visitor *v, double *obj, const char *name,
     *obj = val;
 }

-static void parse_optional(Visitor *v, bool *present, const char *name,
-                           Error **errp)
+static void parse_optional(Visitor *v, bool *present, const char *name)
 {
     StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 1790e8f..b17f843 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1656,15 +1656,10 @@ def gen_visit_fields(members, prefix='', need_cast=False, skiperr=False):
     for memb in members:
         if memb.optional:
             ret += mcgen('''
-    visit_optional(v, &%(prefix)shas_%(c_name)s, "%(name)s", %(errp)s);
+    if (visit_optional(v, &%(prefix)shas_%(c_name)s, "%(name)s")) {
 ''',
                          prefix=prefix, c_name=c_name(memb.name),
                          name=memb.name, errp=errparg)
-            ret += gen_err_check(skiperr=skiperr)
-            ret += mcgen('''
-    if (%(prefix)shas_%(c_name)s) {
-''',
-                         prefix=prefix, c_name=c_name(memb.name))
             push_indent()

         # Ugly: sometimes we need to cast away const
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 26/28] qapi: Move duplicate member checks to schema check()
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (24 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 25/28] qapi: Simplify visits of optional fields Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 27/28] qapi: Move duplicate enum value " Eric Blake
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 28/28] qapi: Detect base class loops Eric Blake
  27 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

With the previous commit, we have two different locations for
detecting member name clashes - one at parse time, and another
at QAPISchema*.check() time.  Consolidate some of the checks
into a single place, which is also in line with our TODO to
eventually move all of the parse time semantic checking into
the newer schema code.  The check_member_clash() function is
no longer needed.

The wording of several error messages has changed, but in many
cases feels like an improvement rather than a regression.  The
recent change (commit 7b2a5c2) to avoid an assertion failure
when a flat union branch name collides with its discriminator
name is also handled nicely by this code; but there is more work
needed before we can detect all collisions in the generated enum
associated with simple union branch names.

No change to generated code.

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

---
v11 (no v10): no change
v9: simplify on top of earlier check() improvements
v8: decide whether to inline members based on union vs. alternate,
not on flat vs. simple, and fix logic to avoid breaking
union-clash-data in the process; add comments; assumes
pull-qapi-2015-10-12 will go in without modifying commit ids
v7: comment improvements, retitle subject
v6: rebase to earlier testsuite improvements, fold in cleanup
of flat-union-clash-type
---
 scripts/qapi.py                               | 34 +--------------------------
 tests/qapi-schema/alternate-clash.err         |  2 +-
 tests/qapi-schema/flat-union-clash-member.err |  2 +-
 tests/qapi-schema/struct-base-clash-deep.err  |  2 +-
 tests/qapi-schema/struct-base-clash.err       |  2 +-
 5 files changed, 5 insertions(+), 37 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index b17f843..1d59ce9 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -502,21 +502,6 @@ def check_type(expr_info, source, value, allow_array=False,
                                 'enum'])


-def check_member_clash(expr_info, base_name, data, source=""):
-    base = find_struct(base_name)
-    assert base
-    base_members = base['data']
-    for key in data.keys():
-        if key.startswith('*'):
-            key = key[1:]
-        if key in base_members or "*" + key in base_members:
-            raise QAPIExprError(expr_info,
-                                "Member name '%s'%s clashes with base '%s'"
-                                % (key, source, base_name))
-    if base.get('base'):
-        check_member_clash(expr_info, base['base'], data, source)
-
-
 def check_command(expr, expr_info):
     name = expr['command']

@@ -595,15 +580,9 @@ def check_union(expr, expr_info):
     for (key, value) in members.items():
         check_name(expr_info, "Member of union '%s'" % name, key)

-        # Each value must name a known type; furthermore, in flat unions,
-        # branches must be a struct with no overlapping member names
+        # Each value must name a known type
         check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
                    value, allow_array=not base, allow_metas=allow_metas)
-        if base:
-            branch_struct = find_struct(value)
-            assert branch_struct
-            check_member_clash(expr_info, base, branch_struct['data'],
-                               " of branch '%s'" % key)

         # If the discriminator names an enum type, then all members
         # of 'data' must also be members of the enum type.
@@ -627,21 +606,12 @@ def check_union(expr, expr_info):
 def check_alternate(expr, expr_info):
     name = expr['alternate']
     members = expr['data']
-    values = {}
     types_seen = {}

     # Check every branch
     for (key, value) in members.items():
         check_name(expr_info, "Member of alternate '%s'" % name, key)

-        # Check for conflicts in the branch names
-        c_key = c_name(key)
-        if c_key in values:
-            raise QAPIExprError(expr_info,
-                                "Alternate '%s' member '%s' clashes with '%s'"
-                                % (name, key, values[c_key]))
-        values[c_key] = key
-
         # Ensure alternates have no type conflicts.
         check_type(expr_info, "Member '%s' of alternate '%s'" % (key, name),
                    value,
@@ -687,8 +657,6 @@ def check_struct(expr, expr_info):
                allow_dict=True, allow_optional=True)
     check_type(expr_info, "'base' for struct '%s'" % name, expr.get('base'),
                allow_metas=['struct'])
-    if expr.get('base'):
-        check_member_clash(expr_info, expr['base'], expr['data'])


 def check_keys(expr_elem, meta, required, optional=[]):
diff --git a/tests/qapi-schema/alternate-clash.err b/tests/qapi-schema/alternate-clash.err
index a475ab6..604d849 100644
--- a/tests/qapi-schema/alternate-clash.err
+++ b/tests/qapi-schema/alternate-clash.err
@@ -1 +1 @@
-tests/qapi-schema/alternate-clash.json:7: Alternate 'Alt1' member 'a_b' clashes with 'a-b'
+tests/qapi-schema/alternate-clash.json:7: 'a_b' (branch of Alt1) collides with 'a-b' (branch of Alt1)
diff --git a/tests/qapi-schema/flat-union-clash-member.err b/tests/qapi-schema/flat-union-clash-member.err
index 2f0397a..2adf697 100644
--- a/tests/qapi-schema/flat-union-clash-member.err
+++ b/tests/qapi-schema/flat-union-clash-member.err
@@ -1 +1 @@
-tests/qapi-schema/flat-union-clash-member.json:11: Member name 'name' of branch 'value1' clashes with base 'Base'
+tests/qapi-schema/flat-union-clash-member.json:11: 'name' (member of Branch1) collides with 'name' (member of Base)
diff --git a/tests/qapi-schema/struct-base-clash-deep.err b/tests/qapi-schema/struct-base-clash-deep.err
index f7a25a3..e2d7943 100644
--- a/tests/qapi-schema/struct-base-clash-deep.err
+++ b/tests/qapi-schema/struct-base-clash-deep.err
@@ -1 +1 @@
-tests/qapi-schema/struct-base-clash-deep.json:10: Member name 'name' clashes with base 'Base'
+tests/qapi-schema/struct-base-clash-deep.json:10: 'name' (member of Sub) collides with 'name' (member of Base)
diff --git a/tests/qapi-schema/struct-base-clash.err b/tests/qapi-schema/struct-base-clash.err
index 3a9f66b..c52f33d 100644
--- a/tests/qapi-schema/struct-base-clash.err
+++ b/tests/qapi-schema/struct-base-clash.err
@@ -1 +1 @@
-tests/qapi-schema/struct-base-clash.json:5: Member name 'name' clashes with base 'Base'
+tests/qapi-schema/struct-base-clash.json:5: 'name' (member of Sub) collides with 'name' (member of Base)
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 27/28] qapi: Move duplicate enum value checks to schema check()
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (25 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 26/28] qapi: Move duplicate member checks to schema check() Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-12 15:46   ` Markus Armbruster
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 28/28] qapi: Detect base class loops Eric Blake
  27 siblings, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Similar to the previous commit, move the detection of a collision
in enum values from parse time to QAPISchemaEnumType.check().
This happens to also detect collisions in union branch names
mapping to the same enum value, even when the names do not
collide case-wise.  So for a decent error message, we have to
determine if the enum is implicit (and if so where the real
collision lies).

Testing this showed that the test union-bad-branch wasn't adding
much: union-clash-branches exposes the error message when branches
directly collide, and union-max exposes the error message when
branches cause an enum collision (union-bad-branch basically
causes an enum collision that would not be a C collision).  This
goes along with our desire to require ALL names to be
case-insensitively unique.

No change to generated code.

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

---
v11 (no v10): message of union-clash-branches.err changed: rely on
enum check rather than Variant.check() to catch it
v9: rebase to earlier changes, update commit message, break out
helper _describe() method
v8: rebase to earlier changes; better comments
v7: retitle and improve commit message; earlier subclass patches
avoid problem with detecting 'kind' collision
v6: new patch
---
 scripts/qapi.py                             | 41 ++++++++++++++++-------------
 tests/Makefile                              |  1 -
 tests/qapi-schema/enum-clash-member.err     |  2 +-
 tests/qapi-schema/enum-max-member.err       |  2 +-
 tests/qapi-schema/union-bad-branch.err      |  1 -
 tests/qapi-schema/union-bad-branch.exit     |  1 -
 tests/qapi-schema/union-bad-branch.json     |  8 ------
 tests/qapi-schema/union-bad-branch.out      |  0
 tests/qapi-schema/union-clash-branches.err  |  2 +-
 tests/qapi-schema/union-clash-branches.json |  2 +-
 tests/qapi-schema/union-max.err             |  2 +-
 11 files changed, 28 insertions(+), 34 deletions(-)
 delete mode 100644 tests/qapi-schema/union-bad-branch.err
 delete mode 100644 tests/qapi-schema/union-bad-branch.exit
 delete mode 100644 tests/qapi-schema/union-bad-branch.json
 delete mode 100644 tests/qapi-schema/union-bad-branch.out

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 1d59ce9..08a366e 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -533,7 +533,6 @@ def check_union(expr, expr_info):
     base = expr.get('base')
     discriminator = expr.get('discriminator')
     members = expr['data']
-    values = {'MAX': '(automatic)'}

     # Two types of unions, determined by discriminator.

@@ -593,15 +592,6 @@ def check_union(expr, expr_info):
                                     "enum '%s'" %
                                     (key, enum_define["enum_name"]))

-        # Otherwise, check for conflicts in the generated enum
-        else:
-            c_key = camel_to_upper(key)
-            if c_key in values:
-                raise QAPIExprError(expr_info,
-                                    "Union '%s' member '%s' clashes with '%s'"
-                                    % (name, key, values[c_key]))
-            values[c_key] = key
-

 def check_alternate(expr, expr_info):
     name = expr['alternate']
@@ -630,7 +620,6 @@ def check_enum(expr, expr_info):
     name = expr['enum']
     members = expr.get('data')
     prefix = expr.get('prefix')
-    values = {'MAX': '(automatic)'}

     if not isinstance(members, list):
         raise QAPIExprError(expr_info,
@@ -641,12 +630,6 @@ def check_enum(expr, expr_info):
     for member in members:
         check_name(expr_info, "Member of enum '%s'" % name, member,
                    enum_member=True)
-        key = camel_to_upper(member)
-        if key in values:
-            raise QAPIExprError(expr_info,
-                                "Enum '%s' member '%s' clashes with '%s'"
-                                % (name, member, values[key]))
-        values[key] = member


 def check_struct(expr, expr_info):
@@ -873,8 +856,30 @@ class QAPISchemaEnumType(QAPISchemaType):
         self.values = values
         self.prefix = prefix

+    def _describe(self, schema):
+        # If the enum is implicit, report the error on behalf of
+        # the union or alternate that triggered the enum
+        if self.is_implicit():
+            owner = schema.lookup_type(self.name[:-4])
+            assert owner
+            if isinstance(owner, QAPISchemaAlternateType):
+                return "Alternate '%s' branch" % owner.name
+            else:
+                return "Union '%s' branch" % owner.name
+        else:
+            return "Enum '%s' value" % self.name
+
     def check(self, schema):
-        assert len(set(self.values)) == len(self.values)
+        # Check for collisions on the generated C enum values
+        seen = {c_enum_const(self.name, 'MAX'): '(automatic MAX)'}
+        for value in self.values:
+            c_value = c_enum_const(self.name, value)
+            if c_value in seen:
+                raise QAPIExprError(self.info,
+                                    "%s '%s' clashes with '%s'"
+                                    % (self._describe(schema), value,
+                                       seen[c_value]))
+            seen[c_value] = value

     def is_implicit(self):
         # See QAPISchema._make_implicit_enum_type()
diff --git a/tests/Makefile b/tests/Makefile
index d1c6817..cdff7a4 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -336,7 +336,6 @@ qapi-schema += unclosed-list.json
 qapi-schema += unclosed-object.json
 qapi-schema += unclosed-string.json
 qapi-schema += unicode-str.json
-qapi-schema += union-bad-branch.json
 qapi-schema += union-base-no-discriminator.json
 qapi-schema += union-clash-branches.json
 qapi-schema += union-clash-data.json
diff --git a/tests/qapi-schema/enum-clash-member.err b/tests/qapi-schema/enum-clash-member.err
index 48bd136..84030c5 100644
--- a/tests/qapi-schema/enum-clash-member.err
+++ b/tests/qapi-schema/enum-clash-member.err
@@ -1 +1 @@
-tests/qapi-schema/enum-clash-member.json:2: Enum 'MyEnum' member 'ONE' clashes with 'one'
+tests/qapi-schema/enum-clash-member.json:2: Enum 'MyEnum' value 'ONE' clashes with 'one'
diff --git a/tests/qapi-schema/enum-max-member.err b/tests/qapi-schema/enum-max-member.err
index f77837f..6b9ef9b 100644
--- a/tests/qapi-schema/enum-max-member.err
+++ b/tests/qapi-schema/enum-max-member.err
@@ -1 +1 @@
-tests/qapi-schema/enum-max-member.json:3: Enum 'MyEnum' member 'max' clashes with '(automatic)'
+tests/qapi-schema/enum-max-member.json:3: Enum 'MyEnum' value 'max' clashes with '(automatic MAX)'
diff --git a/tests/qapi-schema/union-bad-branch.err b/tests/qapi-schema/union-bad-branch.err
deleted file mode 100644
index 8822735..0000000
--- a/tests/qapi-schema/union-bad-branch.err
+++ /dev/null
@@ -1 +0,0 @@
-tests/qapi-schema/union-bad-branch.json:6: Union 'MyUnion' member 'ONE' clashes with 'one'
diff --git a/tests/qapi-schema/union-bad-branch.exit b/tests/qapi-schema/union-bad-branch.exit
deleted file mode 100644
index d00491f..0000000
--- a/tests/qapi-schema/union-bad-branch.exit
+++ /dev/null
@@ -1 +0,0 @@
-1
diff --git a/tests/qapi-schema/union-bad-branch.json b/tests/qapi-schema/union-bad-branch.json
deleted file mode 100644
index 913aa38..0000000
--- a/tests/qapi-schema/union-bad-branch.json
+++ /dev/null
@@ -1,8 +0,0 @@
-# we reject normal unions where branches would collide in C
-{ 'struct': 'One',
-  'data': { 'string': 'str' } }
-{ 'struct': 'Two',
-  'data': { 'number': 'int' } }
-{ 'union': 'MyUnion',
-  'data': { 'one': 'One',
-            'ONE': 'Two' } }
diff --git a/tests/qapi-schema/union-bad-branch.out b/tests/qapi-schema/union-bad-branch.out
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/qapi-schema/union-clash-branches.err b/tests/qapi-schema/union-clash-branches.err
index 005c48d..d8f1265 100644
--- a/tests/qapi-schema/union-clash-branches.err
+++ b/tests/qapi-schema/union-clash-branches.err
@@ -1 +1 @@
-tests/qapi-schema/union-clash-branches.json:4: Union 'TestUnion' member 'a_b' clashes with 'a-b'
+tests/qapi-schema/union-clash-branches.json:4: Union 'TestUnion' branch 'a_b' clashes with 'a-b'
diff --git a/tests/qapi-schema/union-clash-branches.json b/tests/qapi-schema/union-clash-branches.json
index 31d135f..3bece8c 100644
--- a/tests/qapi-schema/union-clash-branches.json
+++ b/tests/qapi-schema/union-clash-branches.json
@@ -1,5 +1,5 @@
 # Union branch name collision
 # Reject a union that would result in a collision in generated C names (this
-# would try to generate two enum values 'TEST_UNION_KIND_A_B').
+# would try to generate two members 'a_b').
 { 'union': 'TestUnion',
   'data': { 'a-b': 'int', 'a_b': 'str' } }
diff --git a/tests/qapi-schema/union-max.err b/tests/qapi-schema/union-max.err
index 55ce439..b93beae 100644
--- a/tests/qapi-schema/union-max.err
+++ b/tests/qapi-schema/union-max.err
@@ -1 +1 @@
-tests/qapi-schema/union-max.json:2: Union 'Union' member 'max' clashes with '(automatic)'
+tests/qapi-schema/union-max.json:2: Union 'Union' branch 'max' clashes with '(automatic MAX)'
-- 
2.4.3

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

* [Qemu-devel] [PATCH v11 28/28] qapi: Detect base class loops
  2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
                   ` (26 preceding siblings ...)
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 27/28] qapi: Move duplicate enum value " Eric Blake
@ 2015-11-11  6:51 ` Eric Blake
  2015-11-12 16:06   ` Markus Armbruster
  27 siblings, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-11  6:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

It should be fairly obvious that qapi base classes need to
form an acyclic graph, since QMP cannot specify the same
key more than once, while base classes are included as flat
members alongside other members added by the child.  But the
old check_member_clash() parser function was not prepared to
check for this, and entered an infinite recursion (at least
until python gives up, complaining about nesting too deep).

Now that check_member_clash() has been recently removed,
attempts at self-inheritance trigger an assertion failure
introduced by commit ac88219a.  The obvious fix is to turn
the assertion into a conditional.

This patch includes both the test and the fix, since the .err
file output for the unfixed case is not useful (particularly
when it was warning about unbounded recursion, as that limit
may be platform-specific).

We don't need to worry about cycles in flat unions (neither
the base nor a variant class can be a union) nor in alternates
(alternate branches cannot themselves be an alternate).  And
even the case of using the same class for a flat union base
class and one its variants is already caught by the fact that
both uses will introduce the same member name (or will be okay
if the class is empty).

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

---
v11 (no v10): rename base-cycle to base-cycle-indirect, and add
base-cycle-direct; touch up commit message
v9: no change
v8: improve commit message
v7: improve commit message
v6: rebase to earlier info changes
---
 scripts/qapi.py                            | 6 +++++-
 tests/Makefile                             | 2 ++
 tests/qapi-schema/base-cycle-direct.err    | 1 +
 tests/qapi-schema/base-cycle-direct.exit   | 1 +
 tests/qapi-schema/base-cycle-direct.json   | 2 ++
 tests/qapi-schema/base-cycle-direct.out    | 0
 tests/qapi-schema/base-cycle-indirect.err  | 1 +
 tests/qapi-schema/base-cycle-indirect.exit | 1 +
 tests/qapi-schema/base-cycle-indirect.json | 3 +++
 tests/qapi-schema/base-cycle-indirect.out  | 0
 10 files changed, 16 insertions(+), 1 deletion(-)
 create mode 100644 tests/qapi-schema/base-cycle-direct.err
 create mode 100644 tests/qapi-schema/base-cycle-direct.exit
 create mode 100644 tests/qapi-schema/base-cycle-direct.json
 create mode 100644 tests/qapi-schema/base-cycle-direct.out
 create mode 100644 tests/qapi-schema/base-cycle-indirect.err
 create mode 100644 tests/qapi-schema/base-cycle-indirect.exit
 create mode 100644 tests/qapi-schema/base-cycle-indirect.json
 create mode 100644 tests/qapi-schema/base-cycle-indirect.out

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 08a366e..e60c1d8 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -941,7 +941,11 @@ class QAPISchemaObjectType(QAPISchemaType):
         self.members = None

     def check(self, schema):
-        assert self.members is not False        # not running in cycles
+        if self.members is False:               # check for cycles
+            assert self._base_name
+            raise QAPIExprError(self.info,
+                                "Object %s cyclically depends on %s"
+                                % (self.name, self._base_name))
         if self.members:
             return
         self.members = False                    # mark as being checked
diff --git a/tests/Makefile b/tests/Makefile
index cdff7a4..2706126 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -253,6 +253,8 @@ qapi-schema += bad-ident.json
 qapi-schema += bad-type-bool.json
 qapi-schema += bad-type-dict.json
 qapi-schema += bad-type-int.json
+qapi-schema += base-cycle-direct.json
+qapi-schema += base-cycle-indirect.json
 qapi-schema += command-int.json
 qapi-schema += comments.json
 qapi-schema += double-data.json
diff --git a/tests/qapi-schema/base-cycle-direct.err b/tests/qapi-schema/base-cycle-direct.err
new file mode 100644
index 0000000..46fc436
--- /dev/null
+++ b/tests/qapi-schema/base-cycle-direct.err
@@ -0,0 +1 @@
+tests/qapi-schema/base-cycle-direct.json:2: Object Foo cyclically depends on Foo
diff --git a/tests/qapi-schema/base-cycle-direct.exit b/tests/qapi-schema/base-cycle-direct.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/base-cycle-direct.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/base-cycle-direct.json b/tests/qapi-schema/base-cycle-direct.json
new file mode 100644
index 0000000..dff6fab
--- /dev/null
+++ b/tests/qapi-schema/base-cycle-direct.json
@@ -0,0 +1,2 @@
+# we reject a loop in base classes
+{ 'struct': 'Foo', 'base': 'Foo', 'data': {} }
diff --git a/tests/qapi-schema/base-cycle-direct.out b/tests/qapi-schema/base-cycle-direct.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/base-cycle-indirect.err b/tests/qapi-schema/base-cycle-indirect.err
new file mode 100644
index 0000000..9a1ca44
--- /dev/null
+++ b/tests/qapi-schema/base-cycle-indirect.err
@@ -0,0 +1 @@
+tests/qapi-schema/base-cycle-indirect.json:2: Object Base1 cyclically depends on Base2
diff --git a/tests/qapi-schema/base-cycle-indirect.exit b/tests/qapi-schema/base-cycle-indirect.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/base-cycle-indirect.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/base-cycle-indirect.json b/tests/qapi-schema/base-cycle-indirect.json
new file mode 100644
index 0000000..2866772
--- /dev/null
+++ b/tests/qapi-schema/base-cycle-indirect.json
@@ -0,0 +1,3 @@
+# we reject a loop in base classes
+{ 'struct': 'Base1', 'base': 'Base2', 'data': {} }
+{ 'struct': 'Base2', 'base': 'Base1', 'data': {} }
diff --git a/tests/qapi-schema/base-cycle-indirect.out b/tests/qapi-schema/base-cycle-indirect.out
new file mode 100644
index 0000000..e69de29
-- 
2.4.3

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

* Re: [Qemu-devel] [PATCH v11 18/28] qerror: more error_setg() usage
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 18/28] qerror: more error_setg() usage Eric Blake
@ 2015-11-11 13:26   ` Andreas Färber
  2015-11-11 14:21   ` Markus Armbruster
  1 sibling, 0 replies; 72+ messages in thread
From: Andreas Färber @ 2015-11-11 13:26 UTC (permalink / raw)
  To: Eric Blake, qemu-devel
  Cc: Kevin Wolf, Jiri Pirko, Eduardo Habkost, qemu-block,
	Michael S. Tsirkin, Jason Wang, armbru, Scott Feldman,
	Paolo Bonzini, Richard Henderson

Am 11.11.2015 um 07:51 schrieb Eric Blake:
> A few uses of error_set(ERROR_CLASS_GENERIC_ERROR) have snuck in
> since c6bd8c706.  Nuke them.
> 
> Signed-off-by: Eric Blake <eblake@redhat.com>
[...]
> diff --git a/qom/object.c b/qom/object.c
> index fc6e161..c0decb6 100644
> --- a/qom/object.c
> +++ b/qom/object.c
> @@ -1330,8 +1330,8 @@ static Object *object_resolve_link(Object *obj, const char *name,
>      target = object_resolve_path_type(path, target_type, &ambiguous);
> 
>      if (ambiguous) {
> -        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
> -                  "Path '%s' does not uniquely identify an object", path);
> +        error_setg(errp, "Path '%s' does not uniquely identify an object",
> +                   path);
>      } else if (!target) {
>          target = object_resolve_path(path, &ambiguous);
>          if (target || ambiguous) {

Acked-by: Andreas Färber <afaerber@suse.de>

No idea why it's this way, maybe predated the function?

Regards,
Andreas

-- 
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton; HRB 21284 (AG Nürnberg)

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

* Re: [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values Eric Blake
@ 2015-11-11 13:29   ` Andreas Färber
  2015-11-11 14:50   ` Markus Armbruster
  2015-11-13 17:46   ` Eric Blake
  2 siblings, 0 replies; 72+ messages in thread
From: Andreas Färber @ 2015-11-11 13:29 UTC (permalink / raw)
  To: Eric Blake, qemu-devel
  Cc: Kevin Wolf, Peter Maydell, Michael Roth, qemu-block,
	Michael S. Tsirkin, Jason Wang, armbru, Luiz Capitulino,
	Gerd Hoffmann, Amit Shah

Am 11.11.2015 um 07:51 schrieb Eric Blake:
> When munging enum values, the fact that we were passing the entire
> prefix + value through camel_to_upper() meant that enum values
> spelled with CamelCase could be turned into CAMEL_CASE.  However,
> this provides a potential collision (both OneTwo and One-Two would
> munge into ONE_TWO).  By changing the generation of enum constants
> to always be prefix + '_' + c_name(value).upper(), we can avoid
> any risk of collisions (if we can also ensure no case collisions,
> in the next patch) without having to think about what the
> heuristics in camel_to_upper() will actually do to the value.
> 
> Thankfully, only two enums are affected: ErrorClass and InputButton.
> 
> Suggested by: Markus Armbruster <armbru@redhat.com>
> Signed-off-by: Eric Blake <eblake@redhat.com>
[...]
> diff --git a/qdev-monitor.c b/qdev-monitor.c
> index a35098f..1c39432 100644
> --- a/qdev-monitor.c
> +++ b/qdev-monitor.c
> @@ -461,7 +461,7 @@ static BusState *qbus_find(const char *path, Error **errp)
>          pos += len;
>          dev = qbus_find_dev(bus, elem);
>          if (!dev) {
> -            error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
> +            error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
>                        "Device '%s' not found", elem);
>              qbus_list_dev(bus, errp);
>              return NULL;
> @@ -788,7 +788,7 @@ void qmp_device_del(const char *id, Error **errp)
>      }
> 
>      if (!obj) {
> -        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
> +        error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
>                    "Device '%s' not found", id);
>          return;
>      }
[...]
> diff --git a/qom/object.c b/qom/object.c
> index c0decb6..425b7c3 100644
> --- a/qom/object.c
> +++ b/qom/object.c
> @@ -1018,7 +1018,7 @@ Object *object_property_get_link(Object *obj, const char *name,
>      if (str && *str) {
>          target = object_resolve_path(str, NULL);
>          if (!target) {
> -            error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
> +            error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
>                        "Device '%s' not found", str);
>          }
>      }
> @@ -1337,7 +1337,7 @@ static Object *object_resolve_link(Object *obj, const char *name,
>          if (target || ambiguous) {
>              error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, target_type);
>          } else {
> -            error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
> +            error_set(errp, ERROR_CLASS_DEVICENOTFOUND,
>                        "Device '%s' not found", path);
>          }
>          target = NULL;

That spelling is not exactly an improvement, but well,

Reviewed-by: Andreas Färber <afaerber@suse.de>

Regards,
Andreas

-- 
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton; HRB 21284 (AG Nürnberg)

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

* Re: [Qemu-devel] [PATCH v11 11/28] qapi: Check for qapi collisions of flat union branches
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 11/28] qapi: Check for qapi collisions of flat union branches Eric Blake
@ 2015-11-11 13:42   ` Markus Armbruster
  2015-11-11 15:49     ` Eric Blake
  0 siblings, 1 reply; 72+ messages in thread
From: Markus Armbruster @ 2015-11-11 13:42 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Right now, our ad hoc parser ensures that we cannot have a
> flat union that introduces any qapi member names that would
> conflict with the non-variant qapi members already present
> from the union's base type (see flat-union-clash-member.json).
> We want QAPISchemaObjectType.check() to make the same check,
> so we can later reduce some of the ad hoc checks.
>
> We already ensure that all branches of a flat union are qapi
> structs with no variants, at which point those members appear
> in the same JSON object as all non-variant members.  And we
> already have a map 'seen' of all non-variant members.  Still
> missing, until now, was a call to variant.type.check() during
> Variants.check() (to populate variant.type.members), and a
> new Variants.check_clash() which clones the seen map for each
> variant before checking that variant's members for clashes.
>
> Note that cloning 'seen' inside Variants.check_clash()
> resembles the one we just removed from Variants.check(); the
> difference here is that we are now checking for clashes
> among the qapi members of the variant type, rather than for
> a single clash with the variant tag name itself.
>
> In general, a non-empy type used as a branch of a flat union

non-empty

> cannot also be the base type of the flat union, so even though
> we are adding a call to variant.type.check() in order to
> populate variant.type.members, this is merely a case of
> gaining topological sorting of how types are visited (and
> type.check() is already set up to allow multiple calls due to
> base types).

Yes.

When the variant's type or one of its base types equals its owning type
or one of its base types, then each of this type's members clashes with
itself.  Your clash detection code will flag that.

Except when "this type" is the owning type.  Then the owning type
contains itself, which the QAPISchemaObjectType.check()'s cycle detector
flags before we get around to checking for member clashes.

Your patch actually does two things:

1. Extend the "object type cannot contain itself" check to cover its
   variant types in addition to base type.  This is the two-liner change
   to QAPISchemaObjectTypeVariants.check().

2. Extend the "object type cannot contain members with clashing names"
   to cover variant members.

They're related: you need the former's side effect to compute .members
to be able to do the latter.

Doing it in two separate patches *might* make explaining it in the
commit message easier.  See below.

> For simple unions, the same code happens to work by design,
> because of our synthesized wrapper classes (however, the
> wrapper has a single member 'data' which will never collide
> with the one non-variant member 'type', so it doesn't really
> matter).
>
> There is no impact to alternates, which intentionally do not
> need to call variants.check_clash() (there, at most one of
> the variant's branches will be an ObjectType, and even if one
> exists, we are not inlining the qapi members of that object
> into a parent object, the way we do for unions).

The number of object variants we support is immaterial, because variants
can't collide with each other.

I'd simplify to something like

    For alternates, there's nothing for a variant object type's members
    to clash with, and therefore no need to call variants.check_clash().

>                                                   However,
> since variants can have non-object branches, it did mean that
> the addition of v.type.check() in Variants.check() had to be
> conditional on visiting an object type.

"did mean" and "had to be"?  It still means, and it still has to be...

It has to be conditional because only object types' check() is prepared
for getting called multiple times.  All the other types' check() really
want to be called exactly once from QAPISchema.check().

The conditional can only be false for alternates.  When it's true for
alternates, the .check() isn't really needed as of now, but doesn't
hurt.

> No change to generated code.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v11: keep type.check() in check(), add comment to alternate
> v10: create new Variants.check_clash() rather than piggybacking
> on .check()
> v9: new patch, split off from v8 7/17

Patch looks good.

Let me have a stab at the commit message of the *unsplit* patch, use as
you see fit:

qapi: Check for collisions involving variant members

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

We already have a map 'seen' of all non-variant members.  We still need
to check for collisions between each variant type's members and the
non-variant ones.

To know the variant type's members, we need to call
variant.type.check().  This also detects when a type contains itself in
a variant, exactly like the existing base.check() detects when a type
contains itself as a base.

Slight complication: an alternate's variant can have arbitrary type, but
only an object type's check() may be called outside QAPISchema.check().
We could either skip the call for variants of alternates, or skip it for
non-object types.  Do the latter, because it's easier.

Then we can call each variant member's check_clash() with the
appropriate 'seen' map.  Since members of different variants can't
clash, We have to clone a fresh seen for each variant.  Wrap this in the
new helper method QAPISchemaObjectTypeVariants.check_clash().

Note that cloning 'seen' inside Variants.check_clash()
resembles the one we just removed from Variants.check(); the
difference here is that we are now checking for clashes
among the qapi members of the variant type, rather than for
a single clash with the variant tag name itself.

Note that by construction collisions can't actually happen for simple
unions: each variant's type is a wrapper with a single member 'data',
which will never collide with the only non-variant member 'type'.

For alternates, there's nothing for a variant object type's members to
clash with, and therefore no need to call variants.check_clash().

No change to generated code.

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

* Re: [Qemu-devel] [PATCH v11 13/28] qapi: Hoist tag collision check to Variants.check()
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 13/28] qapi: Hoist tag collision check to Variants.check() Eric Blake
@ 2015-11-11 13:56   ` Markus Armbruster
  2015-11-11 16:11     ` Eric Blake
  0 siblings, 1 reply; 72+ messages in thread
From: Markus Armbruster @ 2015-11-11 13:56 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Checking that a given QAPISchemaObjectTypeVariant.name is a
> member of the corresponding QAPISchemaEnumType of the owning
> QAPISchemaObjectTypeVariants.tag_member ensures that there are
> no collisions in the generated C union for those tag values
> (since the enum itself should have no collisions).
>
> However, this check was the only thing that Variant.check() was
> doing beyond the work of the superclass ObjectTypeMember.check(),

Since PATCH 05, actually.  Suggest to mention that.

> and resulted in a difference of the .check() signatures just to
> pass the enum type down.
>
> Simplify things by instead doing the tag name check as part of
> Variants.check(), at which point we can rely on inheritance
> instead of overriding Variant.check().
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v11: don't use tag_type local variable, rebase to v.type.check()
> v10: new patch
> ---
>  scripts/qapi.py | 7 ++-----
>  1 file changed, 2 insertions(+), 5 deletions(-)
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 296b9bb..c6f3fce 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -1059,7 +1059,8 @@ class QAPISchemaObjectTypeVariants(object):
>              self.tag_member = seen[self.tag_name]
>          assert isinstance(self.tag_member.type, QAPISchemaEnumType)
>          for v in self.variants:
> -            v.check(schema, self.tag_member.type)
> +            v.check(schema)
> +            assert v.name in self.tag_member.type.values
>              if isinstance(v.type, QAPISchemaObjectType):
>                  v.type.check(schema)
>
> @@ -1075,10 +1076,6 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
>      def __init__(self, name, typ):
>          QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
>
> -    def check(self, schema, tag_type):
> -        QAPISchemaObjectTypeMember.check(self, schema)
> -        assert self.name in tag_type.values
> -
>      # This function exists to support ugly simple union special cases
>      # TODO get rid of them, and drop the function
>      def simple_union_type(self):

QAPISchemaObjectTypeVariant is now an almost trivial variation of
QAPISchemaObjectTypeMember.  Differences:

* __init__() has no parameter optional

* Method simple_union_type(), which exists only to support pointless
  differences in code generation for simple unions, all marked TODO.

There's hope we can get rid of the class after the TODOs are resolved.

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

* Re: [Qemu-devel] [PATCH v11 17/28] cpu: Convert CpuInfo into flat union
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 17/28] cpu: Convert CpuInfo into flat union Eric Blake
@ 2015-11-11 14:13   ` Markus Armbruster
  0 siblings, 0 replies; 72+ messages in thread
From: Markus Armbruster @ 2015-11-11 14:13 UTC (permalink / raw)
  To: Eric Blake; +Cc: Paolo Bonzini, qemu-devel, Luiz Capitulino

Eric Blake <eblake@redhat.com> writes:

> The CpuInfo struct is used only by the 'query-cpus' output
> command, so we are free to modify it by adding fields (clients
> are already supposed to ignore unknown output fields), or by
> changing optional members to mandatory, while still keeping
> QMP wire compatibility with older versions of qemu.
>
> When qapi type CpuInfo was originally created for 0.14, we had
> no notion of a flat union, and instead just listed a bunch of
> optional fields with documentation about the mutually-exclusive
> choice of which instruction pointer field(s) would be provided
> for a given architecture.  But now that we have flat unions and
> introspection, it is better to segregate off which fields will
> be provided according to the actual architecture.  With this in
> place, we no longer need the fields to be optional, because the
> choice of the new 'arch' discriminator serves that role.
>
> This has an additional benefit: the old all-in-one struct was
> the only place in the code base that had a case-sensitive
> naming of members 'pc' vs. 'PC'.  Separating these spellings
> into different branches of the flat union will allow us to add
> restrictions against future case-insensitive collisions, since
> that is generally a poor interface practice.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v11: also fix qmp-commands.hx, improve commit message. If this
> misses 2.5 (likely), it will need updates to call out 2.6

I'd consider reordering the patches to get this one closer to the front
and into 2.5 if it was the only case of "using optional where we really
should use flat unions".  It isn't, so why bother :)

> v10: new patch
> ---
>  cpus.c           |  31 ++++++++------
>  hmp.c            |  30 +++++++++-----
>  qapi-schema.json | 120 ++++++++++++++++++++++++++++++++++++++++++++++---------
>  qmp-commands.hx  |   4 ++
>  4 files changed, 143 insertions(+), 42 deletions(-)
>
> diff --git a/cpus.c b/cpus.c
> index 877bd70..77ba15b 100644
> --- a/cpus.c
> +++ b/cpus.c
> @@ -1556,22 +1556,29 @@ CpuInfoList *qmp_query_cpus(Error **errp)
>          info->value->qom_path = object_get_canonical_path(OBJECT(cpu));
>          info->value->thread_id = cpu->thread_id;
>  #if defined(TARGET_I386)
> -        info->value->has_pc = true;
> -        info->value->pc = env->eip + env->segs[R_CS].base;
> +        info->value->arch = CPU_INFO_ARCH_X86;
> +        info->value->u.x86 = g_new0(CpuInfoX86, 1);
> +        info->value->u.x86->pc = env->eip + env->segs[R_CS].base;
>  #elif defined(TARGET_PPC)
> -        info->value->has_nip = true;
> -        info->value->nip = env->nip;
> +        info->value->arch = CPU_INFO_ARCH_PPC;
> +        info->value->u.ppc = g_new0(CpuInfoPpc, 1);
> +        info->value->u.ppc->nip = env->nip;
>  #elif defined(TARGET_SPARC)
> -        info->value->has_pc = true;
> -        info->value->pc = env->pc;
> -        info->value->has_npc = true;
> -        info->value->npc = env->npc;
> +        info->value->arch = CPU_INFO_ARCH_SPARC;
> +        info->value->u.sparc = g_new0(CpuInfoSPARC, 1);
> +        info->value->u.sparc->pc = env->pc;
> +        info->value->u.sparc->npc = env->npc;
>  #elif defined(TARGET_MIPS)
> -        info->value->has_PC = true;
> -        info->value->PC = env->active_tc.PC;
> +        info->value->arch = CPU_INFO_ARCH_MIPS;
> +        info->value->u.mips = g_new0(CpuInfoMips, 1);
> +        info->value->u.mips->PC = env->active_tc.PC;
>  #elif defined(TARGET_TRICORE)
> -        info->value->has_PC = true;
> -        info->value->PC = env->PC;
> +        info->value->arch = CPU_INFO_ARCH_TRICORE;
> +        info->value->u.tricore = g_new0(CpuInfoTricore, 1);
> +        info->value->u.tricore->PC = env->PC;
> +#else
> +        info->value->arch = CPU_INFO_ARCH_OTHER;
> +        info->value->u.other = g_new0(CpuInfoOther, 1);
>  #endif
>
>          /* XXX: waiting for the qapi to support GSList */
> diff --git a/hmp.c b/hmp.c
> index e1f854a..b992a29 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -310,17 +310,25 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict)
>
>          monitor_printf(mon, "%c CPU #%" PRId64 ":", active, cpu->value->CPU);
>
> -        if (cpu->value->has_pc) {
> -            monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->pc);
> -        }
> -        if (cpu->value->has_nip) {
> -            monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->nip);
> -        }
> -        if (cpu->value->has_npc) {
> -            monitor_printf(mon, " npc=0x%016" PRIx64, cpu->value->npc);
> -        }
> -        if (cpu->value->has_PC) {
> -            monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->PC);
> +        switch (cpu->value->arch) {
> +        case CPU_INFO_ARCH_X86:
> +            monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->u.x86->pc);
> +            break;
> +        case CPU_INFO_ARCH_PPC:
> +            monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->u.ppc->nip);
> +            break;
> +        case CPU_INFO_ARCH_SPARC:
> +            monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->u.sparc->pc);
> +            monitor_printf(mon, " npc=0x%016" PRIx64, cpu->value->u.sparc->npc);
> +            break;
> +        case CPU_INFO_ARCH_MIPS:
> +            monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.mips->PC);
> +            break;
> +        case CPU_INFO_ARCH_TRICORE:
> +            monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.tricore->PC);
> +            break;
> +        default:
> +            break;
>          }
>
>          if (cpu->value->halted) {

In C, the change complicates notation only a bit, and is offset by an
improvement in clarity.

> diff --git a/qapi-schema.json b/qapi-schema.json
> index 8c3a42a..30bcedf 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -742,43 +742,125 @@
>  { 'command': 'query-mice', 'returns': ['MouseInfo'] }
>
>  ##
> -# @CpuInfo:
> +# @CpuInfoArch:
>  #
> -# Information about a virtual CPU
> +# An enumeration of cpu types that enable additional information during
> +# @query-cpus.
> +#
> +# Since: 2.5
> +##
> +{ 'enum': 'CpuInfoArch',
> +  'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 'other' ] }
> +
> +##
> +# @CpuInfoBase:
> +#
> +# Common information about a virtual CPU
>  #
>  # @CPU: the index of the virtual CPU
>  #
> -# @current: this only exists for backwards compatible and should be ignored
> +# @current: this only exists for backwards compatibility and should be ignored
>  #
>  # @halted: true if the virtual CPU is in the halt state.  Halt usually refers
>  #          to a processor specific low power mode.
>  #
>  # @qom_path: path to the CPU object in the QOM tree (since 2.4)
>  #
> -# @pc: #optional If the target is i386 or x86_64, this is the 64-bit instruction
> -#                pointer.
> -#                If the target is Sparc, this is the PC component of the
> -#                instruction pointer.
> -#
> -# @nip: #optional If the target is PPC, the instruction pointer
> -#
> -# @npc: #optional If the target is Sparc, the NPC component of the instruction
> -#                 pointer
> -#
> -# @PC: #optional If the target is MIPS, the instruction pointer
> -#
>  # @thread_id: ID of the underlying host thread
>  #
> +# @arch: architecture of the cpu, which determines which additional fields
> +#        will be listed (since 2.5)
> +#
>  # Since: 0.14.0
>  #
>  # Notes: @halted is a transient state that changes frequently.  By the time the
>  #        data is sent to the client, the guest may no longer be halted.
>  ##
> -{ 'struct': 'CpuInfo',
> +{ 'struct': 'CpuInfoBase',
>    'data': {'CPU': 'int', 'current': 'bool', 'halted': 'bool',
> -           'qom_path': 'str',
> -           '*pc': 'int', '*nip': 'int', '*npc': 'int', '*PC': 'int',
> -           'thread_id': 'int'} }
> +           'qom_path': 'str', 'thread_id': 'int', 'arch': 'CpuInfoArch' } }
> +
> +##
> +# @CpuInfo:
> +#
> +# Information about a virtual CPU
> +#
> +# Since: 0.14.0
> +##
> +{ 'union': 'CpuInfo', 'base': 'CpuInfoBase', 'discriminator': 'arch',
> +  'data': { 'x86': 'CpuInfoX86',
> +            'sparc': 'CpuInfoSparc',
> +            'ppc': 'CpuInfoPpc',
> +            'mips': 'CpuInfoMips',
> +            'tricore': 'CpuInfoTricore',
> +            'other': 'CpuInfoOther' } }
> +
> +##
> +# @CpuInfoX86:
> +#
> +# Additional information about a virtual i386 or x86_64 CPU
> +#
> +# @pc: the 64-bit instruction pointer
> +#
> +# Since 2.5
> +##
> +{ 'struct': 'CpuInfoX86', 'data': { 'pc': 'int' } }
> +
> +##
> +# @CpuInfoSparc:
> +#
> +# Additional information about a virtual Sparc CPU
> +#
> +# @pc: the PC component of the instruction pointer
> +#
> +# @npc: the NPC component of the instruction pointer
> +#
> +# Since 2.5
> +##
> +{ 'struct': 'CpuInfoSparc', 'data': { 'pc': 'int', 'npc': 'int' } }
> +
> +##
> +# @CpuInfoPpc:
> +#
> +# Additional information about a virtual PPC CPU
> +#
> +# @nip: the instruction pointer
> +#
> +# Since 2.5
> +##
> +{ 'struct': 'CpuInfoPpc', 'data': { 'nip': 'int' } }
> +
> +##
> +# @CpuInfoMips:
> +#
> +# Additional information about a virtual MIPS CPU
> +#
> +# @PC: the instruction pointer
> +#
> +# Since 2.5
> +##
> +{ 'struct': 'CpuInfoMips', 'data': { 'PC': 'int' } }
> +
> +##
> +# @CpuInfoMips:
> +#
> +# Additional information about a virtual Tricore CPU
> +#
> +# @PC: the instruction pointer
> +#
> +# Since 2.5
> +##
> +{ 'struct': 'CpuInfoTricore', 'data': { 'PC': 'int' } }
> +
> +##
> +# @CpuInfoOther:
> +#
> +# No additional information is available about the virtual CPU
> +#
> +# Since 2.5
> +#
> +##
> +{ 'struct': 'CpuInfoOther', 'data': { } }
>
>  ##
>  # @query-cpus:

In contrast to C, the notational overhead is *much* larger in the QAPI
schema.  I hope we can fix that later on.

> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 7f85d40..66e0c39 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -2657,6 +2657,8 @@ Return a json-array. Each CPU is represented by a json-object, which contains:
>  - "current": true if this is the current CPU, false otherwise (json-bool)
>  - "halted": true if the cpu is halted, false otherwise (json-bool)
>  - "qom_path": path to the CPU object in the QOM tree (json-str)
> +- "arch": architecture of the cpu, which determines what additional
> +          keys will be present (json-str)
>  - Current program counter. The key's name depends on the architecture:
>       "pc": i386/x86_64 (json-int)
>       "nip": PPC (json-int)
> @@ -2674,6 +2676,7 @@ Example:
>              "current":true,
>              "halted":false,
>              "qom_path":"/machine/unattached/device[0]",
> +            "arch":"x86",
>              "pc":3227107138,
>              "thread_id":3134
>           },
> @@ -2682,6 +2685,7 @@ Example:
>              "current":false,
>              "halted":true,
>              "qom_path":"/machine/unattached/device[2]",
> +            "arch":"x86",
>              "pc":7108165,
>              "thread_id":3135
>           }

Thanks for remembering docs!

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

* Re: [Qemu-devel] [PATCH v11 18/28] qerror: more error_setg() usage
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 18/28] qerror: more error_setg() usage Eric Blake
  2015-11-11 13:26   ` Andreas Färber
@ 2015-11-11 14:21   ` Markus Armbruster
  2015-11-11 14:23     ` Andreas Färber
  2015-11-11 16:19     ` Eric Blake
  1 sibling, 2 replies; 72+ messages in thread
From: Markus Armbruster @ 2015-11-11 14:21 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, Jiri Pirko, Eduardo Habkost,
	open list:Block layer core, Michael S. Tsirkin, Jason Wang,
	qemu-devel, Scott Feldman, Paolo Bonzini, Andreas Färber,
	Richard Henderson

Eric Blake <eblake@redhat.com> writes:

> A few uses of error_set(ERROR_CLASS_GENERIC_ERROR) have snuck in
> since c6bd8c706.  Nuke them.

Doesn't really belong to this series, but that's okay.

> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v11: new patch
> ---
>  block.c                       |  3 +--
>  docs/writing-qmp-commands.txt | 20 +++++++++-----------
>  hw/i386/pc.c                  |  2 +-
>  hw/net/rocker/rocker.c        |  6 ++----
>  hw/net/rocker/rocker_of_dpa.c | 12 ++++--------
>  qom/object.c                  |  4 ++--
>  6 files changed, 19 insertions(+), 28 deletions(-)
>
> diff --git a/block.c b/block.c
> index e9f40dc..53a978a 100644
> --- a/block.c
> +++ b/block.c
> @@ -1795,8 +1795,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
>
>      ret = bdrv_flush(reopen_state->bs);
>      if (ret) {
> -        error_set(errp, ERROR_CLASS_GENERIC_ERROR, "Error (%s) flushing drive",
> -                  strerror(-ret));
> +        error_setg_errno(errp, -ret, "Error flushing drive");
>          goto error;
>      }
>
> diff --git a/docs/writing-qmp-commands.txt b/docs/writing-qmp-commands.txt
> index 8647cac..59aa77a 100644
> --- a/docs/writing-qmp-commands.txt
> +++ b/docs/writing-qmp-commands.txt
> @@ -210,7 +210,7 @@ if you don't see these strings, then something went wrong.
>  === Errors ===
>
>  QMP commands should use the error interface exported by the error.h header
> -file. Basically, errors are set by calling the error_set() function.
> +file. Basically, most errors are set by calling the error_setg() function.
>
>  Let's say we don't accept the string "message" to contain the word "love". If
>  it does contain it, we want the "hello-world" command to return an error:
> @@ -219,8 +219,7 @@ void qmp_hello_world(bool has_message, const char *message, Error **errp)
>  {
>      if (has_message) {
>          if (strstr(message, "love")) {
> -            error_set(errp, ERROR_CLASS_GENERIC_ERROR,
> -                      "the word 'love' is not allowed");
> +            error_setg(errp, "the word 'love' is not allowed");
>              return;
>          }
>          printf("%s\n", message);
> @@ -229,10 +228,8 @@ void qmp_hello_world(bool has_message, const char *message, Error **errp)
>      }
>  }
>
> -The first argument to the error_set() function is the Error pointer to pointer,
> -which is passed to all QMP functions. The second argument is a ErrorClass
> -value, which should be ERROR_CLASS_GENERIC_ERROR most of the time (more
> -details about error classes are given below). The third argument is a human
> +The first argument to the error_setg() function is the Error pointer
> +to pointer, which is passed to all QMP functions. The next argument is a human
>  description of the error, this is a free-form printf-like string.
>
>  Let's test the example above. Build qemu, run it as defined in the "Testing"
> @@ -249,8 +246,9 @@ The QMP server's response should be:
>      }
>  }
>
> -As a general rule, all QMP errors should use ERROR_CLASS_GENERIC_ERROR. There
> -are two exceptions to this rule:
> +As a general rule, all QMP errors should use ERROR_CLASS_GENERIC_ERROR
> +(done by default when using error_setg()). There are two exceptions to
> +this rule:
>
>   1. A non-generic ErrorClass value exists* for the failure you want to report
>      (eg. DeviceNotFound)
> @@ -259,8 +257,8 @@ are two exceptions to this rule:
>      want to report, hence you have to add a new ErrorClass value so that they
>      can check for it
>
> -If the failure you want to report doesn't fall in one of the two cases above,
> -just report ERROR_CLASS_GENERIC_ERROR.
> +If the failure you want to report falls into one of the two cases above,
> +use error_set() with a second argument of an ErrorClass value.
>
>   * All existing ErrorClass values are defined in the qapi-schema.json file
>

Thanks a lot for this doc update!

> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> index 0cb8afd..dfb57a8 100644
> --- a/hw/i386/pc.c
> +++ b/hw/i386/pc.c
> @@ -1795,7 +1795,7 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
>          return;
>      }
>      if (value > (1ULL << 32)) {
> -        error_set(&error, ERROR_CLASS_GENERIC_ERROR,
> +        error_setg(&error,
>                    "Machine option 'max-ram-below-4g=%"PRIu64
>                    "' expects size less than or equal to 4G", value);

Indentation is now off.  Can tidy up in my tree.

>          error_propagate(errp, error);
[Rest snipped, it looks good]

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

* Re: [Qemu-devel] [PATCH v11 18/28] qerror: more error_setg() usage
  2015-11-11 14:21   ` Markus Armbruster
@ 2015-11-11 14:23     ` Andreas Färber
  2015-11-11 15:51       ` Eric Blake
  2015-11-11 16:19     ` Eric Blake
  1 sibling, 1 reply; 72+ messages in thread
From: Andreas Färber @ 2015-11-11 14:23 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Jiri Pirko, Eduardo Habkost, qemu-block,
	Michael S. Tsirkin, Jason Wang, qemu-devel, Scott Feldman,
	Paolo Bonzini, Richard Henderson

Am 11.11.2015 um 15:21 schrieb Markus Armbruster:
> Eric Blake <eblake@redhat.com> writes:
> 
>> A few uses of error_set(ERROR_CLASS_GENERIC_ERROR) have snuck in
>> since c6bd8c706.  Nuke them.
> 
> Doesn't really belong to this series, but that's okay.

It seemed to avoid a GENERIC_ERROR -> GENERICERROR change in the
following patch, so it is related.

Cheers,
Andreas

-- 
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton; HRB 21284 (AG Nürnberg)

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

* Re: [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values Eric Blake
  2015-11-11 13:29   ` Andreas Färber
@ 2015-11-11 14:50   ` Markus Armbruster
  2015-11-11 16:03     ` Eric Blake
  2015-11-11 16:06     ` Eric Blake
  2015-11-13 17:46   ` Eric Blake
  2 siblings, 2 replies; 72+ messages in thread
From: Markus Armbruster @ 2015-11-11 14:50 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, Peter Maydell, open list:Block layer core,
	Michael S. Tsirkin, Jason Wang, qemu-devel, Michael Roth,
	Gerd Hoffmann, Amit Shah, Luiz Capitulino, Andreas Färber

Eric Blake <eblake@redhat.com> writes:

> When munging enum values, the fact that we were passing the entire
> prefix + value through camel_to_upper() meant that enum values
> spelled with CamelCase could be turned into CAMEL_CASE.  However,
> this provides a potential collision (both OneTwo and One-Two would
> munge into ONE_TWO).  By changing the generation of enum constants
> to always be prefix + '_' + c_name(value).upper(), we can avoid
> any risk of collisions (if we can also ensure no case collisions,
> in the next patch) without having to think about what the
> heuristics in camel_to_upper() will actually do to the value.

This is the good part: the rules for clashes become much simpler.

Bonus: the implementation for detecting them will be simple, too.

> Thankfully, only two enums are affected: ErrorClass and InputButton.

By convention (see CODING_STYLE), we use CamelCase for type names, and
nothing else.

Only enums violating this naming convention can be affected.  The bad
part: they exist.

InputButton has two camels: WheelUp and WheelDown.  The C enumeration
constants change from INPUT_BUTTON_WHEEL_UP/WHEEL_DOWN to
INPUT_BUTTON_WHEELUP/WHEELDOWN.  Not exactly an improvement, but one,
there are just 21 occurences in 11 files, and two, I think we can still
fix the enumeration to "lower case with dash", as it's only used by
x-input-send-event.

ErrorClass's members are all camels.  The C enumeration constants change
as follows

    ERROR_CLASS_GENERIC_ERROR           ERROR_CLASS_GENERICERROR
    ERROR_CLASS_COMMAND_NOT_FOUND       ERROR_CLASS_COMMANDNOTFOUND
    ERROR_CLASS_DEVICE_ENCRYPTED        ERROR_CLASS_DEVICEENCRYPTED
    ERROR_CLASS_DEVICE_NOT_ACTIVE       ERROR_CLASS_DEVICENOTACTIVE
    ERROR_CLASS_DEVICE_NOT_FOUND        ERROR_CLASS_DEVICENOTFOUND
    ERROR_CLASS_KVM_MISSING_CAP         ERROR_CLASS_KVMMISSINGCAP

Again, not an improvement, but perhaps tolerabe, because these constants
aren't used much anymore: 55 occurences in 20 files.

If we find it not tolerable, we can manually add aliases: rename the
QAPI type out of the way, say 'QAPIErrorClass', then stick

    typedef enum ErrorClass {
        ERROR_CLASS_GENERIC_ERROR = QAPI_ERROR_CLASS_GENERICERROR,
        ERROR_CLASS_COMMAND_NOT_FOUND = QAPI_ERROR_CLASS_COMMANDNOTFOUND,
        ERROR_CLASS_DEVICE_ENCRYPTED = QAPI_ERROR_CLASS_DEVICEENCRYPTED,
        ERROR_CLASS_DEVICE_NOT_ACTIVE = QAPI_ERROR_CLASS_DEVICENOTACTIVE,
        ERROR_CLASS_DEVICE_NOT_FOUND = QAPI_ERROR_CLASS_DEVICENOTFOUND,
        ERROR_CLASS_KVM_MISSING_CAP = QAPI_ERROR_CLASS_KVMMISSINGCAP,
    } ErrorClass;

into error.h with a suitable comment.

Opinions?

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

* Re: [Qemu-devel] [PATCH v11 20/28] qapi: Forbid case-insensitive clashes
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 20/28] qapi: Forbid case-insensitive clashes Eric Blake
@ 2015-11-11 14:53   ` Markus Armbruster
  2015-11-13  5:32     ` Eric Blake
  0 siblings, 1 reply; 72+ messages in thread
From: Markus Armbruster @ 2015-11-11 14:53 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> In general, designing user interfaces that rely on case
> distinction is poor practice.  Another benefit of enforcing
> a restriction against case-insensitive clashes is that we
> no longer have to worry about the situation of enum values
> that could be distinguished by case if mapped by c_name(),
> but which cannot be distinguished when mapped to C as
> ALL_CAPS by camel_to_upper().

With PATCH 19, they're mapped by c_name(N).upper().

>                                Thus, having the generator
> look for case collisions up front will prevent developers
> from worrying whether different munging rules for member
> names compared to enum values as a discriminator will cause
> any problems in qapi unions.
>
> There is also the possibility that we may want to add a
> future extension to QMP of teaching it to be case-insensitive
> (the user could request command 'Quit' instead of 'quit', or
> could spell a struct field as 'CPU' instead of 'cpu').  But
> for that to be a practical extension, we cannot break
> backwards compatibility with any existing struct that was
> already relying on case sensitivity.  Fortunately, after the
> previous patch cleaned up CpuInfo, there are no such existing
> qapi structs.  Of course, the idea of a future extension is
> not as strong of a reason to make the change.
>
> At any rate, it is easier to be strict now, and relax things
> later if we find a reason to need case-sensitive QMP members,
> than it would be to wish we had the restriction in place.

Suggest to briefly mention the new test.

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

Patch looks good.

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

* Re: [Qemu-devel] [PATCH v11 11/28] qapi: Check for qapi collisions of flat union branches
  2015-11-11 13:42   ` Markus Armbruster
@ 2015-11-11 15:49     ` Eric Blake
  0 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11 15:49 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 11/11/2015 06:42 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:

> Your patch actually does two things:
> 
> 1. Extend the "object type cannot contain itself" check to cover its
>    variant types in addition to base type.  This is the two-liner change
>    to QAPISchemaObjectTypeVariants.check().
> 
> 2. Extend the "object type cannot contain members with clashing names"
>    to cover variant members.
> 
> They're related: you need the former's side effect to compute .members
> to be able to do the latter.
> 
> Doing it in two separate patches *might* make explaining it in the
> commit message easier.  See below.

Interesting observation.  I can split if you still think it is worth it;
otherwise, I think your wording makes both changes obvious:


> Let me have a stab at the commit message of the *unsplit* patch, use as
> you see fit:
> 
> qapi: Check for collisions involving variant members
> 
> Right now, our ad hoc parser ensures that we cannot have a flat union
> that introduces any members that would clash with non-variant members
> inherited from the union's base type (see flat-union-clash-member.json).
> We want QAPISchemaObjectType.check() to make the same check, so we can
> later reduce some of the ad hoc checks.
> 
> We already have a map 'seen' of all non-variant members.  We still need
> to check for collisions between each variant type's members and the
> non-variant ones.
> 
> To know the variant type's members, we need to call
> variant.type.check().  This also detects when a type contains itself in
> a variant, exactly like the existing base.check() detects when a type
> contains itself as a base.

If I split, this paragraph would go in the first patch (adding the
.check() for cycle detection).

> 
> Slight complication: an alternate's variant can have arbitrary type, but
> only an object type's check() may be called outside QAPISchema.check().
> We could either skip the call for variants of alternates, or skip it for
> non-object types.  Do the latter, because it's easier.

I actually have some churn on this; later in 22/28, I'm stuck making
Variants.check() do something for unions only, and had to reintroduce an
'if seen:' conditional, at which point I moved the v.type.check() back
to unions only.  I guess your review of that patch may determine whether
I minimize churn back here, or add a 'for now' phrase to the commit
message, or just not worry about it.

> 
> Then we can call each variant member's check_clash() with the
> appropriate 'seen' map.  Since members of different variants can't
> clash, We have to clone a fresh seen for each variant.  Wrap this in the
> new helper method QAPISchemaObjectTypeVariants.check_clash().
> 
> Note that cloning 'seen' inside Variants.check_clash()
> resembles the one we just removed from Variants.check(); the
> difference here is that we are now checking for clashes
> among the qapi members of the variant type, rather than for
> a single clash with the variant tag name itself.
> 
> Note that by construction collisions can't actually happen for simple
> unions: each variant's type is a wrapper with a single member 'data',
> which will never collide with the only non-variant member 'type'.
> 
> For alternates, there's nothing for a variant object type's members to
> clash with, and therefore no need to call variants.check_clash().
> 
> No change to generated code.
> 

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

* Re: [Qemu-devel] [PATCH v11 18/28] qerror: more error_setg() usage
  2015-11-11 14:23     ` Andreas Färber
@ 2015-11-11 15:51       ` Eric Blake
  0 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11 15:51 UTC (permalink / raw)
  To: Andreas Färber, Markus Armbruster
  Cc: Kevin Wolf, Jiri Pirko, Eduardo Habkost, qemu-block,
	Michael S. Tsirkin, Jason Wang, qemu-devel, Scott Feldman,
	Paolo Bonzini, Richard Henderson

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

On 11/11/2015 07:23 AM, Andreas Färber wrote:
> Am 11.11.2015 um 15:21 schrieb Markus Armbruster:
>> Eric Blake <eblake@redhat.com> writes:
>>
>>> A few uses of error_set(ERROR_CLASS_GENERIC_ERROR) have snuck in
>>> since c6bd8c706.  Nuke them.
>>
>> Doesn't really belong to this series, but that's okay.
> 
> It seemed to avoid a GENERIC_ERROR -> GENERICERROR change in the
> following patch, so it is related.

Indeed, that's why it ended up here. But I'm also okay if we want to
detach this one from the series and apply it now through the qerror tree
for 2.5, as it includes a doc update and should be safe even during hard
freeze.

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

* Re: [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values
  2015-11-11 14:50   ` Markus Armbruster
@ 2015-11-11 16:03     ` Eric Blake
  2015-11-11 17:11       ` Markus Armbruster
  2015-11-12  8:29       ` Gerd Hoffmann
  2015-11-11 16:06     ` Eric Blake
  1 sibling, 2 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11 16:03 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Peter Maydell, open list:Block layer core,
	Michael S. Tsirkin, Jason Wang, qemu-devel, Michael Roth,
	Gerd Hoffmann, Amit Shah, Luiz Capitulino, Andreas Färber

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

[hmm, wonder why scripts/get-maintainer.pl didn't loop in Gerd to the
patch itself]

On 11/11/2015 07:50 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> When munging enum values, the fact that we were passing the entire
>> prefix + value through camel_to_upper() meant that enum values
>> spelled with CamelCase could be turned into CAMEL_CASE.  However,
>> this provides a potential collision (both OneTwo and One-Two would
>> munge into ONE_TWO).  By changing the generation of enum constants
>> to always be prefix + '_' + c_name(value).upper(), we can avoid
>> any risk of collisions (if we can also ensure no case collisions,
>> in the next patch) without having to think about what the
>> heuristics in camel_to_upper() will actually do to the value.
> 
> This is the good part: the rules for clashes become much simpler.
> 
> Bonus: the implementation for detecting them will be simple, too.
> 
>> Thankfully, only two enums are affected: ErrorClass and InputButton.

Visiting just InputButton in this email.

> 
> By convention (see CODING_STYLE), we use CamelCase for type names, and
> nothing else.
> 
> Only enums violating this naming convention can be affected.  The bad
> part: they exist.
> 
> InputButton has two camels: WheelUp and WheelDown.  The C enumeration
> constants change from INPUT_BUTTON_WHEEL_UP/WHEEL_DOWN to
> INPUT_BUTTON_WHEELUP/WHEELDOWN.  Not exactly an improvement, but one,
> there are just 21 occurences in 11 files, and two, I think we can still
> fix the enumeration to "lower case with dash", as it's only used by
> x-input-send-event.

The InputButton type has existed since 2.0; which is then part of the
'InputBtnEvent' struct, then the 'InputEvent' union, also since 2.0.  I
can't easily tell if it was only used internally at that point, or if we
exposed it through the command line (even if we didn't expose it through
QMP); but I concur with your reading that in QMP it is only used via
'x-input-send-event' (since 2.2, but the x- prefix gives us freedom).
[Oh, and I just spotted a typo; 'InputAxis' has a copy-and-paste doc
typo calling it 'InputButton']

If desired, I can prepare an alternate patch that adds the dash to the
qapi enum definition, to see what we think.

But meanwhile, look at some of the lines in the patch:

> diff --git a/ui/sdl.c b/ui/sdl.c
> index 570cb99..2678611 100644
> --- a/ui/sdl.c
> +++ b/ui/sdl.c
> @@ -469,8 +469,8 @@ static void sdl_send_mouse_event(int dx, int dy, int x, int y, int state)
>          [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT),
>          [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE),
>          [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT),
> -        [INPUT_BUTTON_WHEEL_UP] = SDL_BUTTON(SDL_BUTTON_WHEELUP),
> -        [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
> +        [INPUT_BUTTON_WHEELUP] = SDL_BUTTON(SDL_BUTTON_WHEELUP),
> +        [INPUT_BUTTON_WHEELDOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),

Since SDL already spells the names without space, it's not the end of
the world if we do likewise.

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

* Re: [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values
  2015-11-11 14:50   ` Markus Armbruster
  2015-11-11 16:03     ` Eric Blake
@ 2015-11-11 16:06     ` Eric Blake
  1 sibling, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11 16:06 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Peter Maydell, open list:Block layer core,
	Michael S. Tsirkin, Jason Wang, qemu-devel, Michael Roth,
	Gerd Hoffmann, Amit Shah, Luiz Capitulino, Andreas Färber

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

On 11/11/2015 07:50 AM, Markus Armbruster wrote:

>> Thankfully, only two enums are affected: ErrorClass and InputButton.

[Visiting just ErrorClass in this email]

> 
> By convention (see CODING_STYLE), we use CamelCase for type names, and
> nothing else.
> 
> Only enums violating this naming convention can be affected.  The bad
> part: they exist.
> 

> 
> ErrorClass's members are all camels.  The C enumeration constants change
> as follows
> 
>     ERROR_CLASS_GENERIC_ERROR           ERROR_CLASS_GENERICERROR
>     ERROR_CLASS_COMMAND_NOT_FOUND       ERROR_CLASS_COMMANDNOTFOUND
>     ERROR_CLASS_DEVICE_ENCRYPTED        ERROR_CLASS_DEVICEENCRYPTED
>     ERROR_CLASS_DEVICE_NOT_ACTIVE       ERROR_CLASS_DEVICENOTACTIVE
>     ERROR_CLASS_DEVICE_NOT_FOUND        ERROR_CLASS_DEVICENOTFOUND
>     ERROR_CLASS_KVM_MISSING_CAP         ERROR_CLASS_KVMMISSINGCAP
> 
> Again, not an improvement, but perhaps tolerabe, because these constants
> aren't used much anymore: 55 occurences in 20 files.
> 
> If we find it not tolerable, we can manually add aliases: rename the
> QAPI type out of the way, say 'QAPIErrorClass', then stick
> 
>     typedef enum ErrorClass {
>         ERROR_CLASS_GENERIC_ERROR = QAPI_ERROR_CLASS_GENERICERROR,
>         ERROR_CLASS_COMMAND_NOT_FOUND = QAPI_ERROR_CLASS_COMMANDNOTFOUND,
>         ERROR_CLASS_DEVICE_ENCRYPTED = QAPI_ERROR_CLASS_DEVICEENCRYPTED,
>         ERROR_CLASS_DEVICE_NOT_ACTIVE = QAPI_ERROR_CLASS_DEVICENOTACTIVE,
>         ERROR_CLASS_DEVICE_NOT_FOUND = QAPI_ERROR_CLASS_DEVICENOTFOUND,
>         ERROR_CLASS_KVM_MISSING_CAP = QAPI_ERROR_CLASS_KVMMISSINGCAP,
>     } ErrorClass;
> 
> into error.h with a suitable comment.

Interesting hack.  Certainly more legible, and I'm willing to attempt
such a rename.

As far as I'm concerned, though, this does not fix a bug, so much as
make it easier for later qapi patches to be more robust.  So any
renaming we do is not under pressure to have to make it in by 2.5.
We've got time to think about it.

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

* Re: [Qemu-devel] [PATCH v11 13/28] qapi: Hoist tag collision check to Variants.check()
  2015-11-11 13:56   ` Markus Armbruster
@ 2015-11-11 16:11     ` Eric Blake
  2015-11-11 17:03       ` Markus Armbruster
  0 siblings, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-11 16:11 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 11/11/2015 06:56 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Checking that a given QAPISchemaObjectTypeVariant.name is a
>> member of the corresponding QAPISchemaEnumType of the owning
>> QAPISchemaObjectTypeVariants.tag_member ensures that there are
>> no collisions in the generated C union for those tag values
>> (since the enum itself should have no collisions).
>>
>> However, this check was the only thing that Variant.check() was
>> doing beyond the work of the superclass ObjectTypeMember.check(),
> 
> Since PATCH 05, actually.  Suggest to mention that.

I debated about munging patch 5 or 6 to actually make this change there;
but decided that separate is just fine.  But yes, mentioning the earlier
commit title will help.

>> @@ -1075,10 +1076,6 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
>>      def __init__(self, name, typ):
>>          QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
>>
>> -    def check(self, schema, tag_type):
>> -        QAPISchemaObjectTypeMember.check(self, schema)
>> -        assert self.name in tag_type.values
>> -
>>      # This function exists to support ugly simple union special cases
>>      # TODO get rid of them, and drop the function
>>      def simple_union_type(self):
> 
> QAPISchemaObjectTypeVariant is now an almost trivial variation of
> QAPISchemaObjectTypeMember.  Differences:
> 
> * __init__() has no parameter optional
> 
> * Method simple_union_type(), which exists only to support pointless
>   differences in code generation for simple unions, all marked TODO.
> 
> There's hope we can get rid of the class after the TODOs are resolved.

Nope. Because in 15/28 I add back in a non-trivial difference of "role =
'branch'" compared to the superclass "role = 'member'", which affects
the quality of error messages using .describe().

That, and I still have no idea how to get rid of the TODO (we know how
to convert simple unions to flat unions, but the conversion is an
either-or choice: either we keep the C layout the same but change {} in
the JSON representation, or we keep QMP the same but affect the C layout
- so fixing the TODO has to resolve that discrepancy, and may end up
touching lots of code).

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

* Re: [Qemu-devel] [PATCH v11 18/28] qerror: more error_setg() usage
  2015-11-11 14:21   ` Markus Armbruster
  2015-11-11 14:23     ` Andreas Färber
@ 2015-11-11 16:19     ` Eric Blake
  2015-11-11 17:31       ` Markus Armbruster
  1 sibling, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-11 16:19 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Jiri Pirko, Eduardo Habkost,
	open list:Block layer core, Michael S. Tsirkin, Jason Wang,
	qemu-devel, Scott Feldman, Paolo Bonzini, Andreas Färber,
	Richard Henderson

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

On 11/11/2015 07:21 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> A few uses of error_set(ERROR_CLASS_GENERIC_ERROR) have snuck in
>> since c6bd8c706.  Nuke them.
> 
> Doesn't really belong to this series, but that's okay.

If you're going to modify this for 2.5 inclusion through your qerror
tree, you may want to change the description to:

A few uses of error_set(ERROR_CLASS_GENERIC_ERROR) were missed in
c6bd8c706, or have snuck in since.  Nuke them.

>> +++ b/hw/i386/pc.c
>> @@ -1795,7 +1795,7 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
>>          return;
>>      }
>>      if (value > (1ULL << 32)) {
>> -        error_set(&error, ERROR_CLASS_GENERIC_ERROR,
>> +        error_setg(&error,
>>                    "Machine option 'max-ram-below-4g=%"PRIu64
>>                    "' expects size less than or equal to 4G", value);
> 
> Indentation is now off.  Can tidy up in my tree.
> 
>>          error_propagate(errp, error);
> [Rest snipped, it looks good]
> 

There's also the question if we want to address the ErrorClass name
munging of 19/28 by adding your idea of an aliasing typedef in error.h;
if so, should I prepare a smaller patch series of both of those changes
for consideration for 2.5?

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

* Re: [Qemu-devel] [PATCH v11 21/28] qapi: Convert qtype_code into qapi enum type
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 21/28] qapi: Convert qtype_code into qapi enum type Eric Blake
@ 2015-11-11 16:42   ` Markus Armbruster
  2015-11-11 17:03     ` Eric Blake
  0 siblings, 1 reply; 72+ messages in thread
From: Markus Armbruster @ 2015-11-11 16:42 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, Michael Roth, qemu-devel, open list:Block layer core,
	Luiz Capitulino

Eric Blake <eblake@redhat.com> writes:

> What's more meta than using qapi to define qapi? :)
>
> Convert qtype_code into a full-fledged[*] builtin qapi enum type,
> so that a subsequent patch can then use it as the discriminator
> type of qapi alternate types.  Doing so is easiest when renaming
> it to qapi conventions, as QTypeCode.

Out of curiosity: why does the rename make the conversion easier?

If we rename anyway, what about renaming to QType?  Hmm, we burned that
on a struct we use only internally in qobject/.  Oh well.

>                                        Fortunately, there are not
> many places in the tree that were actually spelling the type name
> out, and the judicious use of 'prefix' in the qapi defintion

definition

> avoids churn to the spelling of the enum constants.
>
> To avoid circular definitions, we have to flip the order of
> inclusion between "qobject.h" vs. "qapi-types.h".  Back in commit
> 28770e0, we had the latter include the former, so that we could
> use 'QObject *' for our implementation of 'any'.  But that usage
> also works with only a forward declaration, whereas the
> definition of QType requires QTypeCode to be a complete type.
>
> [*] The type has to be builtin, rather than declared in
> qapi/common.json, because we want to use it for alternates even
> when common.json is not included. But since it is the first
> builtin enum type, we have to add special cases to qapi-types
> and qapi-visit to only emit definitions once, even when two
> qapi files are being compiled into the same binary (the way we
> already handled builtin list types like 'intList').  We may
> need to revisit how multiple qapi files share common types,
> but that's a project for another day.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v11: new patch
> ---
>  block/qapi.c                             |  4 ++--
>  docs/qapi-code-gen.txt                   |  1 +
>  include/hw/qdev-core.h                   |  2 +-
>  include/qapi/qmp/qobject.h               | 19 +++----------------
>  qobject/qdict.c                          |  2 +-
>  scripts/qapi-types.py                    | 13 ++++++++++---
>  scripts/qapi-visit.py                    | 10 ++++++++--
>  scripts/qapi.py                          |  7 ++++++-
>  tests/qapi-schema/alternate-empty.out    |  2 ++
>  tests/qapi-schema/comments.out           |  2 ++
>  tests/qapi-schema/empty.out              |  2 ++
>  tests/qapi-schema/event-case.out         |  2 ++
>  tests/qapi-schema/flat-union-empty.out   |  2 ++
>  tests/qapi-schema/ident-with-escape.out  |  2 ++
>  tests/qapi-schema/include-relpath.out    |  2 ++
>  tests/qapi-schema/include-repetition.out |  2 ++
>  tests/qapi-schema/include-simple.out     |  2 ++
>  tests/qapi-schema/indented-expr.out      |  2 ++
>  tests/qapi-schema/qapi-schema-test.out   |  2 ++
>  tests/qapi-schema/union-clash-data.out   |  2 ++
>  tests/qapi-schema/union-empty.out        |  2 ++
>  21 files changed, 58 insertions(+), 26 deletions(-)
>
> diff --git a/block/qapi.c b/block/qapi.c
> index ec0f513..4211f11 100644
> --- a/block/qapi.c
> +++ b/block/qapi.c
> @@ -539,7 +539,7 @@ static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation,
>      int i = 0;
>
>      for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) {
> -        qtype_code type = qobject_type(entry->value);
> +        QTypeCode type = qobject_type(entry->value);
>          bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
>          const char *format = composite ? "%*s[%i]:\n" : "%*s[%i]: ";
>
> @@ -557,7 +557,7 @@ static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
>      const QDictEntry *entry;
>
>      for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) {
> -        qtype_code type = qobject_type(entry->value);
> +        QTypeCode type = qobject_type(entry->value);
>          bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
>          const char *format = composite ? "%*s%s:\n" : "%*s%s: ";
>          char key[strlen(entry->key) + 1];
> diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
> index 54a6a7b..35301c5 100644
> --- a/docs/qapi-code-gen.txt
> +++ b/docs/qapi-code-gen.txt
> @@ -163,6 +163,7 @@ The following types are predefined, and map to C as follows:
>                         accepts size suffixes
>    bool      bool       JSON true or false
>    any       QObject *  any JSON value
> +  QTypeCode QTypeCode  JSON string of enum QTypeCode values

QTypeCode is currently used only internally, so the JSON values don't
matter.  I don't expect that to change.  However, we either enforce
internal use somehow, or document the JSON values.  Documenting them is
easier.

In short, your patch is fine.

>
>
>  === Includes ===
> diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
> index 8057aed..8e7df8e 100644
> --- a/include/hw/qdev-core.h
> +++ b/include/hw/qdev-core.h
> @@ -239,7 +239,7 @@ struct Property {
>      PropertyInfo *info;
>      int          offset;
>      uint8_t      bitnr;
> -    qtype_code   qtype;
> +    QTypeCode    qtype;
>      int64_t      defval;
>      int          arrayoffset;
>      PropertyInfo *arrayinfo;
> diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h
> index 4b96ed5..8d6322b 100644
> --- a/include/qapi/qmp/qobject.h
> +++ b/include/qapi/qmp/qobject.h
> @@ -34,23 +34,10 @@
>
>  #include <stddef.h>
>  #include <assert.h>
> -
> -typedef enum {
> -    QTYPE_NONE,    /* sentinel value, no QObject has this type code */
> -    QTYPE_QNULL,
> -    QTYPE_QINT,
> -    QTYPE_QSTRING,
> -    QTYPE_QDICT,
> -    QTYPE_QLIST,
> -    QTYPE_QFLOAT,
> -    QTYPE_QBOOL,
> -    QTYPE_MAX,
> -} qtype_code;
> -
> -struct QObject;
> +#include "qapi-types.h"
>
>  typedef struct QType {
> -    qtype_code code;
> +    QTypeCode code;
>      void (*destroy)(struct QObject *);
>  } QType;
>
   typedef struct QObject {
       const QType *type;
       size_t refcnt;
   } QObject;

Note: typedef name QObject still defined here.

> @@ -101,7 +88,7 @@ static inline void qobject_decref(QObject *obj)
>  /**
>   * qobject_type(): Return the QObject's type
>   */
> -static inline qtype_code qobject_type(const QObject *obj)
> +static inline QTypeCode qobject_type(const QObject *obj)
>  {
>      assert(obj->type != NULL);
>      return obj->type->code;
> diff --git a/qobject/qdict.c b/qobject/qdict.c
> index 2d67bf1..92915f4 100644
> --- a/qobject/qdict.c
> +++ b/qobject/qdict.c
> @@ -185,7 +185,7 @@ size_t qdict_size(const QDict *qdict)
>   * qdict_get_obj(): Get a QObject of a specific type
>   */
>  static QObject *qdict_get_obj(const QDict *qdict, const char *key,
> -                              qtype_code type)
> +                              QTypeCode type)
>  {
>      QObject *obj;
>
> diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
> index 2f2f7df..93e905a 100644
> --- a/scripts/qapi-types.py
> +++ b/scripts/qapi-types.py
> @@ -233,8 +233,14 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
>          self.defn += gen_type_cleanup(name)
>
>      def visit_enum_type(self, name, info, values, prefix):
> -        self._fwdecl += gen_enum(name, values, prefix)
> -        self._fwdefn += gen_enum_lookup(name, values, prefix)
> +        # Special case for our lone builtin enum type
> +        if name == 'QTypeCode':

Would "if not info" work?  Same in qapi-visit.py below.

> +            self._btin += gen_enum(name, values, prefix)
> +            if do_builtins:
> +                self.defn += gen_enum_lookup(name, values, prefix)
> +        else:
> +            self._fwdecl += gen_enum(name, values, prefix)
> +            self._fwdefn += gen_enum_lookup(name, values, prefix)
>
>      def visit_array_type(self, name, info, element_type):
>          if isinstance(element_type, QAPISchemaBuiltinType):
> @@ -319,7 +325,8 @@ fdef.write(mcgen('''
>  fdecl.write(mcgen('''
>  #include <stdbool.h>
>  #include <stdint.h>
> -#include "qapi/qmp/qobject.h"
> +
> +typedef struct QObject QObject;

Typedef name QObject now also defined here.  GCC accepts this silently
without -Wpedantic, but other compilers might not.  Whether we care for
such compilers or not, defining things in exactly one place is neater.

Possible fixes:

* Drop the typedef from qobject.h

* Don't add it to qapi-types.h, and use struct QObject there

>  '''))
>
>  schema = QAPISchema(input_file)
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 94cd113..6f0b4e1 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -347,8 +347,14 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
>                      isinstance(entity, QAPISchemaObjectType))
>
>      def visit_enum_type(self, name, info, values, prefix):
> -        self.decl += gen_visit_decl(name, scalar=True)
> -        self.defn += gen_visit_enum(name)
> +        # Special case for our lone builtin enum type
> +        if name == 'QTypeCode':
> +            self._btin += gen_visit_decl(name, scalar=True)
> +            if do_builtins:
> +                self.defn += gen_visit_enum(name)
> +        else:
> +            self.decl += gen_visit_decl(name, scalar=True)
> +            self.defn += gen_visit_enum(name)
>
>      def visit_array_type(self, name, info, element_type):
>          decl = gen_visit_decl(name)
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index ed7a32b..d4ef08e 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -33,7 +33,7 @@ builtin_types = {
>      'uint32':   'QTYPE_QINT',
>      'uint64':   'QTYPE_QINT',
>      'size':     'QTYPE_QINT',
> -    'any':      None,           # any qtype_code possible, actually
> +    'any':      None,           # any QTypeCode possible, actually
>  }
>

Should we list QTypeCode here?

>  # Whitelist of commands allowed to return a non-dictionary
> @@ -1243,6 +1243,11 @@ class QAPISchema(object):
>          self.the_empty_object_type = QAPISchemaObjectType(':empty', None, None,
>                                                            [], None)
>          self._def_entity(self.the_empty_object_type)
> +        self._def_entity(QAPISchemaEnumType('QTypeCode', None,
> +                                            ['none', 'qnull', 'qint',
> +                                             'qstring', 'qdict', 'qlist',
> +                                             'qfloat', 'qbool'],
> +                                            'QTYPE'))
>
>      def _make_implicit_enum_type(self, name, info, values):
>          name = name + 'Kind'   # Use namespace reserved by add_name()
[Trivial changes to expected test output snipped]

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

* Re: [Qemu-devel] [PATCH v11 21/28] qapi: Convert qtype_code into qapi enum type
  2015-11-11 16:42   ` Markus Armbruster
@ 2015-11-11 17:03     ` Eric Blake
  2015-11-12 13:16       ` Markus Armbruster
  0 siblings, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-11 17:03 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Michael Roth, qemu-devel, open list:Block layer core,
	Luiz Capitulino

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

On 11/11/2015 09:42 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> What's more meta than using qapi to define qapi? :)
>>
>> Convert qtype_code into a full-fledged[*] builtin qapi enum type,
>> so that a subsequent patch can then use it as the discriminator
>> type of qapi alternate types.  Doing so is easiest when renaming
>> it to qapi conventions, as QTypeCode.
> 
> Out of curiosity: why does the rename make the conversion easier?

It guarantees I found all affected instances.  (Although I guess the
rename could be split to a separate patch from making it builtin).

It makes sure that if we later tighten rules about naming, we won't have
to whitelist 'qtype_code' as an anomaly to our conventions.

> 
> If we rename anyway, what about renaming to QType?  Hmm, we burned that
> on a struct we use only internally in qobject/.  Oh well.

Internal structs are often easy to rename.  So if we want to avoid the
need for 'prefix', I could certainly try to achieve that (move internal
QType out of the way, then rename qtype_code to QType, then make QType
the builtin).  Looks like this one patch just became three :)

> 
>>                                        Fortunately, there are not
>> many places in the tree that were actually spelling the type name
>> out, and the judicious use of 'prefix' in the qapi defintion
> 
> definition

I've got to quit coding late at night - my rate of typos increases :)

>> +++ b/docs/qapi-code-gen.txt
>> @@ -163,6 +163,7 @@ The following types are predefined, and map to C as follows:
>>                         accepts size suffixes
>>    bool      bool       JSON true or false
>>    any       QObject *  any JSON value
>> +  QTypeCode QTypeCode  JSON string of enum QTypeCode values
> 
> QTypeCode is currently used only internally, so the JSON values don't
> matter.  I don't expect that to change.  However, we either enforce
> internal use somehow, or document the JSON values.  Documenting them is
> easier.
> 
> In short, your patch is fine.
> 

>> -
>> -struct QObject;
>> +#include "qapi-types.h"
>>
>>  typedef struct QType {
>> -    qtype_code code;
>> +    QTypeCode code;
>>      void (*destroy)(struct QObject *);
>>  } QType;
>>
>    typedef struct QObject {
>        const QType *type;
>        size_t refcnt;
>    } QObject;
> 
> Note: typedef name QObject still defined here.

Oh, I see what you're saying. Since qapi-types.h now has a forward
declaration of the QObject typedef, this could be changed to just

struct QObject {
...
};

>> +++ b/scripts/qapi-types.py
>> @@ -233,8 +233,14 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
>>          self.defn += gen_type_cleanup(name)
>>
>>      def visit_enum_type(self, name, info, values, prefix):
>> -        self._fwdecl += gen_enum(name, values, prefix)
>> -        self._fwdefn += gen_enum_lookup(name, values, prefix)
>> +        # Special case for our lone builtin enum type
>> +        if name == 'QTypeCode':
> 
> Would "if not info" work?  Same in qapi-visit.py below.

Feels a bit hacky, since we just recently added is_implicit() to hide
(and then change) the 'if not info' check on objects.  Maybe an accessor
is_builtin() makes more sense?  But yes, same approach to both client files.


>> -#include "qapi/qmp/qobject.h"
>> +
>> +typedef struct QObject QObject;
> 
> Typedef name QObject now also defined here.  GCC accepts this silently
> without -Wpedantic, but other compilers might not.  Whether we care for
> such compilers or not, defining things in exactly one place is neater.
> 
> Possible fixes:
> 
> * Drop the typedef from qobject.h
> 
> * Don't add it to qapi-types.h, and use struct QObject there
> 

I favor dropping the second typedef.


>> +++ b/scripts/qapi.py
>> @@ -33,7 +33,7 @@ builtin_types = {
>>      'uint32':   'QTYPE_QINT',
>>      'uint64':   'QTYPE_QINT',
>>      'size':     'QTYPE_QINT',
>> -    'any':      None,           # any qtype_code possible, actually
>> +    'any':      None,           # any QTypeCode possible, actually
>>  }
>>
> 
> Should we list QTypeCode here?

Yeah, probably.  This array is only used by the ad hoc parser, and may
disappear later as we move more into check(), but we should be
consistent in the meantime.

> 
>>  # Whitelist of commands allowed to return a non-dictionary
>> @@ -1243,6 +1243,11 @@ class QAPISchema(object):
>>          self.the_empty_object_type = QAPISchemaObjectType(':empty', None, None,
>>                                                            [], None)
>>          self._def_entity(self.the_empty_object_type)
>> +        self._def_entity(QAPISchemaEnumType('QTypeCode', None,
>> +                                            ['none', 'qnull', 'qint',
>> +                                             'qstring', 'qdict', 'qlist',
>> +                                             'qfloat', 'qbool'],
>> +                                            'QTYPE'))
>>
>>      def _make_implicit_enum_type(self, name, info, values):
>>          name = name + 'Kind'   # Use namespace reserved by add_name()
> [Trivial changes to expected test output snipped]

I debated about hacking tests/qapi-schema/test-qapi.py to omit QTypeCode
(the way we already omit builtin types and things like 'intList'), for
less churn in the .out files.  I can go either way, if you have a
preference.

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

* Re: [Qemu-devel] [PATCH v11 13/28] qapi: Hoist tag collision check to Variants.check()
  2015-11-11 16:11     ` Eric Blake
@ 2015-11-11 17:03       ` Markus Armbruster
  0 siblings, 0 replies; 72+ messages in thread
From: Markus Armbruster @ 2015-11-11 17:03 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 11/11/2015 06:56 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> Checking that a given QAPISchemaObjectTypeVariant.name is a
>>> member of the corresponding QAPISchemaEnumType of the owning
>>> QAPISchemaObjectTypeVariants.tag_member ensures that there are
>>> no collisions in the generated C union for those tag values
>>> (since the enum itself should have no collisions).
>>>
>>> However, this check was the only thing that Variant.check() was
>>> doing beyond the work of the superclass ObjectTypeMember.check(),
>> 
>> Since PATCH 05, actually.  Suggest to mention that.
>
> I debated about munging patch 5 or 6 to actually make this change there;
> but decided that separate is just fine.  But yes, mentioning the earlier
> commit title will help.
>
>>> @@ -1075,10 +1076,6 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
>>>      def __init__(self, name, typ):
>>>          QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
>>>
>>> -    def check(self, schema, tag_type):
>>> -        QAPISchemaObjectTypeMember.check(self, schema)
>>> -        assert self.name in tag_type.values
>>> -
>>>      # This function exists to support ugly simple union special cases
>>>      # TODO get rid of them, and drop the function
>>>      def simple_union_type(self):
>> 
>> QAPISchemaObjectTypeVariant is now an almost trivial variation of
>> QAPISchemaObjectTypeMember.  Differences:
>> 
>> * __init__() has no parameter optional
>> 
>> * Method simple_union_type(), which exists only to support pointless
>>   differences in code generation for simple unions, all marked TODO.
>> 
>> There's hope we can get rid of the class after the TODOs are resolved.
>
> Nope. Because in 15/28 I add back in a non-trivial difference of "role =
> 'branch'" compared to the superclass "role = 'member'", which affects
> the quality of error messages using .describe().
>
> That, and I still have no idea how to get rid of the TODO (we know how
> to convert simple unions to flat unions, but the conversion is an
> either-or choice: either we keep the C layout the same but change {} in
> the JSON representation, or we keep QMP the same but affect the C layout
> - so fixing the TODO has to resolve that discrepancy, and may end up
> touching lots of code).

I'm not afraid of touching lots of QEMU code when I have a good reason.
Anyway, it's not something we should worry about right now.

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

* Re: [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values
  2015-11-11 16:03     ` Eric Blake
@ 2015-11-11 17:11       ` Markus Armbruster
  2015-11-12  8:34         ` Gerd Hoffmann
  2015-11-12  8:29       ` Gerd Hoffmann
  1 sibling, 1 reply; 72+ messages in thread
From: Markus Armbruster @ 2015-11-11 17:11 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, Peter Maydell, open list:Block layer core,
	Michael S. Tsirkin, Jason Wang, qemu-devel, Michael Roth,
	Gerd Hoffmann, Amit Shah, Luiz Capitulino, Andreas Färber

Eric Blake <eblake@redhat.com> writes:

> [hmm, wonder why scripts/get-maintainer.pl didn't loop in Gerd to the
> patch itself]
>
> On 11/11/2015 07:50 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> When munging enum values, the fact that we were passing the entire
>>> prefix + value through camel_to_upper() meant that enum values
>>> spelled with CamelCase could be turned into CAMEL_CASE.  However,
>>> this provides a potential collision (both OneTwo and One-Two would
>>> munge into ONE_TWO).  By changing the generation of enum constants
>>> to always be prefix + '_' + c_name(value).upper(), we can avoid
>>> any risk of collisions (if we can also ensure no case collisions,
>>> in the next patch) without having to think about what the
>>> heuristics in camel_to_upper() will actually do to the value.
>> 
>> This is the good part: the rules for clashes become much simpler.
>> 
>> Bonus: the implementation for detecting them will be simple, too.
>> 
>>> Thankfully, only two enums are affected: ErrorClass and InputButton.
>
> Visiting just InputButton in this email.
>
>> 
>> By convention (see CODING_STYLE), we use CamelCase for type names, and
>> nothing else.
>> 
>> Only enums violating this naming convention can be affected.  The bad
>> part: they exist.
>> 
>> InputButton has two camels: WheelUp and WheelDown.  The C enumeration
>> constants change from INPUT_BUTTON_WHEEL_UP/WHEEL_DOWN to
>> INPUT_BUTTON_WHEELUP/WHEELDOWN.  Not exactly an improvement, but one,
>> there are just 21 occurences in 11 files, and two, I think we can still
>> fix the enumeration to "lower case with dash", as it's only used by
>> x-input-send-event.
>
> The InputButton type has existed since 2.0; which is then part of the
> 'InputBtnEvent' struct, then the 'InputEvent' union, also since 2.0.  I
> can't easily tell if it was only used internally at that point, or if we
> exposed it through the command line (even if we didn't expose it through
> QMP);

As far as I can tell, we used it internally, and for tracing.

>       but I concur with your reading that in QMP it is only used via
> 'x-input-send-event' (since 2.2, but the x- prefix gives us freedom).
> [Oh, and I just spotted a typo; 'InputAxis' has a copy-and-paste doc
> typo calling it 'InputButton']
>
> If desired, I can prepare an alternate patch that adds the dash to the
> qapi enum definition, to see what we think.

If Gerd is fine with the rename, let's do it.

> But meanwhile, look at some of the lines in the patch:
>
>> diff --git a/ui/sdl.c b/ui/sdl.c
>> index 570cb99..2678611 100644
>> --- a/ui/sdl.c
>> +++ b/ui/sdl.c
>> @@ -469,8 +469,8 @@ static void sdl_send_mouse_event(int dx, int dy,
>> int x, int y, int state)
>>          [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT),
>>          [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE),
>>          [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT),
>> -        [INPUT_BUTTON_WHEEL_UP] = SDL_BUTTON(SDL_BUTTON_WHEELUP),
>> -        [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
>> +        [INPUT_BUTTON_WHEELUP] = SDL_BUTTON(SDL_BUTTON_WHEELUP),
>> +        [INPUT_BUTTON_WHEELDOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
>
> Since SDL already spells the names without space, it's not the end of
> the world if we do likewise.

Good point.

Even if we adopt SDL's spelling WHEELUP and WHEELDOWN, I'd still prefer
to downcase the QAPI names for consistency with the rest of QAPI.

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

* Re: [Qemu-devel] [PATCH v11 18/28] qerror: more error_setg() usage
  2015-11-11 16:19     ` Eric Blake
@ 2015-11-11 17:31       ` Markus Armbruster
  2015-11-11 17:44         ` Eric Blake
  0 siblings, 1 reply; 72+ messages in thread
From: Markus Armbruster @ 2015-11-11 17:31 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, Jiri Pirko, Eduardo Habkost,
	open list:Block layer core, Michael S. Tsirkin, Jason Wang,
	qemu-devel, Scott Feldman, Paolo Bonzini, Andreas Färber,
	Richard Henderson

Eric Blake <eblake@redhat.com> writes:

> On 11/11/2015 07:21 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> A few uses of error_set(ERROR_CLASS_GENERIC_ERROR) have snuck in
>>> since c6bd8c706.  Nuke them.
>> 
>> Doesn't really belong to this series, but that's okay.
>
> If you're going to modify this for 2.5 inclusion through your qerror
> tree, you may want to change the description to:

I'll try to get this into 2.5 mostly for the documentation update.

> A few uses of error_set(ERROR_CLASS_GENERIC_ERROR) were missed in
> c6bd8c706, or have snuck in since.  Nuke them.

Done.

>>> +++ b/hw/i386/pc.c
>>> @@ -1795,7 +1795,7 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
>>>          return;
>>>      }
>>>      if (value > (1ULL << 32)) {
>>> -        error_set(&error, ERROR_CLASS_GENERIC_ERROR,
>>> +        error_setg(&error,
>>>                    "Machine option 'max-ram-below-4g=%"PRIu64
>>>                    "' expects size less than or equal to 4G", value);
>> 
>> Indentation is now off.  Can tidy up in my tree.
>> 
>>>          error_propagate(errp, error);
>> [Rest snipped, it looks good]
>> 
>
> There's also the question if we want to address the ErrorClass name
> munging of 19/28 by adding your idea of an aliasing typedef in error.h;
> if so, should I prepare a smaller patch series of both of those changes
> for consideration for 2.5?

Can safely wait for 2.6, can't it?

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

* Re: [Qemu-devel] [PATCH v11 18/28] qerror: more error_setg() usage
  2015-11-11 17:31       ` Markus Armbruster
@ 2015-11-11 17:44         ` Eric Blake
  0 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-11 17:44 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Jiri Pirko, Eduardo Habkost,
	open list:Block layer core, Michael S. Tsirkin, Jason Wang,
	qemu-devel, Scott Feldman, Paolo Bonzini, Andreas Färber,
	Richard Henderson

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

On 11/11/2015 10:31 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> On 11/11/2015 07:21 AM, Markus Armbruster wrote:
>>> Eric Blake <eblake@redhat.com> writes:
>>>
>>>> A few uses of error_set(ERROR_CLASS_GENERIC_ERROR) have snuck in
>>>> since c6bd8c706.  Nuke them.
>>>
>>> Doesn't really belong to this series, but that's okay.
>>
>> If you're going to modify this for 2.5 inclusion through your qerror
>> tree, you may want to change the description to:
> 
> I'll try to get this into 2.5 mostly for the documentation update.
> 
>> A few uses of error_set(ERROR_CLASS_GENERIC_ERROR) were missed in
>> c6bd8c706, or have snuck in since.  Nuke them.
> 
> Done.

Oh, and I missed your capitalization preferences in the title:

qerror: More error_setg() usage


>> There's also the question if we want to address the ErrorClass name
>> munging of 19/28 by adding your idea of an aliasing typedef in error.h;
>> if so, should I prepare a smaller patch series of both of those changes
>> for consideration for 2.5?
> 
> Can safely wait for 2.6, can't it?

Yes.  Just trying to make sure I'm focusing on 2.5 patches first.

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

* Re: [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values
  2015-11-11 16:03     ` Eric Blake
  2015-11-11 17:11       ` Markus Armbruster
@ 2015-11-12  8:29       ` Gerd Hoffmann
  1 sibling, 0 replies; 72+ messages in thread
From: Gerd Hoffmann @ 2015-11-12  8:29 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, Peter Maydell, open list:Block layer core,
	Michael S. Tsirkin, qemu-devel, Jason Wang, Michael Roth,
	Markus Armbruster, Amit Shah, Luiz Capitulino,
	Andreas Färber

  Hi,

> The InputButton type has existed since 2.0; which is then part of the
> 'InputBtnEvent' struct, then the 'InputEvent' union, also since 2.0.  I
> can't easily tell if it was only used internally at that point,

Internal only.

> 'x-input-send-event' (since 2.2, but the x- prefix gives us freedom).

Yes, x-input-send-event is the only external usage.

cheers,
  Gerd

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

* Re: [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values
  2015-11-11 17:11       ` Markus Armbruster
@ 2015-11-12  8:34         ` Gerd Hoffmann
  2015-11-12 11:16           ` Markus Armbruster
  0 siblings, 1 reply; 72+ messages in thread
From: Gerd Hoffmann @ 2015-11-12  8:34 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Peter Maydell, open list:Block layer core,
	Michael S. Tsirkin, Jason Wang, qemu-devel, Michael Roth,
	Amit Shah, Luiz Capitulino, Andreas Färber

  Hi,

> > If desired, I can prepare an alternate patch that adds the dash to the
> > qapi enum definition, to see what we think.
> 
> If Gerd is fine with the rename, let's do it.

No need to do so I think ...

> >> -        [INPUT_BUTTON_WHEEL_UP] = SDL_BUTTON(SDL_BUTTON_WHEELUP),
> >> -        [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
> >> +        [INPUT_BUTTON_WHEELUP] = SDL_BUTTON(SDL_BUTTON_WHEELUP),
> >> +        [INPUT_BUTTON_WHEELDOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
> >
> > Since SDL already spells the names without space, it's not the end of
> > the world if we do likewise.
> 
> Good point.

This doesn't look too bad.  And even if x-input-send-event isn't
official api I'd prefer to not break it for such a minor cosmetic issue.

cheers,
  Gerd

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

* Re: [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values
  2015-11-12  8:34         ` Gerd Hoffmann
@ 2015-11-12 11:16           ` Markus Armbruster
  0 siblings, 0 replies; 72+ messages in thread
From: Markus Armbruster @ 2015-11-12 11:16 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Kevin Wolf, Peter Maydell, open list:Block layer core,
	Michael S. Tsirkin, Jason Wang, qemu-devel, Michael Roth,
	Amit Shah, Luiz Capitulino, Andreas Färber

Gerd Hoffmann <kraxel@redhat.com> writes:

>   Hi,
>
>> > If desired, I can prepare an alternate patch that adds the dash to the
>> > qapi enum definition, to see what we think.
>> 
>> If Gerd is fine with the rename, let's do it.
>
> No need to do so I think ...
>
>> >> -        [INPUT_BUTTON_WHEEL_UP] = SDL_BUTTON(SDL_BUTTON_WHEELUP),
>> >> -        [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
>> >> +        [INPUT_BUTTON_WHEELUP] = SDL_BUTTON(SDL_BUTTON_WHEELUP),
>> >> +        [INPUT_BUTTON_WHEELDOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
>> >
>> > Since SDL already spells the names without space, it's not the end of
>> > the world if we do likewise.
>> 
>> Good point.
>> 
>> Even if we adopt SDL's spelling WHEELUP and WHEELDOWN, I'd still prefer
>> to downcase the QAPI names for consistency with the rest of QAPI.
>
> This doesn't look too bad.  And even if x-input-send-event isn't
> official api I'd prefer to not break it for such a minor cosmetic issue.

To slow our slide into a morass of inconsistency, I intend to make
qapi.py enforce naming conventions.  Involves a whitelist for existing
violators we can't or won't fix.  Naturally, I'd prefer to keep the list
as short as possible.

I feel these ones can and should be fixed, and the best time to fix them
is when we drop the x- from the command.

But if you insist on keeping the current names then, I'll live with the
extra whitelist entries.

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

* Re: [Qemu-devel] [PATCH v11 21/28] qapi: Convert qtype_code into qapi enum type
  2015-11-11 17:03     ` Eric Blake
@ 2015-11-12 13:16       ` Markus Armbruster
  2015-11-18  6:27         ` Eric Blake
  0 siblings, 1 reply; 72+ messages in thread
From: Markus Armbruster @ 2015-11-12 13:16 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, Luiz Capitulino, Michael Roth,
	open list:Block layer core, qemu-devel

Eric Blake <eblake@redhat.com> writes:

> On 11/11/2015 09:42 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> What's more meta than using qapi to define qapi? :)
>>>
>>> Convert qtype_code into a full-fledged[*] builtin qapi enum type,
>>> so that a subsequent patch can then use it as the discriminator
>>> type of qapi alternate types.  Doing so is easiest when renaming
>>> it to qapi conventions, as QTypeCode.
>> 
>> Out of curiosity: why does the rename make the conversion easier?
>
> It guarantees I found all affected instances.  (Although I guess the
> rename could be split to a separate patch from making it builtin).

Well, you have to find them only because you rename, don't you?

> It makes sure that if we later tighten rules about naming, we won't have
> to whitelist 'qtype_code' as an anomaly to our conventions.

Good point.

>> If we rename anyway, what about renaming to QType?  Hmm, we burned that
>> on a struct we use only internally in qobject/.  Oh well.
>
> Internal structs are often easy to rename.  So if we want to avoid the
> need for 'prefix', I could certainly try to achieve that (move internal
> QType out of the way, then rename qtype_code to QType, then make QType
> the builtin).  Looks like this one patch just became three :)

Not sure it's worth the bother; the patch is okay as it is.

QType is overkill.  Instead of

    typedef struct QType {
        qtype_code code;
        void (*destroy)(struct QObject *);
    } QType;

    typedef struct QObject {
        const QType *type;
        size_t refcnt;
    } QObject;

we could simply have

    typedef struct QObject {
        QTypeCode type;
        size_t refcnt;
    } QObject;

with an array mapping QTypeCode to destroy methods.  We're not going to
define additional types at run time.

Perhaps such a change would be actually worth the bother.

>>>                                        Fortunately, there are not
>>> many places in the tree that were actually spelling the type name
>>> out, and the judicious use of 'prefix' in the qapi defintion
>> 
>> definition
>
> I've got to quit coding late at night - my rate of typos increases :)
>
>>> +++ b/docs/qapi-code-gen.txt
>>> @@ -163,6 +163,7 @@ The following types are predefined, and map to C as follows:
>>>                         accepts size suffixes
>>>    bool      bool       JSON true or false
>>>    any       QObject *  any JSON value
>>> +  QTypeCode QTypeCode  JSON string of enum QTypeCode values
>> 
>> QTypeCode is currently used only internally, so the JSON values don't
>> matter.  I don't expect that to change.  However, we either enforce
>> internal use somehow, or document the JSON values.  Documenting them is
>> easier.
>> 
>> In short, your patch is fine.
>> 
>
>>> -
>>> -struct QObject;
>>> +#include "qapi-types.h"
>>>
>>>  typedef struct QType {
>>> -    qtype_code code;
>>> +    QTypeCode code;
>>>      void (*destroy)(struct QObject *);
>>>  } QType;
>>>
>>    typedef struct QObject {
>>        const QType *type;
>>        size_t refcnt;
>>    } QObject;
>> 
>> Note: typedef name QObject still defined here.
>
> Oh, I see what you're saying. Since qapi-types.h now has a forward
> declaration of the QObject typedef, this could be changed to just
>
> struct QObject {
> ...
> };
>
>>> +++ b/scripts/qapi-types.py
>>> @@ -233,8 +233,14 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
>>>          self.defn += gen_type_cleanup(name)
>>>
>>>      def visit_enum_type(self, name, info, values, prefix):
>>> -        self._fwdecl += gen_enum(name, values, prefix)
>>> -        self._fwdefn += gen_enum_lookup(name, values, prefix)
>>> +        # Special case for our lone builtin enum type
>>> +        if name == 'QTypeCode':
>> 
>> Would "if not info" work?  Same in qapi-visit.py below.
>
> Feels a bit hacky, since we just recently added is_implicit() to hide
> (and then change) the 'if not info' check on objects.  Maybe an accessor
> is_builtin() makes more sense?  But yes, same approach to both client files.

QAPISchemaEntity methods like is_implicit() or a new is_builtin() can't
work here, because we lack the entity.

We have one in visit_needed(), and we use its is_implicit() to skip
implicit object types.  We could use entity.is_builtin() to skip (some)
builtins, and handle them elsewhere, but that doesn't feel like an
improvement over your code.

Let's take a step back and reconsider how we do builtins.

>> +            self._btin += gen_enum(name, values, prefix)
>> +            if do_builtins:
>> +                self.defn += gen_enum_lookup(name, values, prefix)
>> +        else:
>> +            self._fwdecl += gen_enum(name, values, prefix)
>> +            self._fwdefn += gen_enum_lookup(name, values, prefix)
>>
>>      def visit_array_type(self, name, info, element_type):
>>          if isinstance(element_type, QAPISchemaBuiltinType):

Linking generated code from multiple schemata that share names may fail,
because multiple definitions of the same external symbol exist.

Example: two schemata both define enum BadIdea.  Both generate const
char *BadIdea_lookup[] = { ... }, and we end up with two global symbols
BadIdea_lookup.

Solution: don't do that then.  Easy enough, except *all* schemata share
the builtin symbols!  Solution:

1. For declarations, use ifdeffery to make the compiler ignore all but
   the first copy it encounters,

2. For definitions, make the programmer pick one schema to generate the
   definitions, and run qapi-types.py and qapi-visit.py with -b.

In generator code, this looks like

    self._btin += ... declarations ...
    if do_builtins:
        self.defn += ... definitions ...

instead of the normal

    self.decl += ... declarations ...
    self.defn += ... declarations ...

(or the same with ._fwdecl, ._fwdefn, doesn't matter).

This is why you need to know whether the enum is builtin in
.visit_enum_type() above.

The builtin definitions are emitted into a suitable #ifdef block by
bracketing this code with an initial

    self._btin = guardstart('QAPI_TYPES_BUILTIN')

and a final

    self._btin += guardend('QAPI_TYPES_BUILTIN')
    self.decl = self._btin + self.decl
    self._btin = None

Here's an alternative solution that permits slightly code simpler
generator code, and thus avoids the need to know:

* Generate code for builtins exactly the same as for any other entities,
  i.e. get rid of self._btin and the ifdeffery.

* If the program links just one generated schema, this just works.

* If the program links multiple generated schemata, the programmer has
  to ensure their definitions get generated just once, and their
  declarations are available everywhere anyway.  Straightforward method:

  - The programmer suppresses builtins *completely* for *all* schemata.
    The obvious way to suppress them is to filter them out in
    visit_needed().

  - Instead, he generates them once for the *empty* schema, with a
    well-known --prefix.

  - Suppressing builtins generates a suitable #include for the
    well-known .h with the builtin declarations.

  - Additionally link the .c containing the builtin definitions.

Alternatively, trade some ease-of-use for the single schema case for
ease-of-use for the multiple schemata case and fewer cases:

* The generators either generate for a schema, or they generate builtins.

* When they generate builtins, they always use well-known file names.

* When they generate for a schema, they always generate the #include for
  the well-known builtin .h.  They never generate builtins.

>>> -#include "qapi/qmp/qobject.h"
>>> +
>>> +typedef struct QObject QObject;
>> 
>> Typedef name QObject now also defined here.  GCC accepts this silently
>> without -Wpedantic, but other compilers might not.  Whether we care for
>> such compilers or not, defining things in exactly one place is neater.
>> 
>> Possible fixes:
>> 
>> * Drop the typedef from qobject.h
>> 
>> * Don't add it to qapi-types.h, and use struct QObject there
>> 
>
> I favor dropping the second typedef.

Your choice.

>>> +++ b/scripts/qapi.py
>>> @@ -33,7 +33,7 @@ builtin_types = {
>>>      'uint32':   'QTYPE_QINT',
>>>      'uint64':   'QTYPE_QINT',
>>>      'size':     'QTYPE_QINT',
>>> -    'any':      None,           # any qtype_code possible, actually
>>> +    'any':      None,           # any QTypeCode possible, actually
>>>  }
>>>
>> 
>> Should we list QTypeCode here?
>
> Yeah, probably.  This array is only used by the ad hoc parser, and may
> disappear later as we move more into check(), but we should be
> consistent in the meantime.
>
>> 
>>>  # Whitelist of commands allowed to return a non-dictionary
>>> @@ -1243,6 +1243,11 @@ class QAPISchema(object):
>>>          self.the_empty_object_type = QAPISchemaObjectType(':empty', None, None,
>>>                                                            [], None)
>>>          self._def_entity(self.the_empty_object_type)
>>> +        self._def_entity(QAPISchemaEnumType('QTypeCode', None,
>>> +                                            ['none', 'qnull', 'qint',
>>> +                                             'qstring', 'qdict', 'qlist',
>>> +                                             'qfloat', 'qbool'],
>>> +                                            'QTYPE'))
>>>
>>>      def _make_implicit_enum_type(self, name, info, values):
>>>          name = name + 'Kind'   # Use namespace reserved by add_name()
>> [Trivial changes to expected test output snipped]
>
> I debated about hacking tests/qapi-schema/test-qapi.py to omit QTypeCode
> (the way we already omit builtin types and things like 'intList'), for
> less churn in the .out files.  I can go either way, if you have a
> preference.

Omit them only if it's trivial.

I guess it would be trivial if we adopted the alternative way to do
builtins I sketched above.

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

* Re: [Qemu-devel] [PATCH v11 22/28] qapi: Simplify visiting of alternate types
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 22/28] qapi: Simplify visiting of alternate types Eric Blake
@ 2015-11-12 14:21   ` Markus Armbruster
  2015-11-12 15:54   ` Markus Armbruster
  2015-11-13 23:54   ` Eric Blake
  2 siblings, 0 replies; 72+ messages in thread
From: Markus Armbruster @ 2015-11-12 14:21 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Previously, working with alternates required two lookup arrays
> and some indirection: for type Foo, we created Foo_qtypes[]
> which maps each qtype to a value of the generated FooKind enum,
> then look up that value in FooKind_lookup[] like we do for other
> union types.
>
> This has a couple of subtle bugs.  First, the generator was
> creating a call with a parameter '(int *) &(*obj)->type' where
> type is an enum type; this is unsafe if the compiler chooses
> to store the enum type in a different size than int, where
> assigning through the wrong size pointer can corrupt data or
> cause a SIGBUS.
>
> Second, since the values of the FooKind enum start at zero, all
> entries of the Foo_qtypes[] array that were not explicitly
> initialized will map to the same branch of the union as the
> first member of the alternate, rather than triggering a desired
> failure in visit_get_next_type().  Fortunately, the bug seldom
> bites; the very next thing the input visitor does is try to
> parse the incoming JSON with the wrong parser, which normally
> fails; the output visitor is not used with a C struct in that
> state, and the dealloc visitor has nothing to clean up (so
> there is no leak).
>
> However, the second bug IS observable in one case: parsing an
> integer causes unusual behavior in an alternate that contains
> at least a 'number' member but no 'int' member, because the
> 'number' parser accepts QTYPE_QINT in addition to the expected
> QTYPE_QFLOAT (that is, since 'int' is not a member, the type
> QTYPE_QINT accidentally maps to FooKind 0; if this enum value
> is the 'number' branch the integer parses successfully, but if
> the 'number' branch is not first, some other branch tries to
> parse the integer and rejects it).  A later patch will worry
> about fixing alternates to always parse all inputs that a
> non-alternate 'number' would accept, for now this is still
> marked FIXME in the updated test-qmp-input-visitor.c, to
> merely point out that new undesired behavior of 'ans' matches
> the existing undesired behavior of 'asn'.
>
> This patch fixes the default-initialization bug by deleting the
> indirection, and modifying get_next_type() to directly assign a
> QTypeCode parameter.  This in turn fixes the type-casting bug,
> as we are no longer casting a pointer to enum to a questionable
> size. There is no longer a need to generate an implicit FooKind
> enum associated with the alternate type (since the QMP wire
> format never uses the stringized counterparts of the C union
> member names); that also means we no longer have a collision
> with an alternate branch named 'max'.  Since the updated
> visit_get_next_type() does not know which qtypes are expected,
> the generated visitor is modified to generate an error statement
> if an unexpected type is encountered.
>
> Callers now have to know the QTYPE_* mapping when looking at the
> discriminator; but so far, only the testsuite was even using the
> C struct of an alternate types.  I considered the possibility of
> keeping the internal enum FooKind, but initialized differently
> than most generated arrays, as in:
>   typedef enum FooKind {
>       FOO_KIND_A = QTYPE_QDICT,
>       FOO_KIND_B = QTYPE_QINT,
>   } FooKind;
> to create nicer aliases for knowing when to use foo->a or foo->b
> when inspecting foo->type; but it turned out to add too much
> complexity, especially without a client.
>
> There is a user-visible side effect to this change, but I
> consider it to be an improvement. Previously,
> the invalid QMP command:
>   {"execute":"blockdev-add", "arguments":{"options":
>     {"driver":"raw", "id":"a", "file":true}}}
> failed with:
>   {"error": {"class": "GenericError",
>     "desc": "Invalid parameter type for 'file', expected: QDict"}}
> (visit_get_next_type() succeeded, and the error comes from the
> visit_type_BlockdevOptions() expecting {}; there is no mention of
> the fact that a string would also work).  Now it fails with:
>   {"error": {"class": "GenericError",
>     "desc": "Invalid parameter type for 'file', expected: BlockdevRef"}}
> (the error when the next type doesn't match any expected types for
> the overall alternate).
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
[...]
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index d4ef08e..1790e8f 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -627,15 +627,15 @@ def check_union(expr, expr_info):
>  def check_alternate(expr, expr_info):
>      name = expr['alternate']
>      members = expr['data']
> -    values = {'MAX': '(automatic)'}
> +    values = {}
>      types_seen = {}
>
>      # Check every branch
>      for (key, value) in members.items():
>          check_name(expr_info, "Member of alternate '%s'" % name, key)
>
> -        # Check for conflicts in the generated enum
> -        c_key = camel_to_upper(key)
> +        # Check for conflicts in the branch names
> +        c_key = c_name(key)
>          if c_key in values:
>              raise QAPIExprError(expr_info,
>                                  "Alternate '%s' member '%s' clashes with '%s'"
> @@ -1090,8 +1090,11 @@ class QAPISchemaObjectTypeVariants(object):
>          assert isinstance(self.tag_member.type, QAPISchemaEnumType)
>          for v in self.variants:
>              v.check(schema)
> -            assert v.name in self.tag_member.type.values
> -            if isinstance(v.type, QAPISchemaObjectType):
> +            # Union names must match enum values; alternate names are
> +            # checked separately. Use 'seen' to tell the two apart.
> +            if seen:
> +                assert v.name in self.tag_member.type.values
> +                assert isinstance(v.type, QAPISchemaObjectType)
>                  v.type.check(schema)

Not exactly elegant, but it'll do.  Perhaps we can clean it up later.

>
>      def check_clash(self, schema, info, seen):
> @@ -1133,6 +1136,11 @@ class QAPISchemaAlternateType(QAPISchemaType):
>          # Not calling self.variants.check_clash(), because there's nothing
>          # to clash with
>          self.variants.check(schema, {})
> +        # Alternate branch names have no relation to the tag enum values;
> +        # so we have to check for potential name collisions ourselves.
> +        cases = {}
> +        for var in self.variants.variants:
> +            var.check_clash(self.info, cases)

If we call it cases here, perhaps we should call it members rather than
seen elsewhere.  I guess I'd simply stick to seen, though.

>
>      def json_type(self):
>          return 'value'
> @@ -1340,7 +1348,7 @@ class QAPISchema(object):
>          data = expr['data']
>          variants = [self._make_variant(key, value)
>                      for (key, value) in data.iteritems()]
> -        tag_member = self._make_implicit_tag(name, info, variants)
> +        tag_member = QAPISchemaObjectTypeMember('type', 'QTypeCode', False)

This drops generation of the implicit enum.

Only one user of _make_implicit_tag() left: _def_union_type() for simple
unions.  Possible followup: inline it there.

>          self._def_entity(
>              QAPISchemaAlternateType(name, info,
>                                      QAPISchemaObjectTypeVariants(None,
[...]

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

* Re: [Qemu-devel] [PATCH v11 23/28] qapi: Fix alternates that accept 'number' but not 'int'
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 23/28] qapi: Fix alternates that accept 'number' but not 'int' Eric Blake
@ 2015-11-12 15:01   ` Markus Armbruster
  0 siblings, 0 replies; 72+ messages in thread
From: Markus Armbruster @ 2015-11-12 15:01 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> The QMP input visitor allows integral values to be assigned by
> promotion to a QTYPE_QFLOAT.  However, when parsing an alternate,
> we did not take this into account, such that an alternate that
> accepts 'number' and some other type, but not 'int', would reject
> integral values.
>
> With this patch, we now have the following desirable table:
>
>     alternate has      case selected for
>     'int'  'number'    QTYPE_QINT  QTYPE_QFLOAT
>       no        no     error       error
>       no       yes     'number'    'number'
>      yes        no     'int'       error
>      yes       yes     'int'       'number'
>
> While it is unlikely that we will ever use 'number' in an
> alternate other than in the testsuite, it never hurts to be
> more precise in what we allow.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v11 (no v10): slight commit message tweak, rebase to earlier changes
> v9: rebase to earlier changes
> v8: no change
> v7: rebase to named .u union
> v6: rebase onto earlier testsuite and gen_err_check() improvements
> ---
>  include/qapi/visitor-impl.h    |  2 +-
>  include/qapi/visitor.h         |  3 ++-
>  qapi/qapi-visit-core.c         |  4 ++--
>  qapi/qmp-input-visitor.c       |  4 ++++
>  scripts/qapi-visit.py          | 11 +++++++----
>  tests/test-qmp-input-visitor.c | 16 ++++++----------
>  6 files changed, 22 insertions(+), 18 deletions(-)
>
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index e36da60..ac6c17e 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -33,7 +33,7 @@ struct Visitor
>      void (*type_enum)(Visitor *v, int *obj, const char * const strings[],
>                        const char *kind, const char *name, Error **errp);
>      /* May be NULL; only needed for input visitors. */
> -    void (*get_next_type)(Visitor *v, QTypeCode *type,
> +    void (*get_next_type)(Visitor *v, QTypeCode *type, bool promote_int,
>                            const char *name, Error **errp);
>
>      void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp);
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index 4fa6d50..250e8e1 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -46,8 +46,9 @@ void visit_optional(Visitor *v, bool *present, const char *name,
>   * Determine the qtype of the item @name in the current object visit.
>   * For input visitors, set *@type to the correct qtype of a qapi
>   * alternate type; for other visitors, leave *@type unchanged.
> + * If @promote_int, treat integers as QTYPE_FLOAT.
>   */
> -void visit_get_next_type(Visitor *v, QTypeCode *type,
> +void visit_get_next_type(Visitor *v, QTypeCode *type, bool promote_int,
>                           const char *name, Error **errp);
>  void visit_type_enum(Visitor *v, int *obj, const char * const strings[],
>                       const char *kind, const char *name, Error **errp);
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index ddb3a15..52c132a 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -81,11 +81,11 @@ void visit_optional(Visitor *v, bool *present, const char *name,
>      }
>  }
>
> -void visit_get_next_type(Visitor *v, QTypeCode *type,
> +void visit_get_next_type(Visitor *v, QTypeCode *type, bool promote_int,
>                           const char *name, Error **errp)
>  {
>      if (v->get_next_type) {
> -        v->get_next_type(v, type, name, errp);
> +        v->get_next_type(v, type, promote_int, name, errp);
>      }
>  }
>
> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
> index 2141ff5..4238faf 100644
> --- a/qapi/qmp-input-visitor.c
> +++ b/qapi/qmp-input-visitor.c
> @@ -209,6 +209,7 @@ static void qmp_input_end_list(Visitor *v, Error **errp)
>  }
>
>  static void qmp_input_get_next_type(Visitor *v, QTypeCode *type,
> +                                    bool promote_int,
>                                      const char *name, Error **errp)
>  {
>      QmpInputVisitor *qiv = to_qiv(v);
> @@ -219,6 +220,9 @@ static void qmp_input_get_next_type(Visitor *v, QTypeCode *type,
>          return;
>      }
>      *type = qobject_type(qobj);
> +    if (promote_int && *type == QTYPE_QINT) {
> +        *type = QTYPE_QFLOAT;
> +    }
>  }
>

In case you also wonder why only the QMP input visitor implements this
method: the generated alternate visitors are the only users of
visit_get_next_type(), and so far alternates are only used with QMP.  I
think.

>  static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name,
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index ad98685..4c9d739 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -183,6 +183,11 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s *obj, const char *name, Error
>
>
>  def gen_visit_alternate(name, variants):
> +    promote_int = 'true'
> +    for var in variants.variants:
> +        if var.type.alternate_qtype() == 'QTYPE_QINT':
> +            promote_int = 'false'
> +
>      ret = mcgen('''
>
>  void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
> @@ -193,16 +198,14 @@ 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, &(*obj)->type, name, &err);
> +    visit_get_next_type(v, &(*obj)->type, %(promote_int)s, name, &err);
>      if (err) {
>          goto out_obj;
>      }
>      switch ((*obj)->type) {
>  ''',
> -                c_name=c_name(name))
> +                c_name=c_name(name), promote_int=promote_int)
>
> -    # FIXME: When 'number' but not 'int' is present in the alternate, we
> -    # should allow QTYPE_INT to promote to QTYPE_FLOAT.
>      for var in variants.variants:
>          ret += mcgen('''
>      case %(case)s:
> diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
> index 43b9e18..b4a5bee 100644
> --- a/tests/test-qmp-input-visitor.c
> +++ b/tests/test-qmp-input-visitor.c
> @@ -347,20 +347,16 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data,
>      error_free_or_abort(&err);
>      qapi_free_AltStrBool(asb);
>
> -    /* FIXME: integer should parse as number */
>      v = visitor_input_test_init(data, "42");
> -    visit_type_AltStrNum(v, &asn, NULL, &err);
> -    /* FIXME g_assert_cmpint(asn->type, ==, QTYPE_QFLOAT); */
> -    /* FIXME g_assert_cmpfloat(asn->u.n, ==, 42); */
> -    error_free_or_abort(&err);
> +    visit_type_AltStrNum(v, &asn, NULL, &error_abort);
> +    g_assert_cmpint(asn->type, ==, QTYPE_QFLOAT);
> +    g_assert_cmpfloat(asn->u.n, ==, 42);
>      qapi_free_AltStrNum(asn);
>
> -    /* FIXME: integer should parse as number */
>      v = visitor_input_test_init(data, "42");
> -    visit_type_AltNumStr(v, &ans, NULL, &err);
> -    /* FIXME g_assert_cmpint(ans->type, ==, QTYPE_QFLOAT); */
> -    /* FIXME g_assert_cmpfloat(ans->u.n, ==, 42); */
> -    error_free_or_abort(&err);
> +    visit_type_AltNumStr(v, &ans, NULL, &error_abort);
> +    g_assert_cmpint(ans->type, ==, QTYPE_QFLOAT);
> +    g_assert_cmpfloat(ans->u.n, ==, 42);
>      qapi_free_AltNumStr(ans);
>
>      v = visitor_input_test_init(data, "42");

Looks good.

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

* Re: [Qemu-devel] [PATCH v11 25/28] qapi: Simplify visits of optional fields
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 25/28] qapi: Simplify visits of optional fields Eric Blake
@ 2015-11-12 15:11   ` Markus Armbruster
  2015-11-12 15:30     ` Eric Blake
  0 siblings, 1 reply; 72+ messages in thread
From: Markus Armbruster @ 2015-11-12 15:11 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> None of the visitor callbacks would set an error when testing
> if an optional field was present; make this part of the interface
> contract by eliminating the errp argument.  Then, for less code,
> reflect the determined boolean value back to the caller instead
> of making the caller read the boolean after the fact.
>
> The resulting generated code has a nice diff:
>
> |-    visit_optional(v, &has_fdset_id, "fdset-id", &err);
> |-    if (err) {
> |-        goto out;
> |-    }
> |-    if (has_fdset_id) {
> |+    if (visit_optional(v, &has_fdset_id, "fdset-id")) {
> |         visit_type_int(v, &fdset_id, "fdset-id", &err);
> |         if (err) {
> |             goto out;
> |         }
> |     }

Any particular reason not to do

        has_fdset_id = visit_optional(v, "fdset-id");
        if (has_fdset_id) {
            visit_type_int(v, &fdset_id, "fdset-id", &err);
            if (err) {
                goto out;
            }
        }

?

> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v11 (no v10): no change
> v9: no change
> v8: no change
> v7: rebase to no member.c_name()
> v6: rebase onto earlier testsuite and gen_err_check() improvements
> ---
>  include/qapi/visitor-impl.h |  5 ++---
>  include/qapi/visitor.h      | 10 ++++++++--
>  qapi/opts-visitor.c         |  2 +-
>  qapi/qapi-visit-core.c      |  6 +++---
>  qapi/qmp-input-visitor.c    |  3 +--
>  qapi/string-input-visitor.c |  3 +--
>  scripts/qapi.py             |  7 +------
>  7 files changed, 17 insertions(+), 19 deletions(-)
>
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index ac6c17e..81d1aa0 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -44,9 +44,8 @@ struct Visitor
>      void (*type_any)(Visitor *v, QObject **obj, const char *name,
>                       Error **errp);
>
> -    /* May be NULL */
> -    void (*optional)(Visitor *v, bool *present, const char *name,
> -                     Error **errp);
> +    /* May be NULL; most useful for input visitors. */
> +    void (*optional)(Visitor *v, bool *present, const char *name);
>
>      void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp);
>      void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp);
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index 250e8e1..dd4f0b3 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -39,8 +39,14 @@ void visit_end_implicit_struct(Visitor *v, Error **errp);
>  void visit_start_list(Visitor *v, const char *name, Error **errp);
>  GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
>  void visit_end_list(Visitor *v, Error **errp);
> -void visit_optional(Visitor *v, bool *present, const char *name,
> -                    Error **errp);
> +
> +/**
> + * Check if an optional member @name of an object needs visiting.
> + * For input visitors, set *@present according to whether the
> + * corresponding visit_type_*() needs calling; for other visitors,
> + * leave *@present unchanged.  Return *@present for convenience.
> + */
> +bool visit_optional(Visitor *v, bool *present, const char *name);
>
>  /**
>   * Determine the qtype of the item @name in the current object visit.
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index cd10392..ef5fb8b 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -488,7 +488,7 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>
>
>  static void
> -opts_optional(Visitor *v, bool *present, const char *name, Error **errp)
> +opts_optional(Visitor *v, bool *present, const char *name)
>  {
>      OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
>
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 52c132a..daaa3af 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -73,12 +73,12 @@ void visit_end_union(Visitor *v, bool data_present, Error **errp)
>      }
>  }
>
> -void visit_optional(Visitor *v, bool *present, const char *name,
> -                    Error **errp)
> +bool visit_optional(Visitor *v, bool *present, const char *name)
>  {
>      if (v->optional) {
> -        v->optional(v, present, name, errp);
> +        v->optional(v, present, name);
>      }
> +    return *present;
>  }

Slightly ugly: struct Visitor method optional returns void, but the
wrapper returns bool.

>
>  void visit_get_next_type(Visitor *v, QTypeCode *type, bool promote_int,
> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
> index 4238faf..9f0ed69 100644
> --- a/qapi/qmp-input-visitor.c
> +++ b/qapi/qmp-input-visitor.c
> @@ -304,8 +304,7 @@ static void qmp_input_type_any(Visitor *v, QObject **obj, const char *name,
>      *obj = qobj;
>  }
>
> -static void qmp_input_optional(Visitor *v, bool *present, const char *name,
> -                               Error **errp)
> +static void qmp_input_optional(Visitor *v, bool *present, const char *name)
>  {
>      QmpInputVisitor *qiv = to_qiv(v);
>      QObject *qobj = qmp_input_get_object(qiv, name, true);
> diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
> index bbd6a54..dee780a 100644
> --- a/qapi/string-input-visitor.c
> +++ b/qapi/string-input-visitor.c
> @@ -299,8 +299,7 @@ static void parse_type_number(Visitor *v, double *obj, const char *name,
>      *obj = val;
>  }
>
> -static void parse_optional(Visitor *v, bool *present, const char *name,
> -                           Error **errp)
> +static void parse_optional(Visitor *v, bool *present, const char *name)
>  {
>      StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 1790e8f..b17f843 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -1656,15 +1656,10 @@ def gen_visit_fields(members, prefix='', need_cast=False, skiperr=False):
>      for memb in members:
>          if memb.optional:
>              ret += mcgen('''
> -    visit_optional(v, &%(prefix)shas_%(c_name)s, "%(name)s", %(errp)s);
> +    if (visit_optional(v, &%(prefix)shas_%(c_name)s, "%(name)s")) {
>  ''',
>                           prefix=prefix, c_name=c_name(memb.name),
>                           name=memb.name, errp=errparg)
> -            ret += gen_err_check(skiperr=skiperr)
> -            ret += mcgen('''
> -    if (%(prefix)shas_%(c_name)s) {
> -''',
> -                         prefix=prefix, c_name=c_name(memb.name))
>              push_indent()
>
>          # Ugly: sometimes we need to cast away const

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

* Re: [Qemu-devel] [PATCH v11 25/28] qapi: Simplify visits of optional fields
  2015-11-12 15:11   ` Markus Armbruster
@ 2015-11-12 15:30     ` Eric Blake
  2015-11-12 16:20       ` Markus Armbruster
  0 siblings, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-12 15:30 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 11/12/2015 08:11 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> None of the visitor callbacks would set an error when testing
>> if an optional field was present; make this part of the interface
>> contract by eliminating the errp argument.  Then, for less code,
>> reflect the determined boolean value back to the caller instead
>> of making the caller read the boolean after the fact.
>>
>> The resulting generated code has a nice diff:
>>
>> |-    visit_optional(v, &has_fdset_id, "fdset-id", &err);
>> |-    if (err) {
>> |-        goto out;
>> |-    }
>> |-    if (has_fdset_id) {
>> |+    if (visit_optional(v, &has_fdset_id, "fdset-id")) {
>> |         visit_type_int(v, &fdset_id, "fdset-id", &err);
>> |         if (err) {
>> |             goto out;
>> |         }
>> |     }
> 
> Any particular reason not to do
> 
>         has_fdset_id = visit_optional(v, "fdset-id");
>         if (has_fdset_id) {

We can't. Output visitors do not implement visit_optional() callbacks,
but must rely on the incoming value of has_fdset_id.  Which means
assigning to has_fdset_id without an incoming value will do the wrong
thing.  Or worded differently, &has_fdset_id is modified as an output
parameter by input visitors, and read unchanged as an input parameter by
output visitors.

>> +++ b/qapi/qapi-visit-core.c
>> @@ -73,12 +73,12 @@ void visit_end_union(Visitor *v, bool data_present, Error **errp)
>>      }
>>  }
>>
>> -void visit_optional(Visitor *v, bool *present, const char *name,
>> -                    Error **errp)
>> +bool visit_optional(Visitor *v, bool *present, const char *name)
>>  {
>>      if (v->optional) {
>> -        v->optional(v, present, name, errp);
>> +        v->optional(v, present, name);
>>      }
>> +    return *present;
>>  }
> 
> Slightly ugly: struct Visitor method optional returns void, but the
> wrapper returns bool.

I could make all the callbacks (all 3 of them: opts-visitor,
qmp-input-visitor, string-input-visitor) return bool, but didn't see the
point in the churn, especially since the contract of the return value is
easy to do in the wrapper.

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

* Re: [Qemu-devel] [PATCH v11 27/28] qapi: Move duplicate enum value checks to schema check()
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 27/28] qapi: Move duplicate enum value " Eric Blake
@ 2015-11-12 15:46   ` Markus Armbruster
  2015-11-12 16:08     ` Eric Blake
  2015-11-18  6:48     ` Eric Blake
  0 siblings, 2 replies; 72+ messages in thread
From: Markus Armbruster @ 2015-11-12 15:46 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Similar to the previous commit, move the detection of a collision
> in enum values from parse time to QAPISchemaEnumType.check().
> This happens to also detect collisions in union branch names
> mapping to the same enum value, even when the names do not
> collide case-wise.  So for a decent error message, we have to
> determine if the enum is implicit (and if so where the real
> collision lies).
>
> Testing this showed that the test union-bad-branch wasn't adding
> much: union-clash-branches exposes the error message when branches
> directly collide, and union-max exposes the error message when
> branches cause an enum collision (union-bad-branch basically
> causes an enum collision that would not be a C collision).  This
> goes along with our desire to require ALL names to be
> case-insensitively unique.
>
> No change to generated code.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v11 (no v10): message of union-clash-branches.err changed: rely on
> enum check rather than Variant.check() to catch it
> v9: rebase to earlier changes, update commit message, break out
> helper _describe() method
> v8: rebase to earlier changes; better comments
> v7: retitle and improve commit message; earlier subclass patches
> avoid problem with detecting 'kind' collision
> v6: new patch
> ---
>  scripts/qapi.py                             | 41 ++++++++++++++++-------------
>  tests/Makefile                              |  1 -
>  tests/qapi-schema/enum-clash-member.err     |  2 +-
>  tests/qapi-schema/enum-max-member.err       |  2 +-
>  tests/qapi-schema/union-bad-branch.err      |  1 -
>  tests/qapi-schema/union-bad-branch.exit     |  1 -
>  tests/qapi-schema/union-bad-branch.json     |  8 ------
>  tests/qapi-schema/union-bad-branch.out      |  0
>  tests/qapi-schema/union-clash-branches.err  |  2 +-
>  tests/qapi-schema/union-clash-branches.json |  2 +-
>  tests/qapi-schema/union-max.err             |  2 +-
>  11 files changed, 28 insertions(+), 34 deletions(-)
>  delete mode 100644 tests/qapi-schema/union-bad-branch.err
>  delete mode 100644 tests/qapi-schema/union-bad-branch.exit
>  delete mode 100644 tests/qapi-schema/union-bad-branch.json
>  delete mode 100644 tests/qapi-schema/union-bad-branch.out
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 1d59ce9..08a366e 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -533,7 +533,6 @@ def check_union(expr, expr_info):
>      base = expr.get('base')
>      discriminator = expr.get('discriminator')
>      members = expr['data']
> -    values = {'MAX': '(automatic)'}
>
>      # Two types of unions, determined by discriminator.
>
> @@ -593,15 +592,6 @@ def check_union(expr, expr_info):
>                                      "enum '%s'" %
>                                      (key, enum_define["enum_name"]))
>
> -        # Otherwise, check for conflicts in the generated enum
> -        else:
> -            c_key = camel_to_upper(key)
> -            if c_key in values:
> -                raise QAPIExprError(expr_info,
> -                                    "Union '%s' member '%s' clashes with '%s'"
> -                                    % (name, key, values[c_key]))
> -            values[c_key] = key
> -
>
>  def check_alternate(expr, expr_info):
>      name = expr['alternate']
> @@ -630,7 +620,6 @@ def check_enum(expr, expr_info):
>      name = expr['enum']
>      members = expr.get('data')
>      prefix = expr.get('prefix')
> -    values = {'MAX': '(automatic)'}
>
>      if not isinstance(members, list):
>          raise QAPIExprError(expr_info,
> @@ -641,12 +630,6 @@ def check_enum(expr, expr_info):
>      for member in members:
>          check_name(expr_info, "Member of enum '%s'" % name, member,
>                     enum_member=True)
> -        key = camel_to_upper(member)
> -        if key in values:
> -            raise QAPIExprError(expr_info,
> -                                "Enum '%s' member '%s' clashes with '%s'"
> -                                % (name, member, values[key]))
> -        values[key] = member
>
>
>  def check_struct(expr, expr_info):
> @@ -873,8 +856,30 @@ class QAPISchemaEnumType(QAPISchemaType):
>          self.values = values
>          self.prefix = prefix
>
> +    def _describe(self, schema):
> +        # If the enum is implicit, report the error on behalf of
> +        # the union or alternate that triggered the enum
> +        if self.is_implicit():
> +            owner = schema.lookup_type(self.name[:-4])
> +            assert owner
> +            if isinstance(owner, QAPISchemaAlternateType):
> +                return "Alternate '%s' branch" % owner.name

Didn't we just drop this kind of implicit enum?

> +            else:
> +                return "Union '%s' branch" % owner.name
> +        else:
> +            return "Enum '%s' value" % self.name

I like to call it "member" rather than value, because it avoids
confusion with the numeric value of the C enumeration constant generated
for it.

The conditional isn't exactly elegant, but it would do.  I'm not 100%
convinced we need it, though.  self.info already points to whatever
defined this enum, either an explicit enum definition, or a simple union
definition.  How do the error messages come out if we dumb down to
"Member '%s'"?

A method with a similar purpose exists in QAPISchemaObjectTypeMember,
but it's spelled describe().  It's used only from within the class.
Rename it to match this one?

> +
>      def check(self, schema):
> -        assert len(set(self.values)) == len(self.values)
> +        # Check for collisions on the generated C enum values
> +        seen = {c_enum_const(self.name, 'MAX'): '(automatic MAX)'}
> +        for value in self.values:
> +            c_value = c_enum_const(self.name, value)
> +            if c_value in seen:
> +                raise QAPIExprError(self.info,
> +                                    "%s '%s' clashes with '%s'"
> +                                    % (self._describe(schema), value,
> +                                       seen[c_value]))
> +            seen[c_value] = value

Loop body is very similar to QAPISchemaObjectTypeMember.check_clash().
Differences:

* c_enum_const(enum_name, member_name) vs. c_name(member_name).upper()

  This isn't really a difference, because the former returns the latter
  prefixed by a string that doesn't vary in the loop.

  One could argue that using c_enum_const() lets us abstract from what
  it does, but that's an illusion.  We rely on it when we generate union
  members called c_name(member_name) without checking for collisions
  again.

  By the way, c_enum_const(self.name, value, self.prefix) would be more
  correct.  Doesn't matter here, of course.

  Therefore, I'd be very much tempted to use c_name(member_name).upper()
  here as well.

* The error message.  But I suspect the same "Member '%s' clashes with
  '%s'" could do for both.

If I'm right and we can drop the differences, the common code could
become a helper function.

>
>      def is_implicit(self):
>          # See QAPISchema._make_implicit_enum_type()
> diff --git a/tests/Makefile b/tests/Makefile
> index d1c6817..cdff7a4 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -336,7 +336,6 @@ qapi-schema += unclosed-list.json
>  qapi-schema += unclosed-object.json
>  qapi-schema += unclosed-string.json
>  qapi-schema += unicode-str.json
> -qapi-schema += union-bad-branch.json
>  qapi-schema += union-base-no-discriminator.json
>  qapi-schema += union-clash-branches.json
>  qapi-schema += union-clash-data.json
> diff --git a/tests/qapi-schema/enum-clash-member.err b/tests/qapi-schema/enum-clash-member.err
> index 48bd136..84030c5 100644
> --- a/tests/qapi-schema/enum-clash-member.err
> +++ b/tests/qapi-schema/enum-clash-member.err
> @@ -1 +1 @@
> -tests/qapi-schema/enum-clash-member.json:2: Enum 'MyEnum' member 'ONE' clashes with 'one'
> +tests/qapi-schema/enum-clash-member.json:2: Enum 'MyEnum' value 'ONE' clashes with 'one'

Would become

   tests/qapi-schema/enum-clash-member.json:2: Member 'ONE' clashes with 'one'

Good enough for me.

> diff --git a/tests/qapi-schema/enum-max-member.err b/tests/qapi-schema/enum-max-member.err
> index f77837f..6b9ef9b 100644
> --- a/tests/qapi-schema/enum-max-member.err
> +++ b/tests/qapi-schema/enum-max-member.err
> @@ -1 +1 @@
> -tests/qapi-schema/enum-max-member.json:3: Enum 'MyEnum' member 'max' clashes with '(automatic)'
> +tests/qapi-schema/enum-max-member.json:3: Enum 'MyEnum' value 'max' clashes with '(automatic MAX)'

   tests/qapi-schema/enum-max-member.json:3: Member 'max' clashes with '(automatic MAX)'

Good enough for me.

> diff --git a/tests/qapi-schema/union-bad-branch.err b/tests/qapi-schema/union-bad-branch.err
> deleted file mode 100644
> index 8822735..0000000
> --- a/tests/qapi-schema/union-bad-branch.err
> +++ /dev/null
> @@ -1 +0,0 @@
> -tests/qapi-schema/union-bad-branch.json:6: Union 'MyUnion' member 'ONE' clashes with 'one'
> diff --git a/tests/qapi-schema/union-bad-branch.exit b/tests/qapi-schema/union-bad-branch.exit
> deleted file mode 100644
> index d00491f..0000000
> --- a/tests/qapi-schema/union-bad-branch.exit
> +++ /dev/null
> @@ -1 +0,0 @@
> -1
> diff --git a/tests/qapi-schema/union-bad-branch.json b/tests/qapi-schema/union-bad-branch.json
> deleted file mode 100644
> index 913aa38..0000000
> --- a/tests/qapi-schema/union-bad-branch.json
> +++ /dev/null
> @@ -1,8 +0,0 @@
> -# we reject normal unions where branches would collide in C
> -{ 'struct': 'One',
> -  'data': { 'string': 'str' } }
> -{ 'struct': 'Two',
> -  'data': { 'number': 'int' } }
> -{ 'union': 'MyUnion',
> -  'data': { 'one': 'One',
> -            'ONE': 'Two' } }
> diff --git a/tests/qapi-schema/union-bad-branch.out b/tests/qapi-schema/union-bad-branch.out
> deleted file mode 100644
> index e69de29..0000000
> diff --git a/tests/qapi-schema/union-clash-branches.err b/tests/qapi-schema/union-clash-branches.err
> index 005c48d..d8f1265 100644
> --- a/tests/qapi-schema/union-clash-branches.err
> +++ b/tests/qapi-schema/union-clash-branches.err
> @@ -1 +1 @@
> -tests/qapi-schema/union-clash-branches.json:4: Union 'TestUnion' member 'a_b' clashes with 'a-b'
> +tests/qapi-schema/union-clash-branches.json:4: Union 'TestUnion' branch 'a_b' clashes with 'a-b'

   tests/qapi-schema/union-clash-branches.json:4: Member 'a_b' clashes with 'a-b'

Good enough for me.

> diff --git a/tests/qapi-schema/union-clash-branches.json b/tests/qapi-schema/union-clash-branches.json
> index 31d135f..3bece8c 100644
> --- a/tests/qapi-schema/union-clash-branches.json
> +++ b/tests/qapi-schema/union-clash-branches.json
> @@ -1,5 +1,5 @@
>  # Union branch name collision
>  # Reject a union that would result in a collision in generated C names (this
> -# would try to generate two enum values 'TEST_UNION_KIND_A_B').
> +# would try to generate two members 'a_b').
>  { 'union': 'TestUnion',
>    'data': { 'a-b': 'int', 'a_b': 'str' } }
> diff --git a/tests/qapi-schema/union-max.err b/tests/qapi-schema/union-max.err
> index 55ce439..b93beae 100644
> --- a/tests/qapi-schema/union-max.err
> +++ b/tests/qapi-schema/union-max.err
> @@ -1 +1 @@
> -tests/qapi-schema/union-max.json:2: Union 'Union' member 'max' clashes with '(automatic)'
> +tests/qapi-schema/union-max.json:2: Union 'Union' branch 'max' clashes with '(automatic MAX)'

   tests/qapi-schema/union-max.json:2: Member 'max' clashes with '(automatic MAX)'

Good enough for me.

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

* Re: [Qemu-devel] [PATCH v11 22/28] qapi: Simplify visiting of alternate types
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 22/28] qapi: Simplify visiting of alternate types Eric Blake
  2015-11-12 14:21   ` Markus Armbruster
@ 2015-11-12 15:54   ` Markus Armbruster
  2015-11-13 23:54   ` Eric Blake
  2 siblings, 0 replies; 72+ messages in thread
From: Markus Armbruster @ 2015-11-12 15:54 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Previously, working with alternates required two lookup arrays
> and some indirection: for type Foo, we created Foo_qtypes[]
> which maps each qtype to a value of the generated FooKind enum,
> then look up that value in FooKind_lookup[] like we do for other
> union types.
[...]
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index d4ef08e..1790e8f 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -627,15 +627,15 @@ def check_union(expr, expr_info):
>  def check_alternate(expr, expr_info):
>      name = expr['alternate']
>      members = expr['data']
> -    values = {'MAX': '(automatic)'}
> +    values = {}
>      types_seen = {}
>
>      # Check every branch
>      for (key, value) in members.items():
>          check_name(expr_info, "Member of alternate '%s'" % name, key)
>
> -        # Check for conflicts in the generated enum
> -        c_key = camel_to_upper(key)
> +        # Check for conflicts in the branch names
> +        c_key = c_name(key)
>          if c_key in values:
>              raise QAPIExprError(expr_info,
>                                  "Alternate '%s' member '%s' clashes with '%s'"
> @@ -1090,8 +1090,11 @@ class QAPISchemaObjectTypeVariants(object):
>          assert isinstance(self.tag_member.type, QAPISchemaEnumType)
>          for v in self.variants:
>              v.check(schema)
> -            assert v.name in self.tag_member.type.values
> -            if isinstance(v.type, QAPISchemaObjectType):
> +            # Union names must match enum values; alternate names are
> +            # checked separately. Use 'seen' to tell the two apart.
> +            if seen:
> +                assert v.name in self.tag_member.type.values
> +                assert isinstance(v.type, QAPISchemaObjectType)
>                  v.type.check(schema)
>
>      def check_clash(self, schema, info, seen):
> @@ -1133,6 +1136,11 @@ class QAPISchemaAlternateType(QAPISchemaType):
>          # Not calling self.variants.check_clash(), because there's nothing
>          # to clash with
>          self.variants.check(schema, {})
> +        # Alternate branch names have no relation to the tag enum values;
> +        # so we have to check for potential name collisions ourselves.
> +        cases = {}
> +        for var in self.variants.variants:
> +            var.check_clash(self.info, cases)

Nitpick: elsewhere in this file, we use "for v" to iterate over
variants.

>
>      def json_type(self):
>          return 'value'
> @@ -1340,7 +1348,7 @@ class QAPISchema(object):
>          data = expr['data']
>          variants = [self._make_variant(key, value)
>                      for (key, value) in data.iteritems()]
> -        tag_member = self._make_implicit_tag(name, info, variants)
> +        tag_member = QAPISchemaObjectTypeMember('type', 'QTypeCode', False)
>          self._def_entity(
>              QAPISchemaAlternateType(name, info,
>                                      QAPISchemaObjectTypeVariants(None,
[...]

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

* Re: [Qemu-devel] [PATCH v11 28/28] qapi: Detect base class loops
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 28/28] qapi: Detect base class loops Eric Blake
@ 2015-11-12 16:06   ` Markus Armbruster
  0 siblings, 0 replies; 72+ messages in thread
From: Markus Armbruster @ 2015-11-12 16:06 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> It should be fairly obvious that qapi base classes need to
> form an acyclic graph, since QMP cannot specify the same
> key more than once, while base classes are included as flat
> members alongside other members added by the child.  But the
> old check_member_clash() parser function was not prepared to
> check for this, and entered an infinite recursion (at least
> until python gives up, complaining about nesting too deep).

Nitpick: Python.

>
> Now that check_member_clash() has been recently removed,
> attempts at self-inheritance trigger an assertion failure
> introduced by commit ac88219a.  The obvious fix is to turn
> the assertion into a conditional.

Exactly.  Like all the assertions in check() methods, it was always
meant as a placeholder for a semantic check.

> This patch includes both the test and the fix, since the .err
> file output for the unfixed case is not useful (particularly
> when it was warning about unbounded recursion, as that limit
> may be platform-specific).
>
> We don't need to worry about cycles in flat unions (neither
> the base nor a variant class can be a union)

(neither the base type nor the type of a variant ...)

Note that if they could, this check would report the cycle just fine.

>                                              nor in alternates
> (alternate branches cannot themselves be an alternate).

If they could, all it took to detect the cycle would be a v.type.check()
in QAPISchemaAlternateType.check().

>                                                          And
> even the case of using the same class for a flat union base
> class and one its variants is already caught by the fact that
> both uses will introduce the same member name (or will be okay
> if the class is empty).

That's not a cycle, that's a diamond, isn't it?

> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v11 (no v10): rename base-cycle to base-cycle-indirect, and add
> base-cycle-direct; touch up commit message
> v9: no change
> v8: improve commit message
> v7: improve commit message
> v6: rebase to earlier info changes
> ---
>  scripts/qapi.py                            | 6 +++++-
>  tests/Makefile                             | 2 ++
>  tests/qapi-schema/base-cycle-direct.err    | 1 +
>  tests/qapi-schema/base-cycle-direct.exit   | 1 +
>  tests/qapi-schema/base-cycle-direct.json   | 2 ++
>  tests/qapi-schema/base-cycle-direct.out    | 0
>  tests/qapi-schema/base-cycle-indirect.err  | 1 +
>  tests/qapi-schema/base-cycle-indirect.exit | 1 +
>  tests/qapi-schema/base-cycle-indirect.json | 3 +++
>  tests/qapi-schema/base-cycle-indirect.out  | 0
>  10 files changed, 16 insertions(+), 1 deletion(-)
>  create mode 100644 tests/qapi-schema/base-cycle-direct.err
>  create mode 100644 tests/qapi-schema/base-cycle-direct.exit
>  create mode 100644 tests/qapi-schema/base-cycle-direct.json
>  create mode 100644 tests/qapi-schema/base-cycle-direct.out
>  create mode 100644 tests/qapi-schema/base-cycle-indirect.err
>  create mode 100644 tests/qapi-schema/base-cycle-indirect.exit
>  create mode 100644 tests/qapi-schema/base-cycle-indirect.json
>  create mode 100644 tests/qapi-schema/base-cycle-indirect.out
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 08a366e..e60c1d8 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -941,7 +941,11 @@ class QAPISchemaObjectType(QAPISchemaType):
>          self.members = None
>
>      def check(self, schema):
> -        assert self.members is not False        # not running in cycles
> +        if self.members is False:               # check for cycles
> +            assert self._base_name
> +            raise QAPIExprError(self.info,
> +                                "Object %s cyclically depends on %s"
> +                                % (self.name, self._base_name))

Well, it cyclically depends on *itself*.

If the cycle is of length one, this message becomes

    Object Foo cyclically depends on Foo

Sounds awkward.

If the cycle is longer, it becomes

    Object Foo cyclically depends on Bar

where Foo -> Bar happens to be the beginning of the cycle.  Borders on
misleading.

We could print the complete cycle, but that feels like too much work for
too little gain.  Let's dumb down the message to something like "Type
Foo contains itself"[*].
  
>          if self.members:
>              return
>          self.members = False                    # mark as being checked
> diff --git a/tests/Makefile b/tests/Makefile
> index cdff7a4..2706126 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -253,6 +253,8 @@ qapi-schema += bad-ident.json
>  qapi-schema += bad-type-bool.json
>  qapi-schema += bad-type-dict.json
>  qapi-schema += bad-type-int.json
> +qapi-schema += base-cycle-direct.json
> +qapi-schema += base-cycle-indirect.json
>  qapi-schema += command-int.json
>  qapi-schema += comments.json
>  qapi-schema += double-data.json
> diff --git a/tests/qapi-schema/base-cycle-direct.err b/tests/qapi-schema/base-cycle-direct.err
> new file mode 100644
> index 0000000..46fc436
> --- /dev/null
> +++ b/tests/qapi-schema/base-cycle-direct.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/base-cycle-direct.json:2: Object Foo cyclically depends on Foo
> diff --git a/tests/qapi-schema/base-cycle-direct.exit b/tests/qapi-schema/base-cycle-direct.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/base-cycle-direct.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/base-cycle-direct.json b/tests/qapi-schema/base-cycle-direct.json
> new file mode 100644
> index 0000000..dff6fab
> --- /dev/null
> +++ b/tests/qapi-schema/base-cycle-direct.json
> @@ -0,0 +1,2 @@
> +# we reject a loop in base classes
> +{ 'struct': 'Foo', 'base': 'Foo', 'data': {} }

I gather you didn't like 'Loopy'.  Pity...

> diff --git a/tests/qapi-schema/base-cycle-direct.out b/tests/qapi-schema/base-cycle-direct.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/base-cycle-indirect.err b/tests/qapi-schema/base-cycle-indirect.err
> new file mode 100644
> index 0000000..9a1ca44
> --- /dev/null
> +++ b/tests/qapi-schema/base-cycle-indirect.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/base-cycle-indirect.json:2: Object Base1 cyclically depends on Base2
> diff --git a/tests/qapi-schema/base-cycle-indirect.exit b/tests/qapi-schema/base-cycle-indirect.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/base-cycle-indirect.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/base-cycle-indirect.json b/tests/qapi-schema/base-cycle-indirect.json
> new file mode 100644
> index 0000000..2866772
> --- /dev/null
> +++ b/tests/qapi-schema/base-cycle-indirect.json
> @@ -0,0 +1,3 @@
> +# we reject a loop in base classes
> +{ 'struct': 'Base1', 'base': 'Base2', 'data': {} }
> +{ 'struct': 'Base2', 'base': 'Base1', 'data': {} }
> diff --git a/tests/qapi-schema/base-cycle-indirect.out b/tests/qapi-schema/base-cycle-indirect.out
> new file mode 100644
> index 0000000..e69de29


[*] I'm tempted to make it "Foo is a type, not a Klein bottle", but I
acknowledge our users may not share my sense of humor ;)

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

* Re: [Qemu-devel] [PATCH v11 27/28] qapi: Move duplicate enum value checks to schema check()
  2015-11-12 15:46   ` Markus Armbruster
@ 2015-11-12 16:08     ` Eric Blake
  2015-11-18  6:48     ` Eric Blake
  1 sibling, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-12 16:08 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 11/12/2015 08:46 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Similar to the previous commit, move the detection of a collision
>> in enum values from parse time to QAPISchemaEnumType.check().
>> This happens to also detect collisions in union branch names
>> mapping to the same enum value, even when the names do not
>> collide case-wise.  So for a decent error message, we have to
>> determine if the enum is implicit (and if so where the real
>> collision lies).
>>

>> @@ -873,8 +856,30 @@ class QAPISchemaEnumType(QAPISchemaType):
>>          self.values = values
>>          self.prefix = prefix
>>
>> +    def _describe(self, schema):
>> +        # If the enum is implicit, report the error on behalf of
>> +        # the union or alternate that triggered the enum
>> +        if self.is_implicit():
>> +            owner = schema.lookup_type(self.name[:-4])
>> +            assert owner
>> +            if isinstance(owner, QAPISchemaAlternateType):
>> +                return "Alternate '%s' branch" % owner.name
> 
> Didn't we just drop this kind of implicit enum?

D'oh; rebase churn reordering patches.

> 
>> +            else:
>> +                return "Union '%s' branch" % owner.name
>> +        else:
>> +            return "Enum '%s' value" % self.name
> 
> I like to call it "member" rather than value, because it avoids
> confusion with the numeric value of the C enumeration constant generated
> for it.

Sure, that sounds slightly better.

> 
> The conditional isn't exactly elegant, but it would do.  I'm not 100%
> convinced we need it, though.  self.info already points to whatever
> defined this enum, either an explicit enum definition, or a simple union
> definition.  How do the error messages come out if we dumb down to
> "Member '%s'"?
> 
> A method with a similar purpose exists in QAPISchemaObjectTypeMember,
> but it's spelled describe().  It's used only from within the class.

Are you talking about _pretty_owner()? Or the actual describe() that
calls _pretty_owner()?

> Rename it to match this one?
> 
>> +
>>      def check(self, schema):
>> -        assert len(set(self.values)) == len(self.values)
>> +        # Check for collisions on the generated C enum values
>> +        seen = {c_enum_const(self.name, 'MAX'): '(automatic MAX)'}
>> +        for value in self.values:
>> +            c_value = c_enum_const(self.name, value)
>> +            if c_value in seen:
>> +                raise QAPIExprError(self.info,
>> +                                    "%s '%s' clashes with '%s'"
>> +                                    % (self._describe(schema), value,
>> +                                       seen[c_value]))
>> +            seen[c_value] = value
> 
> Loop body is very similar to QAPISchemaObjectTypeMember.check_clash().
> Differences:
> 
> * c_enum_const(enum_name, member_name) vs. c_name(member_name).upper()
> 
>   This isn't really a difference, because the former returns the latter
>   prefixed by a string that doesn't vary in the loop.

Well, it _is_ a difference if c_name() munges differently than
c_enum_const() (the whole question of whether 'OneTwo' and 'One-Two' are
detected as clashes); but then again, I have the patches that try to
rework c_enum_const() to use just c_name().upper().

> 
>   One could argue that using c_enum_const() lets us abstract from what
>   it does, but that's an illusion.  We rely on it when we generate union
>   members called c_name(member_name) without checking for collisions
>   again.
> 
>   By the way, c_enum_const(self.name, value, self.prefix) would be more
>   correct.  Doesn't matter here, of course.
> 
>   Therefore, I'd be very much tempted to use c_name(member_name).upper()
>   here as well.
> 
> * The error message.  But I suspect the same "Member '%s' clashes with
>   '%s'" could do for both.
> 
> If I'm right and we can drop the differences, the common code could
> become a helper function.

I'll play with it and see what pops out.

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

* Re: [Qemu-devel] [PATCH v11 25/28] qapi: Simplify visits of optional fields
  2015-11-12 15:30     ` Eric Blake
@ 2015-11-12 16:20       ` Markus Armbruster
  0 siblings, 0 replies; 72+ messages in thread
From: Markus Armbruster @ 2015-11-12 16:20 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 11/12/2015 08:11 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> None of the visitor callbacks would set an error when testing
>>> if an optional field was present; make this part of the interface
>>> contract by eliminating the errp argument.  Then, for less code,
>>> reflect the determined boolean value back to the caller instead
>>> of making the caller read the boolean after the fact.
>>>
>>> The resulting generated code has a nice diff:
>>>
>>> |-    visit_optional(v, &has_fdset_id, "fdset-id", &err);
>>> |-    if (err) {
>>> |-        goto out;
>>> |-    }
>>> |-    if (has_fdset_id) {
>>> |+    if (visit_optional(v, &has_fdset_id, "fdset-id")) {
>>> |         visit_type_int(v, &fdset_id, "fdset-id", &err);
>>> |         if (err) {
>>> |             goto out;
>>> |         }
>>> |     }
>> 
>> Any particular reason not to do
>> 
>>         has_fdset_id = visit_optional(v, "fdset-id");
>>         if (has_fdset_id) {
>
> We can't. Output visitors do not implement visit_optional() callbacks,
> but must rely on the incoming value of has_fdset_id.  Which means
> assigning to has_fdset_id without an incoming value will do the wrong
> thing.  Or worded differently, &has_fdset_id is modified as an output
> parameter by input visitors, and read unchanged as an input parameter by
> output visitors.

I guess I'd limit myself to just dropping the useless error checking
then.  Results in

    visit_optional(v, &has_fdset_id, "fdset-id")
    if (has_fdset_id) {

which is slightly more verbose but also slightly more obvious.  But it's
all in generated code, so it doesn't really matter much either way.

>>> +++ b/qapi/qapi-visit-core.c
>>> @@ -73,12 +73,12 @@ void visit_end_union(Visitor *v, bool data_present, Error **errp)
>>>      }
>>>  }
>>>
>>> -void visit_optional(Visitor *v, bool *present, const char *name,
>>> -                    Error **errp)
>>> +bool visit_optional(Visitor *v, bool *present, const char *name)
>>>  {
>>>      if (v->optional) {
>>> -        v->optional(v, present, name, errp);
>>> +        v->optional(v, present, name);
>>>      }
>>> +    return *present;
>>>  }
>> 
>> Slightly ugly: struct Visitor method optional returns void, but the
>> wrapper returns bool.
>
> I could make all the callbacks (all 3 of them: opts-visitor,
> qmp-input-visitor, string-input-visitor) return bool, but didn't see the
> point in the churn, especially since the contract of the return value is
> easy to do in the wrapper.

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

* Re: [Qemu-devel] [PATCH v11 20/28] qapi: Forbid case-insensitive clashes
  2015-11-11 14:53   ` Markus Armbruster
@ 2015-11-13  5:32     ` Eric Blake
  0 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-13  5:32 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 11/11/2015 07:53 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> In general, designing user interfaces that rely on case
>> distinction is poor practice.  Another benefit of enforcing
>> a restriction against case-insensitive clashes is that we
>> no longer have to worry about the situation of enum values
>> that could be distinguished by case if mapped by c_name(),
>> but which cannot be distinguished when mapped to C as
>> ALL_CAPS by camel_to_upper().
> 
> With PATCH 19, they're mapped by c_name(N).upper().
> 

Yep, need to reword that, thanks to rebase churn.

>>                                Thus, having the generator
>> look for case collisions up front will prevent developers
>> from worrying whether different munging rules for member
>> names compared to enum values as a discriminator will cause
>> any problems in qapi unions.
>>
>> There is also the possibility that we may want to add a
>> future extension to QMP of teaching it to be case-insensitive
>> (the user could request command 'Quit' instead of 'quit', or
>> could spell a struct field as 'CPU' instead of 'cpu').  But
>> for that to be a practical extension, we cannot break
>> backwards compatibility with any existing struct that was
>> already relying on case sensitivity.  Fortunately, after the
>> previous patch cleaned up CpuInfo, there are no such existing
>> qapi structs.  Of course, the idea of a future extension is
>> not as strong of a reason to make the change.
>>
>> At any rate, it is easier to be strict now, and relax things
>> later if we find a reason to need case-sensitive QMP members,
>> than it would be to wish we had the restriction in place.
> 
> Suggest to briefly mention the new test.

Will do.

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

Hmm - this only enforces member name case clashes.  It would also be
worth enforcing command/event/type collisions (technically, only
command/event clashes are user-visible, but tracking all entities is
probably easier) - probably by modifying QAPISchema._def_entity() and
.lookup_entity() to do the same trick of keying the map by a munged
name.  I'll see about adding that as an additional 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] 72+ messages in thread

* Re: [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values Eric Blake
  2015-11-11 13:29   ` Andreas Färber
  2015-11-11 14:50   ` Markus Armbruster
@ 2015-11-13 17:46   ` Eric Blake
  2015-11-13 18:13     ` Markus Armbruster
  2 siblings, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-13 17:46 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Peter Maydell, open list:Block layer core,
	Michael S. Tsirkin, Jason Wang, Michael Roth, armbru,
	Gerd Hoffmann, Amit Shah, Luiz Capitulino, Andreas Färber

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

On 11/10/2015 11:51 PM, Eric Blake wrote:
> When munging enum values, the fact that we were passing the entire
> prefix + value through camel_to_upper() meant that enum values
> spelled with CamelCase could be turned into CAMEL_CASE.  However,
> this provides a potential collision (both OneTwo and One-Two would
> munge into ONE_TWO).  By changing the generation of enum constants
> to always be prefix + '_' + c_name(value).upper(), we can avoid
> any risk of collisions (if we can also ensure no case collisions,
> in the next patch) without having to think about what the
> heuristics in camel_to_upper() will actually do to the value.
> 

> +++ b/scripts/qapi.py
> @@ -1439,7 +1439,7 @@ def camel_to_upper(value):
>  def c_enum_const(type_name, const_name, prefix=None):
>      if prefix is not None:
>          type_name = prefix
> -    return camel_to_upper(type_name + '_' + const_name)
> +    return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()

Doesn't match the commit message, because it used c_name(,False), while
c_name(name) is short for c_name(name, True).

What's more, looking at it exposed a bug: c_name('_Thread-local')
returns '_Thread_local', but this collides with a C11 keyword (and was
supposed to be munged to q__Thread_local); add '_Thread-local':'int' to
a struct to see the resulting hilarity:

  CC    tests/test-qmp-output-visitor.o
In file included from tests/test-qmp-output-visitor.c:17:0:
tests/test-qapi-types.h:781:13: error: expected identifier or ‘(’ before
‘_Thread_local’
     int64_t _Thread_local;
             ^

But it also made me realize that c_name('Q-int') happily returns 'Q_int'
(we only reserved the leading 'q_' namespace, not 'Q_').  Alas, that
means c_name('Q-int', False).upper() and c_name('int', False).upper()
both produce 'Q_INT', and we have a collision.  So I think enum names
have to be munged by c_name(name, True).

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

* Re: [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values
  2015-11-13 17:46   ` Eric Blake
@ 2015-11-13 18:13     ` Markus Armbruster
  2015-11-13 21:37       ` Eric Blake
  0 siblings, 1 reply; 72+ messages in thread
From: Markus Armbruster @ 2015-11-13 18:13 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, Peter Maydell, open list:Block layer core,
	Michael S. Tsirkin, Jason Wang, qemu-devel, Michael Roth,
	Gerd Hoffmann, Amit Shah, Luiz Capitulino, Andreas Färber

Eric Blake <eblake@redhat.com> writes:

> On 11/10/2015 11:51 PM, Eric Blake wrote:
>> When munging enum values, the fact that we were passing the entire
>> prefix + value through camel_to_upper() meant that enum values
>> spelled with CamelCase could be turned into CAMEL_CASE.  However,
>> this provides a potential collision (both OneTwo and One-Two would
>> munge into ONE_TWO).  By changing the generation of enum constants
>> to always be prefix + '_' + c_name(value).upper(), we can avoid
>> any risk of collisions (if we can also ensure no case collisions,
>> in the next patch) without having to think about what the
>> heuristics in camel_to_upper() will actually do to the value.
>> 
>
>> +++ b/scripts/qapi.py
>> @@ -1439,7 +1439,7 @@ def camel_to_upper(value):
>>  def c_enum_const(type_name, const_name, prefix=None):
>>      if prefix is not None:
>>          type_name = prefix
>> -    return camel_to_upper(type_name + '_' + const_name)
>> +    return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
>
> Doesn't match the commit message, because it used c_name(,False), while
> c_name(name) is short for c_name(name, True).
>
> What's more, looking at it exposed a bug: c_name('_Thread-local')
> returns '_Thread_local', but this collides with a C11 keyword (and was
> supposed to be munged to q__Thread_local); add '_Thread-local':'int' to
> a struct to see the resulting hilarity:
>
>   CC    tests/test-qmp-output-visitor.o
> In file included from tests/test-qmp-output-visitor.c:17:0:
> tests/test-qapi-types.h:781:13: error: expected identifier or ‘(’ before
> ‘_Thread_local’
>      int64_t _Thread_local;
>              ^

c_name() first protects ticklish identifiers, then mangles.  That's
exactly backwards.

> But it also made me realize that c_name('Q-int') happily returns 'Q_int'
> (we only reserved the leading 'q_' namespace, not 'Q_').  Alas, that
> means c_name('Q-int', False).upper() and c_name('int', False).upper()
> both produce 'Q_INT', and we have a collision.  So I think enum names
> have to be munged by c_name(name, True).

Our goal is to have simple rules for reserved names and collisions, and
resonably simple code to catch them.

"[PATCH 20] qapi: Forbid case-insensitive clashes" is incomplete --- if
we make clashing case-insensitive, we need to reserve names
case-insensitively, too.

We need c_name() to protect ticklish identifiers only when its result is
used as identifier.  Not when it's *part* of an identifier,
e.g. prefixed with qapi_, or camel_to_upper(type_name) + '_'.

We can protect even when we don't need to, if that helps keeping things
simple.

The obvious simple way to check for collisions works like this:

1. Every QAPI name is mangled in exactly one way, modulo case: always
   with c_name(), and always with the same value of protect.

2. We require the mangled name to be case-insensitively unique in its
   name space.

Any holes left?

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

* Re: [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values
  2015-11-13 18:13     ` Markus Armbruster
@ 2015-11-13 21:37       ` Eric Blake
  2015-11-16 14:30         ` Markus Armbruster
  0 siblings, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-13 21:37 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Peter Maydell, open list:Block layer core,
	Michael S. Tsirkin, Jason Wang, qemu-devel, Michael Roth,
	Gerd Hoffmann, Amit Shah, Luiz Capitulino, Andreas Färber

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

On 11/13/2015 11:13 AM, Markus Armbruster wrote:

> We need c_name() to protect ticklish identifiers only when its result is
> used as identifier.  Not when it's *part* of an identifier,
> e.g. prefixed with qapi_, or camel_to_upper(type_name) + '_'.
> 
> We can protect even when we don't need to, if that helps keeping things
> simple.

As far as I can tell, as soon as we reserve Q_ in addition to q_, and
fix the bug in c_name() applying munging backwards, then we are safe to
state that any comparison between two names for collisions will be
accurate as long as both sides of the comparison picked the same value
of c_name(name, protect), whether or not our later use of that name is
done with protect=True or protect=False.

> 
> The obvious simple way to check for collisions works like this:
> 
> 1. Every QAPI name is mangled in exactly one way, modulo case: always
>    with c_name(), and always with the same value of protect.

That part is easy.  Maybe I could even reuse guardname(name) instead of
c_name(name).upper(), although I don't know that it buys any clarity.

> 
> 2. We require the mangled name to be case-insensitively unique in its
>    name space.

That part is a bit harder: we unfortunately have the existing clash
between the command 'stop' and the event 'STOP'.  I didn't find any
other clashes in existing clients, though (phew), so we can either
whitelist just that one, or more likely, set up separate namespaces for
commands vs. events when doing the case collision tests.

But it's a bummer that it won't be quite as easy as I had hoped (using a
single dictionary for both lookups and case collisions is easier than
having two dictionaries to separate namespaces, while still searching
both dictionaries for lookups).

At any rate, I'm playing with patches along these lines.

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

* Re: [Qemu-devel] [PATCH v11 22/28] qapi: Simplify visiting of alternate types
  2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 22/28] qapi: Simplify visiting of alternate types Eric Blake
  2015-11-12 14:21   ` Markus Armbruster
  2015-11-12 15:54   ` Markus Armbruster
@ 2015-11-13 23:54   ` Eric Blake
  2015-11-16 14:31     ` Markus Armbruster
  2 siblings, 1 reply; 72+ messages in thread
From: Eric Blake @ 2015-11-13 23:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

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

On 11/10/2015 11:51 PM, Eric Blake wrote:
> Previously, working with alternates required two lookup arrays
> and some indirection: for type Foo, we created Foo_qtypes[]
> which maps each qtype to a value of the generated FooKind enum,
> then look up that value in FooKind_lookup[] like we do for other
> union types.
> 
> This has a couple of subtle bugs.  First, the generator was
> creating a call with a parameter '(int *) &(*obj)->type' where
> type is an enum type; this is unsafe if the compiler chooses
> to store the enum type in a different size than int, where
> assigning through the wrong size pointer can corrupt data or
> cause a SIGBUS.

Yuck - we still have this bug for enums:

> void visit_type_OnOffAuto(Visitor *v, OnOffAuto *obj, const char *name, Error **errp)
> {
> visit_type_enum(v, (int *)obj, OnOffAuto_lookup, "OnOffAuto", name, errp);
> }

I guess I just added more work to my plate.

But at the same time, no one has complained of the (int*)Enum causing
SIGBUS on any of our platforms for either enums or alternates, so while
we are doing gross things, I don't think we have to rush a fix into 2.5
for this particular bug.

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

* Re: [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values
  2015-11-13 21:37       ` Eric Blake
@ 2015-11-16 14:30         ` Markus Armbruster
  0 siblings, 0 replies; 72+ messages in thread
From: Markus Armbruster @ 2015-11-16 14:30 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, Peter Maydell, open list:Block layer core,
	Michael S. Tsirkin, Jason Wang, qemu-devel, Michael Roth,
	Gerd Hoffmann, Amit Shah, Luiz Capitulino, Andreas Färber

Eric Blake <eblake@redhat.com> writes:

> On 11/13/2015 11:13 AM, Markus Armbruster wrote:
>
>> We need c_name() to protect ticklish identifiers only when its result is
>> used as identifier.  Not when it's *part* of an identifier,
>> e.g. prefixed with qapi_, or camel_to_upper(type_name) + '_'.
>> 
>> We can protect even when we don't need to, if that helps keeping things
>> simple.
>
> As far as I can tell, as soon as we reserve Q_ in addition to q_, and
> fix the bug in c_name() applying munging backwards, then we are safe to
> state that any comparison between two names for collisions will be
> accurate as long as both sides of the comparison picked the same value
> of c_name(name, protect), whether or not our later use of that name is
> done with protect=True or protect=False.

Okay.

>> The obvious simple way to check for collisions works like this:
>> 
>> 1. Every QAPI name is mangled in exactly one way, modulo case: always
>>    with c_name(), and always with the same value of protect.
>
> That part is easy.  Maybe I could even reuse guardname(name) instead of
> c_name(name).upper(), although I don't know that it buys any clarity.

Certainly not without a rename :)

>> 2. We require the mangled name to be case-insensitively unique in its
>>    name space.
>
> That part is a bit harder: we unfortunately have the existing clash
> between the command 'stop' and the event 'STOP'.  I didn't find any
> other clashes in existing clients, though (phew), so we can either
> whitelist just that one, or more likely, set up separate namespaces for
> commands vs. events when doing the case collision tests.

I think the natural split is three: commands, events, types.

> But it's a bummer that it won't be quite as easy as I had hoped (using a
> single dictionary for both lookups and case collisions is easier than
> having two dictionaries to separate namespaces, while still searching
> both dictionaries for lookups).
>
> At any rate, I'm playing with patches along these lines.

Before I dive into yet another lengthy treatise: I strongly recommend to
kick clash detection work as far back in the queue as practical.  It's
hairy, and improving it isn't a priority.  It has clogged the queue more
than enough already.


Let's take a step back and consider requirements.  I think there are
broadly two: the QAPI schema must make sense, and generation of clashing
C identifiers should be caught.


QAPI's requirements are pretty simple.  It's always clear whether a name
denotes a command, event, type, or member of a certain type.  Thus,
requiring names to be unique within their kind suffices.  In other
words, separate name spaces for commands, events, types and for each
type's members.

To avoid confusion, we can require more.  Right now, we require command,
event and type names to be unique within their combined kinds.  In other
words, a combined name space for commands, events and types.


Catching C clashes is more complicated, because we need to analyze the
generators' (less than disciplined) use of C's name spaces to find its
requirements.

QAPI names get mangled and combined with prefixes or suffixes to become
C identifiers.

Prefixes and suffixes can only reduce clashes among generated names
(they can require reserving names, but let's ignore that here).
Therefore, considering clashes between mangled QAPI names suffices.

If we mangled each name in exactly one way, this would be trivial: use
the mangled name as dictionary key, and use a separate dictionary for
each C name space.

Note it's trivial even with multiple ways to mangle, as long as each
name gets mangled in exactly one of them.

Unfortunately, this is not the case.

We mangle in the following ways:

* Standard mangling with c_name()

  Variations: protect=False, shouted, unshouted.

* camel_to_upper() mangling

Names that get mangled in more than one way, to the best of my
knowledge:

(1) Enumeration member names

  - Shouted standard mangling for the enumeration constant
  - Standard mangling for the union member when used as variant tag

(2) Enumeration type names

  - Standard mangling for the typedef
  - camel_to_upper() mangling for the enumeration constants

(3) Event names

  - Unshouted standard mangling for the function to emit the event
  - Shouted standard mangling for the enumeration constants

(4) Possibly built-in type names

  - Standard mangling with protect=False
  - If we screw up somehow, standard mangling without protect=False

General solution: put all possible manglings into the name space's
dictionary.  For example, enter a command name in standard mangling, but
enter an event name twice, once in unshouted standard mangling, and once
in shouted standard mangling.

If we peek into a few black boxes, we can simplify things a bit.

We use C's ordinary identifier name space, the struct tag name space,
and the member name spaces of structs and unions we generate.

We don't need dictionaries for the C struct, union and enum tag name
spaces, because we always use the same identifier in the ordinary name
space.

We can safely ignore protect=False since we reserve the q_ prefix in all
variations, and avoid its use outside c_name().  Standard mangling as
key suffices for (4).

We can ignore a mangling when we can prove it can clash only when
another one clashes:

* An event name's unshouted standard mangling can clash only when the
  shouted mangling clashes.  Down to one key, but it's still an unusual
  one.

* If we enforce "enumeration member names must be lower-case", then
  shouted standard mangling can clash only when standard mangling
  clashes.  Use of standard mangling as key suffices for (1).

We can cover *all* case variations of standard mangling by making the
dictionary case-insensitive.  Unfortunately, this runs into the STOP
vs. stop roadblock in the ordinary identifier dictionary.  No go.

We could split the ordinary identifier dictionary into events, commands
and types (the split that exists in QAPI) if generators ensure
identifiers from different parts can't clash, by restricting names or by
adding prefixes or siffixes.  I think that's the case, but it's a
non-trivial argument.  The resulting dictionaries could then be made
case-insensitive separately.  But this is getting too complex for my
taste.

Instead of or in addition to simplifying clash detection, we can
simplify mangling:

* If we enforce "event name must be ALL_CAPS", then shouted standard
  mangling can be trivially replaced by standard mangling.  Since we
  already showed that unshouted standard mangling can be ignored, use of
  standard mangling as key suffices for (3).

* That leaves (2).  To make standard mangling suffice there as well, we
  can simply replace camel_to_upper() mangling by standard mangling.  If
  that's inopportune, we'll need to find another solution.

To be honest, I'm *this* close to giving up on clash detection entirely.
It's 100% self-inflicted pain.

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

* Re: [Qemu-devel] [PATCH v11 22/28] qapi: Simplify visiting of alternate types
  2015-11-13 23:54   ` Eric Blake
@ 2015-11-16 14:31     ` Markus Armbruster
  0 siblings, 0 replies; 72+ messages in thread
From: Markus Armbruster @ 2015-11-16 14:31 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 11/10/2015 11:51 PM, Eric Blake wrote:
>> Previously, working with alternates required two lookup arrays
>> and some indirection: for type Foo, we created Foo_qtypes[]
>> which maps each qtype to a value of the generated FooKind enum,
>> then look up that value in FooKind_lookup[] like we do for other
>> union types.
>> 
>> This has a couple of subtle bugs.  First, the generator was
>> creating a call with a parameter '(int *) &(*obj)->type' where
>> type is an enum type; this is unsafe if the compiler chooses
>> to store the enum type in a different size than int, where
>> assigning through the wrong size pointer can corrupt data or
>> cause a SIGBUS.
>
> Yuck - we still have this bug for enums:
>
>> void visit_type_OnOffAuto(Visitor *v, OnOffAuto *obj, const char
>> *name, Error **errp)
>> {
>> visit_type_enum(v, (int *)obj, OnOffAuto_lookup, "OnOffAuto", name, errp);
>> }
>
> I guess I just added more work to my plate.
>
> But at the same time, no one has complained of the (int*)Enum causing
> SIGBUS on any of our platforms for either enums or alternates, so while
> we are doing gross things, I don't think we have to rush a fix into 2.5
> for this particular bug.

If it was a new bug, I'd push for fixing it in 2.5.  Since it's ancient,
I think we should leave it alone in 2.5.

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

* Re: [Qemu-devel] [PATCH v11 21/28] qapi: Convert qtype_code into qapi enum type
  2015-11-12 13:16       ` Markus Armbruster
@ 2015-11-18  6:27         ` Eric Blake
  0 siblings, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-18  6:27 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Luiz Capitulino, Michael Roth,
	open list:Block layer core, qemu-devel

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

On 11/12/2015 06:16 AM, Markus Armbruster wrote:

> We have one in visit_needed(), and we use its is_implicit() to skip
> implicit object types.  We could use entity.is_builtin() to skip (some)
> builtins, and handle them elsewhere, but that doesn't feel like an
> improvement over your code.
> 
> Let's take a step back and reconsider how we do builtins.
> 
>>> +            self._btin += gen_enum(name, values, prefix)
>>> +            if do_builtins:
>>> +                self.defn += gen_enum_lookup(name, values, prefix)
>>> +        else:
>>> +            self._fwdecl += gen_enum(name, values, prefix)
>>> +            self._fwdefn += gen_enum_lookup(name, values, prefix)
>>>
>>>      def visit_array_type(self, name, info, element_type):
>>>          if isinstance(element_type, QAPISchemaBuiltinType):
> 
> Linking generated code from multiple schemata that share names may fail,
> because multiple definitions of the same external symbol exist.
> 
> Example: two schemata both define enum BadIdea.  Both generate const
> char *BadIdea_lookup[] = { ... }, and we end up with two global symbols
> BadIdea_lookup.
> 
> Solution: don't do that then.  Easy enough, except *all* schemata share
> the builtin symbols!  Solution:
[...]

Yes, the existing solution is a bit ugly.  We don't even need the
secondary test-qapi-types.h to declare builtins, since it is relying on
the top-level qapi-types.h to do it, so we are just wasting disk space.

> Here's an alternative solution that permits slightly code simpler
> generator code, and thus avoids the need to know:
> 
> * Generate code for builtins exactly the same as for any other entities,
>   i.e. get rid of self._btin and the ifdeffery.
> 
> * If the program links just one generated schema, this just works.
> 
> * If the program links multiple generated schemata, the programmer has
>   to ensure their definitions get generated just once, and their
>   declarations are available everywhere anyway.  Straightforward method:
> 
>   - The programmer suppresses builtins *completely* for *all* schemata.
>     The obvious way to suppress them is to filter them out in
>     visit_needed().
> 
>   - Instead, he generates them once for the *empty* schema, with a
>     well-known --prefix.
> 
>   - Suppressing builtins generates a suitable #include for the
>     well-known .h with the builtin declarations.
> 
>   - Additionally link the .c containing the builtin definitions.

Reasonably nice ideas.  I'll play with them later, though, in the
interest of getting the existing patch queue flushed first.

> 
> Alternatively, trade some ease-of-use for the single schema case for
> ease-of-use for the multiple schemata case and fewer cases:
> 
> * The generators either generate for a schema, or they generate builtins.
> 
> * When they generate builtins, they always use well-known file names.
> 
> * When they generate for a schema, they always generate the #include for
>   the well-known builtin .h.  They never generate builtins.

I'd probably go with this variant (that is, set up the makefiles to run
once with -b to generate builtins, and multiple times (one per .json)
without -b to generate non-builtins.

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

* Re: [Qemu-devel] [PATCH v11 27/28] qapi: Move duplicate enum value checks to schema check()
  2015-11-12 15:46   ` Markus Armbruster
  2015-11-12 16:08     ` Eric Blake
@ 2015-11-18  6:48     ` Eric Blake
  1 sibling, 0 replies; 72+ messages in thread
From: Eric Blake @ 2015-11-18  6:48 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 11/12/2015 08:46 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Similar to the previous commit, move the detection of a collision
>> in enum values from parse time to QAPISchemaEnumType.check().
>> This happens to also detect collisions in union branch names
>> mapping to the same enum value, even when the names do not
>> collide case-wise.  So for a decent error message, we have to
>> determine if the enum is implicit (and if so where the real
>> collision lies).
>>

>> +    def _describe(self, schema):
>> +        # If the enum is implicit, report the error on behalf of
>> +        # the union or alternate that triggered the enum
>> +        if self.is_implicit():
>> +            owner = schema.lookup_type(self.name[:-4])
>> +            assert owner
>> +            if isinstance(owner, QAPISchemaAlternateType):
>> +                return "Alternate '%s' branch" % owner.name
> 
> Didn't we just drop this kind of implicit enum?
> 
>> +            else:
>> +                return "Union '%s' branch" % owner.name
>> +        else:
>> +            return "Enum '%s' value" % self.name
> 
> I like to call it "member" rather than value, because it avoids
> confusion with the numeric value of the C enumeration constant generated
> for it.
> 
> The conditional isn't exactly elegant, but it would do.  I'm not 100%
> convinced we need it, though.  self.info already points to whatever
> defined this enum, either an explicit enum definition, or a simple union
> definition.  How do the error messages come out if we dumb down to
> "Member '%s'"?
> 
> A method with a similar purpose exists in QAPISchemaObjectTypeMember,
> but it's spelled describe().  It's used only from within the class.
> Rename it to match this one?

[as much notes to myself as anything else...]

We chatted some on IRC, and had some more ideas, thus I will delay
patches 26-28 to a later date while posting the rest of v12.  Among
those ideas
 - getting rid of the enum _MAX clash will simplify things (to be done
in v12)
 - create a new QAPISchemaMember class as the superclass of
QAPISchemaObjectTypeMember, and have enum members be instances of this
type rather than a straight string (at which point the describe() method
and collision checking lives in the superclass, while the associated
type only lives in the subclass). Less code duplication that way
 - the idea of multiline error messages, only when info !=
member.owner.info; as in:
foo.json:4: Member 'one' clashes with 'One'
foo.json:2: Member 'one' defined here

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

end of thread, other threads:[~2015-11-18  6:48 UTC | newest]

Thread overview: 72+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-11-11  6:51 [Qemu-devel] [PATCH v11 00/28] qapi member collision, alternate layout (post-introspection cleanups, subset D) Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 01/28] qapi: Track simple union tag in object.local_members Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 02/28] qapi-types: Consolidate gen_struct() and gen_union() Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 03/28] qapi-types: Simplify gen_struct_field[s] Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 04/28] qapi: Drop obsolete tag value collision assertions Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 05/28] qapi: Simplify QAPISchemaObjectTypeMember.check() Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 06/28] qapi: Clean up after previous commit Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 07/28] qapi: Fix up commit 7618b91's clash sanity checking change Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 08/28] qapi: Eliminate QAPISchemaObjectType.check() variable members Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 09/28] qapi: Factor out QAPISchemaObjectTypeMember.check_clash() Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 10/28] qapi: Simplify QAPISchemaObjectTypeVariants.check() Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 11/28] qapi: Check for qapi collisions of flat union branches Eric Blake
2015-11-11 13:42   ` Markus Armbruster
2015-11-11 15:49     ` Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 12/28] qapi: Factor out QAPISchemaObjectType.check_clash() Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 13/28] qapi: Hoist tag collision check to Variants.check() Eric Blake
2015-11-11 13:56   ` Markus Armbruster
2015-11-11 16:11     ` Eric Blake
2015-11-11 17:03       ` Markus Armbruster
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 14/28] qapi: Remove outdated tests related to QMP/branch collisions Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 15/28] qapi: Track owner of each object member Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 16/28] qapi: Detect collisions in C member names Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 17/28] cpu: Convert CpuInfo into flat union Eric Blake
2015-11-11 14:13   ` Markus Armbruster
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 18/28] qerror: more error_setg() usage Eric Blake
2015-11-11 13:26   ` Andreas Färber
2015-11-11 14:21   ` Markus Armbruster
2015-11-11 14:23     ` Andreas Färber
2015-11-11 15:51       ` Eric Blake
2015-11-11 16:19     ` Eric Blake
2015-11-11 17:31       ` Markus Armbruster
2015-11-11 17:44         ` Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 19/28] qapi: Change munging of CamelCase enum values Eric Blake
2015-11-11 13:29   ` Andreas Färber
2015-11-11 14:50   ` Markus Armbruster
2015-11-11 16:03     ` Eric Blake
2015-11-11 17:11       ` Markus Armbruster
2015-11-12  8:34         ` Gerd Hoffmann
2015-11-12 11:16           ` Markus Armbruster
2015-11-12  8:29       ` Gerd Hoffmann
2015-11-11 16:06     ` Eric Blake
2015-11-13 17:46   ` Eric Blake
2015-11-13 18:13     ` Markus Armbruster
2015-11-13 21:37       ` Eric Blake
2015-11-16 14:30         ` Markus Armbruster
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 20/28] qapi: Forbid case-insensitive clashes Eric Blake
2015-11-11 14:53   ` Markus Armbruster
2015-11-13  5:32     ` Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 21/28] qapi: Convert qtype_code into qapi enum type Eric Blake
2015-11-11 16:42   ` Markus Armbruster
2015-11-11 17:03     ` Eric Blake
2015-11-12 13:16       ` Markus Armbruster
2015-11-18  6:27         ` Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 22/28] qapi: Simplify visiting of alternate types Eric Blake
2015-11-12 14:21   ` Markus Armbruster
2015-11-12 15:54   ` Markus Armbruster
2015-11-13 23:54   ` Eric Blake
2015-11-16 14:31     ` Markus Armbruster
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 23/28] qapi: Fix alternates that accept 'number' but not 'int' Eric Blake
2015-11-12 15:01   ` Markus Armbruster
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 24/28] qapi: Add positive tests to qapi-schema-test Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 25/28] qapi: Simplify visits of optional fields Eric Blake
2015-11-12 15:11   ` Markus Armbruster
2015-11-12 15:30     ` Eric Blake
2015-11-12 16:20       ` Markus Armbruster
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 26/28] qapi: Move duplicate member checks to schema check() Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 27/28] qapi: Move duplicate enum value " Eric Blake
2015-11-12 15:46   ` Markus Armbruster
2015-11-12 16:08     ` Eric Blake
2015-11-18  6:48     ` Eric Blake
2015-11-11  6:51 ` [Qemu-devel] [PATCH v11 28/28] qapi: Detect base class loops Eric Blake
2015-11-12 16:06   ` 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.