Browse Source

Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2021-09-25-v2' into staging

QAPI patches patches for 2021-09-25

# gpg: Signature made Mon 27 Sep 2021 13:44:23 BST
# gpg:                using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653
# gpg:                issuer "armbru@redhat.com"
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full]
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>" [full]
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867  4E5F 3870 B400 EB91 8653

* remotes/armbru/tags/pull-qapi-2021-09-25-v2: (25 commits)
  tests/qapi-schema: Make test-qapi.py -u work when files are absent
  tests/qapi-schema: Use Python OSError instead of outmoded IOError
  test-clone-visitor: Correct an accidental rename
  tests/qapi-schema: Rename flat-union-* test cases to union-*
  qapi: Drop simple unions
  tests/qapi-schema: Purge simple unions from tests
  tests/qapi-schema: Drop simple union __org.qemu_x-Union1
  test-clone-visitor: Wean off __org.qemu_x-Union1
  tests/qapi-schema: Rewrite simple union TestIfUnion to be flat
  tests/qapi-schema: Simple union UserDefListUnion is now unused, drop
  tests/qapi-schema: Wean off UserDefListUnion
  test-clone-visitor: Wean off UserDefListUnion
  test-qobject-output-visitor: Wean off UserDefListUnion
  test-qobject-input-visitor: Wean off UserDefListUnion
  tests/qapi-schema: Prepare for simple union UserDefListUnion removal
  qapi: Convert simple union TransactionAction to flat one
  qapi: Convert simple union ImageInfoSpecific to flat one
  qapi: Convert simple union SocketAddressLegacy to flat one
  qapi: Convert simple union ChardevBackend to flat one
  qapi: Convert simple union MemoryDeviceInfo to flat one
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Peter Maydell 3 năm trước cách đây
mục cha
commit
de8ed1055c
100 tập tin đã thay đổi với 718 bổ sung550 xóa
  1. 1 1
      backends/tpm/tpm_emulator.c
  2. 1 1
      backends/tpm/tpm_passthrough.c
  3. 3 3
      chardev/char-socket.c
  4. 2 2
      chardev/char-udp.c
  5. 29 106
      docs/devel/qapi-code-gen.rst
  6. 4 4
      monitor/hmp-cmds.c
  7. 52 7
      qapi/block-core.json
  8. 168 22
      qapi/char.json
  9. 38 4
      qapi/machine.json
  10. 39 7
      qapi/sockets.json
  11. 22 2
      qapi/tpm.json
  12. 113 26
      qapi/transaction.json
  13. 66 6
      qapi/ui.json
  14. 9 18
      scripts/qapi/expr.py
  15. 22 79
      scripts/qapi/schema.py
  16. 1 1
      tests/qapi-schema/args-union.err
  17. 7 1
      tests/qapi-schema/args-union.json
  18. 1 1
      tests/qapi-schema/bad-base.err
  19. 7 1
      tests/qapi-schema/bad-base.json
  20. 2 11
      tests/qapi-schema/doc-good.json
  21. 0 22
      tests/qapi-schema/doc-good.out
  22. 0 20
      tests/qapi-schema/doc-good.txt
  23. 2 2
      tests/qapi-schema/enum-if-invalid.json
  24. 0 2
      tests/qapi-schema/flat-union-array-branch.err
  25. 0 2
      tests/qapi-schema/flat-union-bad-base.err
  26. 0 2
      tests/qapi-schema/flat-union-bad-discriminator.err
  27. 0 2
      tests/qapi-schema/flat-union-base-any.err
  28. 0 2
      tests/qapi-schema/flat-union-base-union.err
  29. 0 2
      tests/qapi-schema/flat-union-clash-member.err
  30. 0 2
      tests/qapi-schema/flat-union-discriminator-bad-name.err
  31. 0 2
      tests/qapi-schema/flat-union-empty.err
  32. 0 4
      tests/qapi-schema/flat-union-empty.json
  33. 0 2
      tests/qapi-schema/flat-union-inline-invalid-dict.err
  34. 0 2
      tests/qapi-schema/flat-union-int-branch.err
  35. 0 2
      tests/qapi-schema/flat-union-invalid-branch-key.err
  36. 0 2
      tests/qapi-schema/flat-union-invalid-discriminator.err
  37. 0 2
      tests/qapi-schema/flat-union-invalid-if-discriminator.err
  38. 0 2
      tests/qapi-schema/flat-union-no-base.err
  39. 0 2
      tests/qapi-schema/flat-union-optional-discriminator.err
  40. 0 2
      tests/qapi-schema/flat-union-string-discriminator.err
  41. 0 0
      tests/qapi-schema/flat-union-string-discriminator.out
  42. 15 20
      tests/qapi-schema/meson.build
  43. 25 26
      tests/qapi-schema/qapi-schema-test.json
  44. 28 88
      tests/qapi-schema/qapi-schema-test.out
  45. 1 1
      tests/qapi-schema/reserved-member-u.json
  46. 0 2
      tests/qapi-schema/reserved-type-kind.err
  47. 0 2
      tests/qapi-schema/reserved-type-kind.json
  48. 0 0
      tests/qapi-schema/reserved-type-kind.out
  49. 15 5
      tests/qapi-schema/test-qapi.py
  50. 2 0
      tests/qapi-schema/union-array-branch.err
  51. 1 1
      tests/qapi-schema/union-array-branch.json
  52. 0 0
      tests/qapi-schema/union-array-branch.out
  53. 2 0
      tests/qapi-schema/union-bad-base.err
  54. 0 0
      tests/qapi-schema/union-bad-base.json
  55. 0 0
      tests/qapi-schema/union-bad-base.out
  56. 2 0
      tests/qapi-schema/union-bad-discriminator.err
  57. 0 0
      tests/qapi-schema/union-bad-discriminator.json
  58. 0 0
      tests/qapi-schema/union-bad-discriminator.out
  59. 2 0
      tests/qapi-schema/union-base-any.err
  60. 0 0
      tests/qapi-schema/union-base-any.json
  61. 0 0
      tests/qapi-schema/union-base-any.out
  62. 1 1
      tests/qapi-schema/union-base-empty.json
  63. 1 1
      tests/qapi-schema/union-base-no-discriminator.err
  64. 1 1
      tests/qapi-schema/union-base-no-discriminator.json
  65. 2 0
      tests/qapi-schema/union-base-union.err
  66. 3 0
      tests/qapi-schema/union-base-union.json
  67. 0 0
      tests/qapi-schema/union-base-union.out
  68. 0 2
      tests/qapi-schema/union-branch-case.err
  69. 0 2
      tests/qapi-schema/union-branch-case.json
  70. 0 0
      tests/qapi-schema/union-branch-case.out
  71. 1 1
      tests/qapi-schema/union-branch-invalid-dict.err
  72. 4 0
      tests/qapi-schema/union-branch-invalid-dict.json
  73. 0 2
      tests/qapi-schema/union-clash-branches.err
  74. 0 7
      tests/qapi-schema/union-clash-branches.json
  75. 0 0
      tests/qapi-schema/union-clash-branches.out
  76. 2 0
      tests/qapi-schema/union-clash-member.err
  77. 0 0
      tests/qapi-schema/union-clash-member.json
  78. 0 0
      tests/qapi-schema/union-clash-member.out
  79. 2 0
      tests/qapi-schema/union-discriminator-bad-name.err
  80. 0 0
      tests/qapi-schema/union-discriminator-bad-name.json
  81. 0 0
      tests/qapi-schema/union-discriminator-bad-name.out
  82. 1 1
      tests/qapi-schema/union-empty.err
  83. 4 2
      tests/qapi-schema/union-empty.json
  84. 2 0
      tests/qapi-schema/union-inline-invalid-dict.err
  85. 0 0
      tests/qapi-schema/union-inline-invalid-dict.json
  86. 0 0
      tests/qapi-schema/union-inline-invalid-dict.out
  87. 2 0
      tests/qapi-schema/union-int-branch.err
  88. 1 1
      tests/qapi-schema/union-int-branch.json
  89. 0 0
      tests/qapi-schema/union-int-branch.out
  90. 2 0
      tests/qapi-schema/union-invalid-branch-key.err
  91. 0 0
      tests/qapi-schema/union-invalid-branch-key.json
  92. 0 0
      tests/qapi-schema/union-invalid-branch-key.out
  93. 2 0
      tests/qapi-schema/union-invalid-discriminator.err
  94. 0 0
      tests/qapi-schema/union-invalid-discriminator.json
  95. 0 0
      tests/qapi-schema/union-invalid-discriminator.out
  96. 2 0
      tests/qapi-schema/union-invalid-if-discriminator.err
  97. 0 0
      tests/qapi-schema/union-invalid-if-discriminator.json
  98. 0 0
      tests/qapi-schema/union-invalid-if-discriminator.out
  99. 2 0
      tests/qapi-schema/union-no-base.err
  100. 1 1
      tests/qapi-schema/union-no-base.json

+ 1 - 1
backends/tpm/tpm_emulator.c

@@ -623,7 +623,7 @@ static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb)
     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
     TpmTypeOptions *options = g_new0(TpmTypeOptions, 1);
 
-    options->type = TPM_TYPE_OPTIONS_KIND_EMULATOR;
+    options->type = TPM_TYPE_EMULATOR;
     options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options);
 
     return options;

+ 1 - 1
backends/tpm/tpm_passthrough.c

@@ -321,7 +321,7 @@ static TpmTypeOptions *tpm_passthrough_get_tpm_options(TPMBackend *tb)
 {
     TpmTypeOptions *options = g_new0(TpmTypeOptions, 1);
 
-    options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
+    options->type = TPM_TYPE_PASSTHROUGH;
     options->u.passthrough.data = QAPI_CLONE(TPMPassthroughOptions,
                                              TPM_PASSTHROUGH(tb)->options);
 

+ 3 - 3
chardev/char-socket.c

@@ -1520,7 +1520,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
     addr = g_new0(SocketAddressLegacy, 1);
     if (path) {
         UnixSocketAddress *q_unix;
-        addr->type = SOCKET_ADDRESS_LEGACY_KIND_UNIX;
+        addr->type = SOCKET_ADDRESS_TYPE_UNIX;
         q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
         q_unix->path = g_strdup(path);
 #ifdef CONFIG_LINUX
@@ -1530,7 +1530,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
         q_unix->abstract = abstract;
 #endif
     } else if (host) {
-        addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
+        addr->type = SOCKET_ADDRESS_TYPE_INET;
         addr->u.inet.data = g_new(InetSocketAddress, 1);
         *addr->u.inet.data = (InetSocketAddress) {
             .host = g_strdup(host),
@@ -1543,7 +1543,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
             .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
         };
     } else if (fd) {
-        addr->type = SOCKET_ADDRESS_LEGACY_KIND_FD;
+        addr->type = SOCKET_ADDRESS_TYPE_FD;
         addr->u.fd.data = g_new(String, 1);
         addr->u.fd.data->str = g_strdup(fd);
     } else {

+ 2 - 2
chardev/char-udp.c

@@ -165,7 +165,7 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
     qemu_chr_parse_common(opts, qapi_ChardevUdp_base(udp));
 
     addr = g_new0(SocketAddressLegacy, 1);
-    addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
+    addr->type = SOCKET_ADDRESS_TYPE_INET;
     addr->u.inet.data = g_new(InetSocketAddress, 1);
     *addr->u.inet.data = (InetSocketAddress) {
         .host = g_strdup(host),
@@ -180,7 +180,7 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
     if (has_local) {
         udp->has_local = true;
         addr = g_new0(SocketAddressLegacy, 1);
-        addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
+        addr->type = SOCKET_ADDRESS_TYPE_INET;
         addr->u.inet.data = g_new(InetSocketAddress, 1);
         *addr->u.inet.data = (InetSocketAddress) {
             .host = g_strdup(localaddr),

+ 29 - 106
docs/devel/qapi-code-gen.rst

@@ -319,13 +319,9 @@ Union types
 Syntax::
 
     UNION = { 'union': STRING,
-              'data': BRANCHES,
-              '*if': COND,
-              '*features': FEATURES }
-          | { 'union': STRING,
-              'data': BRANCHES,
               'base': ( MEMBERS | STRING ),
               'discriminator': STRING,
+              'data': BRANCHES,
               '*if': COND,
               '*features': FEATURES }
     BRANCHES = { BRANCH, ... }
@@ -334,63 +330,30 @@ Syntax::
 
 Member 'union' names the union type.
 
-There are two flavors of union types: simple (no discriminator or
-base), and flat (both discriminator and base).
+The 'base' member defines the common members.  If it is a MEMBERS_
+object, it defines common members just like a struct type's 'data'
+member defines struct type members.  If it is a STRING, it names a
+struct type whose members are the common members.
+
+Member 'discriminator' must name a non-optional enum-typed member of
+the base struct.  That member's value selects a branch by its name.
+If no such branch exists, an empty branch is assumed.
 
 Each BRANCH of the 'data' object defines a branch of the union.  A
 union must have at least one branch.
 
-The BRANCH's STRING name is the branch name.
+The BRANCH's STRING name is the branch name.  It must be a value of
+the discriminator enum type.
 
 The BRANCH's value defines the branch's properties, in particular its
-type.  The form TYPE-REF_ is shorthand for :code:`{ 'type': TYPE-REF }`.
-
-A simple union type defines a mapping from automatic discriminator
-values to data types like in this example::
-
- { 'struct': 'BlockdevOptionsFile', 'data': { 'filename': 'str' } }
- { 'struct': 'BlockdevOptionsQcow2',
-   'data': { 'backing': 'str', '*lazy-refcounts': 'bool' } }
-
- { 'union': 'BlockdevOptionsSimple',
-   'data': { 'file': 'BlockdevOptionsFile',
-             'qcow2': 'BlockdevOptionsQcow2' } }
-
-In the Client JSON Protocol, a simple union is represented by an
-object that contains the 'type' member as a discriminator, and a
-'data' member that is of the specified data type corresponding to the
-discriminator value, as in these examples::
-
- { "type": "file", "data": { "filename": "/some/place/my-image" } }
- { "type": "qcow2", "data": { "backing": "/some/place/my-image",
-                              "lazy-refcounts": true } }
-
-The generated C code uses a struct containing a union.  Additionally,
-an implicit C enum 'NameKind' is created, corresponding to the union
-'Name', for accessing the various branches of the union.  The value
-for each branch can be of any type.
-
-Flat unions permit arbitrary common members that occur in all variants
-of the union, not just a discriminator.  Their discriminators need not
-be named 'type'.  They also avoid nesting on the wire.
+type.  The type must a struct type.  The form TYPE-REF_ is shorthand
+for :code:`{ 'type': TYPE-REF }`.
 
-The 'base' member defines the common members.  If it is a MEMBERS_
-object, it defines common members just like a struct type's 'data'
-member defines struct type members.  If it is a STRING, it names a
-struct type whose members are the common members.
-
-All flat union branches must be `Struct types`_.
+In the Client JSON Protocol, a union is represented by an object with
+the common members (from the base type) and the selected branch's
+members.  The two sets of member names must be disjoint.
 
-In the Client JSON Protocol, a flat union is represented by an object
-with the common members (from the base type) and the selected branch's
-members.  The two sets of member names must be disjoint.  Member
-'discriminator' must name a non-optional enum-typed member of the base
-struct.
-
-The following example enhances the above simple union example by
-adding an optional common member 'read-only', renaming the
-discriminator to something more applicable than the simple union's
-default of 'type', and reducing the number of ``{}`` required on the wire::
+Example::
 
  { 'enum': 'BlockdevDriver', 'data': [ 'file', 'qcow2' ] }
  { 'union': 'BlockdevOptions',
@@ -406,30 +369,11 @@ Resulting in these JSON objects::
  { "driver": "qcow2", "read-only": false,
    "backing": "/some/place/my-image", "lazy-refcounts": true }
 
-Notice that in a flat union, the discriminator name is controlled by
-the user, but because it must map to a base member with enum type, the
-code generator ensures that branches match the existing values of the
-enum.  The order of branches need not match the order of the enum
-values.  The branches need not cover all possible enum values.
-Omitted enum values are still valid branches that add no additional
-members to the data type.  In the resulting generated C data types, a
-flat union is represented as a struct with the base members in QAPI
-schema order, and then a union of structures for each branch of the
-struct.
-
-A simple union can always be re-written as a flat union where the base
-class has a single member named 'type', and where each branch of the
-union has a struct with a single member named 'data'.  That is, ::
-
- { 'union': 'Simple', 'data': { 'one': 'str', 'two': 'int' } }
-
-is identical on the wire to::
-
- { 'enum': 'Enum', 'data': ['one', 'two'] }
- { 'struct': 'Branch1', 'data': { 'data': 'str' } }
- { 'struct': 'Branch2', 'data': { 'data': 'int' } }
- { 'union': 'Flat', 'base': { 'type': 'Enum' }, 'discriminator': 'type',
-   'data': { 'one': 'Branch1', 'two': 'Branch2' } }
+The order of branches need not match the order of the enum values.
+The branches need not cover all possible enum values.  In the
+resulting generated C data types, a union is represented as a struct
+with the base members in QAPI schema order, and then a union of
+structures for each branch of the struct.
 
 The optional 'if' member specifies a conditional.  See `Configuring
 the schema`_ below for more on this.
@@ -859,9 +803,9 @@ longhand form of MEMBER.
 Example: a struct type with unconditional member 'foo' and conditional
 member 'bar' ::
 
- { 'struct': 'IfStruct', 'data':
-   { 'foo': 'int',
-     'bar': { 'type': 'int', 'if': 'IFCOND'} } }
+ { 'struct': 'IfStruct',
+   'data': { 'foo': 'int',
+             'bar': { 'type': 'int', 'if': 'IFCOND'} } }
 
 A union's discriminator may not be conditional.
 
@@ -871,9 +815,9 @@ the longhand form of ENUM-VALUE_.
 Example: an enum type with unconditional value 'foo' and conditional
 value 'bar' ::
 
- { 'enum': 'IfEnum', 'data':
-   [ 'foo',
-     { 'name' : 'bar', 'if': 'IFCOND' } ] }
+ { 'enum': 'IfEnum',
+   'data': [ 'foo',
+             { 'name' : 'bar', 'if': 'IFCOND' } ] }
 
 Likewise, features can be conditional.  This requires the longhand
 form of FEATURE_.
@@ -1246,7 +1190,7 @@ that provides the variant members for this type tag value).  The
 "variants" array is in no particular order, and is not guaranteed to
 list cases in the same order as the corresponding "tag" enum type.
 
-Example: the SchemaInfo for flat union BlockdevOptions from section
+Example: the SchemaInfo for union BlockdevOptions from section
 `Union types`_ ::
 
     { "name": "BlockdevOptions", "meta-type": "object",
@@ -1261,27 +1205,6 @@ Example: the SchemaInfo for flat union BlockdevOptions from section
 Note that base types are "flattened": its members are included in the
 "members" array.
 
-A simple union implicitly defines an enumeration type for its implicit
-discriminator (called "type" on the wire, see section `Union types`_).
-
-A simple union implicitly defines an object type for each of its
-variants.
-
-Example: the SchemaInfo for simple union BlockdevOptionsSimple from section
-`Union types`_ ::
-
-    { "name": "BlockdevOptionsSimple", "meta-type": "object",
-      "members": [
-          { "name": "type", "type": "BlockdevOptionsSimpleKind" } ],
-      "tag": "type",
-      "variants": [
-          { "case": "file", "type": "q_obj-BlockdevOptionsFile-wrapper" },
-          { "case": "qcow2", "type": "q_obj-BlockdevOptionsQcow2-wrapper" } ] }
-
-    Enumeration type "BlockdevOptionsSimpleKind" and the object types
-    "q_obj-BlockdevOptionsFile-wrapper", "q_obj-BlockdevOptionsQcow2-wrapper"
-    are implicitly defined.
-
 The SchemaInfo for an alternate type has meta-type "alternate", and
 variant member "members".  "members" is a JSON array.  Each element is
 a JSON object with member "type", which names a type.  Values of the

+ 4 - 4
monitor/hmp-cmds.c

@@ -925,10 +925,10 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
                        c, TpmModel_str(ti->model));
 
         monitor_printf(mon, "  \\ %s: type=%s",
-                       ti->id, TpmTypeOptionsKind_str(ti->options->type));
+                       ti->id, TpmType_str(ti->options->type));
 
         switch (ti->options->type) {
-        case TPM_TYPE_OPTIONS_KIND_PASSTHROUGH:
+        case TPM_TYPE_PASSTHROUGH:
             tpo = ti->options->u.passthrough.data;
             monitor_printf(mon, "%s%s%s%s",
                            tpo->has_path ? ",path=" : "",
@@ -936,11 +936,11 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
                            tpo->has_cancel_path ? ",cancel-path=" : "",
                            tpo->has_cancel_path ? tpo->cancel_path : "");
             break;
-        case TPM_TYPE_OPTIONS_KIND_EMULATOR:
+        case TPM_TYPE_EMULATOR:
             teo = ti->options->u.emulator.data;
             monitor_printf(mon, ",chardev=%s", teo->chardev);
             break;
-        case TPM_TYPE_OPTIONS_KIND__MAX:
+        case TPM_TYPE__MAX:
             break;
         }
         monitor_printf(mon, "\n");

+ 52 - 7
qapi/block-core.json

@@ -139,6 +139,52 @@
       '*encryption-format': 'RbdImageEncryptionFormat'
   } }
 
+##
+# @ImageInfoSpecificKind:
+#
+# @luks: Since 2.7
+# @rbd: Since 6.1
+#
+# Since: 1.7
+##
+{ 'enum': 'ImageInfoSpecificKind',
+  'data': [ 'qcow2', 'vmdk', 'luks', 'rbd' ] }
+
+##
+# @ImageInfoSpecificQCow2Wrapper:
+#
+# Since: 1.7
+##
+{ 'struct': 'ImageInfoSpecificQCow2Wrapper',
+  'data': { 'data': 'ImageInfoSpecificQCow2' } }
+
+##
+# @ImageInfoSpecificVmdkWrapper:
+#
+# Since: 6.1
+##
+{ 'struct': 'ImageInfoSpecificVmdkWrapper',
+  'data': { 'data': 'ImageInfoSpecificVmdk' } }
+
+##
+# @ImageInfoSpecificLUKSWrapper:
+#
+# Since: 2.7
+##
+{ 'struct': 'ImageInfoSpecificLUKSWrapper',
+  'data': { 'data': 'QCryptoBlockInfoLUKS' } }
+# If we need to add block driver specific parameters for
+# LUKS in future, then we'll subclass QCryptoBlockInfoLUKS
+# to define a ImageInfoSpecificLUKS
+
+##
+# @ImageInfoSpecificRbdWrapper:
+#
+# Since: 6.1
+##
+{ 'struct': 'ImageInfoSpecificRbdWrapper',
+  'data': { 'data': 'ImageInfoSpecificRbd' } }
+
 ##
 # @ImageInfoSpecific:
 #
@@ -147,14 +193,13 @@
 # Since: 1.7
 ##
 { 'union': 'ImageInfoSpecific',
+  'base': { 'type': 'ImageInfoSpecificKind' },
+  'discriminator': 'type',
   'data': {
-      'qcow2': 'ImageInfoSpecificQCow2',
-      'vmdk': 'ImageInfoSpecificVmdk',
-      # If we need to add block driver specific parameters for
-      # LUKS in future, then we'll subclass QCryptoBlockInfoLUKS
-      # to define a ImageInfoSpecificLUKS
-      'luks': 'QCryptoBlockInfoLUKS',
-      'rbd': 'ImageInfoSpecificRbd'
+      'qcow2': 'ImageInfoSpecificQCow2Wrapper',
+      'vmdk': 'ImageInfoSpecificVmdkWrapper',
+      'luks': 'ImageInfoSpecificLUKSWrapper',
+      'rbd': 'ImageInfoSpecificRbdWrapper'
   } }
 
 ##

+ 168 - 22
qapi/char.json

@@ -407,39 +407,185 @@
   'base': 'ChardevCommon',
   'if': 'CONFIG_SPICE_PROTOCOL' }
 
+##
+# @ChardevBackendKind:
+#
+# @pipe: Since 1.5
+# @udp: Since 1.5
+# @mux: Since 1.5
+# @msmouse: Since 1.5
+# @wctablet: Since 2.9
+# @braille: Since 1.5
+# @testdev: Since 2.2
+# @stdio: Since 1.5
+# @console: Since 1.5
+# @spicevmc: Since 1.5
+# @spiceport: Since 1.5
+# @qemu-vdagent: Since 6.1
+# @vc: v1.5
+# @ringbuf: Since 1.6
+# @memory: Since 1.5
+#
+# Since: 1.4
+##
+{ 'enum': 'ChardevBackendKind',
+  'data': [ 'file',
+            'serial',
+            'parallel',
+            'pipe',
+            'socket',
+            'udp',
+            'pty',
+            'null',
+            'mux',
+            'msmouse',
+            'wctablet',
+            'braille',
+            'testdev',
+            'stdio',
+            'console',
+            { 'name': 'spicevmc', 'if': 'CONFIG_SPICE' },
+            { 'name': 'spiceport', 'if': 'CONFIG_SPICE' },
+            { 'name': 'qemu-vdagent', 'if': 'CONFIG_SPICE_PROTOCOL' },
+            'vc',
+            'ringbuf',
+            # next one is just for compatibility
+            'memory' ] }
+
+##
+# @ChardevFileWrapper:
+#
+# Since: 1.4
+##
+{ 'struct': 'ChardevFileWrapper',
+  'data': { 'data': 'ChardevFile' } }
+
+##
+# @ChardevHostdevWrapper:
+#
+# Since: 1.4
+##
+{ 'struct': 'ChardevHostdevWrapper',
+  'data': { 'data': 'ChardevHostdev' } }
+
+##
+# @ChardevSocketWrapper:
+#
+# Since: 1.4
+##
+{ 'struct': 'ChardevSocketWrapper',
+  'data': { 'data': 'ChardevSocket' } }
+
+##
+# @ChardevUdpWrapper:
+#
+# Since: 1.5
+##
+{ 'struct': 'ChardevUdpWrapper',
+  'data': { 'data': 'ChardevUdp' } }
+
+##
+# @ChardevCommonWrapper:
+#
+# Since: 2.6
+##
+{ 'struct': 'ChardevCommonWrapper',
+  'data': { 'data': 'ChardevCommon' } }
+
+##
+# @ChardevMuxWrapper:
+#
+# Since: 1.5
+##
+{ 'struct': 'ChardevMuxWrapper',
+  'data': { 'data': 'ChardevMux' } }
+
+##
+# @ChardevStdioWrapper:
+#
+# Since: 1.5
+##
+{ 'struct': 'ChardevStdioWrapper',
+  'data': { 'data': 'ChardevStdio' } }
+
+##
+# @ChardevSpiceChannelWrapper:
+#
+# Since: 1.5
+##
+{ 'struct': 'ChardevSpiceChannelWrapper',
+  'data': { 'data': 'ChardevSpiceChannel' },
+  'if': 'CONFIG_SPICE' }
+
+##
+# @ChardevSpicePortWrapper:
+#
+# Since: 1.5
+##
+{ 'struct': 'ChardevSpicePortWrapper',
+  'data': { 'data': 'ChardevSpicePort' },
+  'if': 'CONFIG_SPICE' }
+
+##
+# @ChardevQemuVDAgentWrapper:
+#
+# Since: 6.1
+##
+{ 'struct': 'ChardevQemuVDAgentWrapper',
+  'data': { 'data': 'ChardevQemuVDAgent' },
+  'if': 'CONFIG_SPICE_PROTOCOL' }
+
+##
+# @ChardevVCWrapper:
+#
+# Since: 1.5
+##
+{ 'struct': 'ChardevVCWrapper',
+  'data': { 'data': 'ChardevVC' } }
+
+##
+# @ChardevRingbufWrapper:
+#
+# Since: 1.5
+##
+{ 'struct': 'ChardevRingbufWrapper',
+  'data': { 'data': 'ChardevRingbuf' } }
+
 ##
 # @ChardevBackend:
 #
 # Configuration info for the new chardev backend.
 #
-# Since: 1.4 (testdev since 2.2, wctablet since 2.9, vdagent since 6.1)
+# Since: 1.4
 ##
 { 'union': 'ChardevBackend',
-  'data': { 'file': 'ChardevFile',
-            'serial': 'ChardevHostdev',
-            'parallel': 'ChardevHostdev',
-            'pipe': 'ChardevHostdev',
-            'socket': 'ChardevSocket',
-            'udp': 'ChardevUdp',
-            'pty': 'ChardevCommon',
-            'null': 'ChardevCommon',
-            'mux': 'ChardevMux',
-            'msmouse': 'ChardevCommon',
-            'wctablet': 'ChardevCommon',
-            'braille': 'ChardevCommon',
-            'testdev': 'ChardevCommon',
-            'stdio': 'ChardevStdio',
-            'console': 'ChardevCommon',
-            'spicevmc': { 'type': 'ChardevSpiceChannel',
+  'base': { 'type': 'ChardevBackendKind' },
+  'discriminator': 'type',
+  'data': { 'file': 'ChardevFileWrapper',
+            'serial': 'ChardevHostdevWrapper',
+            'parallel': 'ChardevHostdevWrapper',
+            'pipe': 'ChardevHostdevWrapper',
+            'socket': 'ChardevSocketWrapper',
+            'udp': 'ChardevUdpWrapper',
+            'pty': 'ChardevCommonWrapper',
+            'null': 'ChardevCommonWrapper',
+            'mux': 'ChardevMuxWrapper',
+            'msmouse': 'ChardevCommonWrapper',
+            'wctablet': 'ChardevCommonWrapper',
+            'braille': 'ChardevCommonWrapper',
+            'testdev': 'ChardevCommonWrapper',
+            'stdio': 'ChardevStdioWrapper',
+            'console': 'ChardevCommonWrapper',
+            'spicevmc': { 'type': 'ChardevSpiceChannelWrapper',
                           'if': 'CONFIG_SPICE' },
-            'spiceport': { 'type': 'ChardevSpicePort',
+            'spiceport': { 'type': 'ChardevSpicePortWrapper',
                            'if': 'CONFIG_SPICE' },
-            'qemu-vdagent': { 'type': 'ChardevQemuVDAgent',
+            'qemu-vdagent': { 'type': 'ChardevQemuVDAgentWrapper',
                               'if': 'CONFIG_SPICE_PROTOCOL' },
-            'vc': 'ChardevVC',
-            'ringbuf': 'ChardevRingbuf',
+            'vc': 'ChardevVCWrapper',
+            'ringbuf': 'ChardevRingbufWrapper',
             # next one is just for compatibility
-            'memory': 'ChardevRingbuf' } }
+            'memory': 'ChardevRingbufWrapper' } }
 
 ##
 # @ChardevReturn:

+ 38 - 4
qapi/machine.json

@@ -1194,6 +1194,38 @@
           }
 }
 
+##
+# @MemoryDeviceInfoKind:
+#
+# Since: 2.1
+##
+{ 'enum': 'MemoryDeviceInfoKind',
+  'data': [ 'dimm', 'nvdimm', 'virtio-pmem', 'virtio-mem' ] }
+
+##
+# @PCDIMMDeviceInfoWrapper:
+#
+# Since: 2.1
+##
+{ 'struct': 'PCDIMMDeviceInfoWrapper',
+  'data': { 'data': 'PCDIMMDeviceInfo' } }
+
+##
+# @VirtioPMEMDeviceInfoWrapper:
+#
+# Since: 2.1
+##
+{ 'struct': 'VirtioPMEMDeviceInfoWrapper',
+  'data': { 'data': 'VirtioPMEMDeviceInfo' } }
+
+##
+# @VirtioMEMDeviceInfoWrapper:
+#
+# Since: 2.1
+##
+{ 'struct': 'VirtioMEMDeviceInfoWrapper',
+  'data': { 'data': 'VirtioMEMDeviceInfo' } }
+
 ##
 # @MemoryDeviceInfo:
 #
@@ -1205,10 +1237,12 @@
 # Since: 2.1
 ##
 { 'union': 'MemoryDeviceInfo',
-  'data': { 'dimm': 'PCDIMMDeviceInfo',
-            'nvdimm': 'PCDIMMDeviceInfo',
-            'virtio-pmem': 'VirtioPMEMDeviceInfo',
-            'virtio-mem': 'VirtioMEMDeviceInfo'
+  'base': { 'type': 'MemoryDeviceInfoKind' },
+  'discriminator': 'type',
+  'data': { 'dimm': 'PCDIMMDeviceInfoWrapper',
+            'nvdimm': 'PCDIMMDeviceInfoWrapper',
+            'virtio-pmem': 'VirtioPMEMDeviceInfoWrapper',
+            'virtio-mem': 'VirtioMEMDeviceInfoWrapper'
           }
 }
 

+ 39 - 7
qapi/sockets.json

@@ -110,6 +110,38 @@
     'cid': 'str',
     'port': 'str' } }
 
+##
+# @InetSocketAddressWrapper:
+#
+# Since: 1.3
+##
+{ 'struct': 'InetSocketAddressWrapper',
+  'data': { 'data': 'InetSocketAddress' } }
+
+##
+# @UnixSocketAddressWrapper:
+#
+# Since: 1.3
+##
+{ 'struct': 'UnixSocketAddressWrapper',
+  'data': { 'data': 'UnixSocketAddress' } }
+
+##
+# @VsockSocketAddressWrapper:
+#
+# Since: 2.8
+##
+{ 'struct': 'VsockSocketAddressWrapper',
+  'data': { 'data': 'VsockSocketAddress' } }
+
+##
+# @StringWrapper:
+#
+# Since: 1.3
+##
+{ 'struct': 'StringWrapper',
+  'data': { 'data': 'String' } }
+
 ##
 # @SocketAddressLegacy:
 #
@@ -117,18 +149,18 @@
 #
 # Note: This type is deprecated in favor of SocketAddress.  The
 #       difference between SocketAddressLegacy and SocketAddress is that the
-#       latter is a flat union rather than a simple union. Flat is nicer
-#       because it avoids nesting on the wire, i.e. that form has fewer {}.
-
+#       latter is has fewer {} on the wire.
 #
 # Since: 1.3
 ##
 { 'union': 'SocketAddressLegacy',
+  'base': { 'type': 'SocketAddressType' },
+  'discriminator': 'type',
   'data': {
-    'inet': 'InetSocketAddress',
-    'unix': 'UnixSocketAddress',
-    'vsock': 'VsockSocketAddress',
-    'fd': 'String' } }
+    'inet': 'InetSocketAddressWrapper',
+    'unix': 'UnixSocketAddressWrapper',
+    'vsock': 'VsockSocketAddressWrapper',
+    'fd': 'StringWrapper' } }
 
 ##
 # @SocketAddressType:

+ 22 - 2
qapi/tpm.json

@@ -99,6 +99,24 @@
 { 'struct': 'TPMEmulatorOptions', 'data': { 'chardev' : 'str' },
   'if': 'CONFIG_TPM' }
 
+##
+# @TPMPassthroughOptionsWrapper:
+#
+# Since: 1.5
+##
+{ 'struct': 'TPMPassthroughOptionsWrapper',
+  'data': { 'data': 'TPMPassthroughOptions' },
+  'if': 'CONFIG_TPM' }
+
+##
+# @TPMEmulatorOptionsWrapper:
+#
+# Since: 2.11
+##
+{ 'struct': 'TPMEmulatorOptionsWrapper',
+  'data': { 'data': 'TPMEmulatorOptions' },
+  'if': 'CONFIG_TPM' }
+
 ##
 # @TpmTypeOptions:
 #
@@ -110,8 +128,10 @@
 # Since: 1.5
 ##
 { 'union': 'TpmTypeOptions',
-   'data': { 'passthrough' : 'TPMPassthroughOptions',
-             'emulator': 'TPMEmulatorOptions' },
+  'base': { 'type': 'TpmType' },
+  'discriminator': 'type',
+  'data': { 'passthrough' : 'TPMPassthroughOptionsWrapper',
+            'emulator': 'TPMEmulatorOptionsWrapper' },
   'if': 'CONFIG_TPM' }
 
 ##

+ 113 - 26
qapi/transaction.json

@@ -38,41 +38,128 @@
 { 'enum': 'ActionCompletionMode',
   'data': [ 'individual', 'grouped' ] }
 
+##
+# @TransactionActionKind:
+#
+# @abort: Since 1.6
+# @block-dirty-bitmap-add: Since 2.5
+# @block-dirty-bitmap-remove: Since 4.2
+# @block-dirty-bitmap-clear: Since 2.5
+# @block-dirty-bitmap-enable: Since 4.0
+# @block-dirty-bitmap-disable: Since 4.0
+# @block-dirty-bitmap-merge: Since 4.0
+# @blockdev-backup: Since 2.3
+# @blockdev-snapshot: Since 2.5
+# @blockdev-snapshot-internal-sync: Since 1.7
+# @blockdev-snapshot-sync: since 1.1
+# @drive-backup: Since 1.6
+#
+# Since: 1.1
+##
+{ 'enum': 'TransactionActionKind',
+  'data': [ 'abort', 'block-dirty-bitmap-add', 'block-dirty-bitmap-remove',
+            'block-dirty-bitmap-clear', 'block-dirty-bitmap-enable',
+            'block-dirty-bitmap-disable', 'block-dirty-bitmap-merge',
+            'blockdev-backup', 'blockdev-snapshot',
+            'blockdev-snapshot-internal-sync', 'blockdev-snapshot-sync',
+            'drive-backup' ] }
+
+##
+# @AbortWrapper:
+#
+# Since: 1.6
+##
+{ 'struct': 'AbortWrapper',
+  'data': { 'data': 'Abort' } }
+
+##
+# @BlockDirtyBitmapAddWrapper:
+#
+# Since: 2.5
+##
+{ 'struct': 'BlockDirtyBitmapAddWrapper',
+  'data': { 'data': 'BlockDirtyBitmapAdd' } }
+
+##
+# @BlockDirtyBitmapWrapper:
+#
+# Since: 2.5
+##
+{ 'struct': 'BlockDirtyBitmapWrapper',
+  'data': { 'data': 'BlockDirtyBitmap' } }
+
+##
+# @BlockDirtyBitmapMergeWrapper:
+#
+# Since: 4.0
+##
+{ 'struct': 'BlockDirtyBitmapMergeWrapper',
+  'data': { 'data': 'BlockDirtyBitmapMerge' } }
+
+##
+# @BlockdevBackupWrapper:
+#
+# Since: 2.3
+##
+{ 'struct': 'BlockdevBackupWrapper',
+  'data': { 'data': 'BlockdevBackup' } }
+
+##
+# @BlockdevSnapshotWrapper:
+#
+# Since: 2.5
+##
+{ 'struct': 'BlockdevSnapshotWrapper',
+  'data': { 'data': 'BlockdevSnapshot' } }
+
+##
+# @BlockdevSnapshotInternalWrapper:
+#
+# Since: 1.7
+##
+{ 'struct': 'BlockdevSnapshotInternalWrapper',
+  'data': { 'data': 'BlockdevSnapshotInternal' } }
+
+##
+# @BlockdevSnapshotSyncWrapper:
+#
+# Since: 1.1
+##
+{ 'struct': 'BlockdevSnapshotSyncWrapper',
+  'data': { 'data': 'BlockdevSnapshotSync' } }
+
+##
+# @DriveBackupWrapper:
+#
+# Since: 1.6
+##
+{ 'struct': 'DriveBackupWrapper',
+  'data': { 'data': 'DriveBackup' } }
+
 ##
 # @TransactionAction:
 #
 # A discriminated record of operations that can be performed with
-# @transaction. Action @type can be:
-#
-# - @abort: since 1.6
-# - @block-dirty-bitmap-add: since 2.5
-# - @block-dirty-bitmap-remove: since 4.2
-# - @block-dirty-bitmap-clear: since 2.5
-# - @block-dirty-bitmap-enable: since 4.0
-# - @block-dirty-bitmap-disable: since 4.0
-# - @block-dirty-bitmap-merge: since 4.0
-# - @blockdev-backup: since 2.3
-# - @blockdev-snapshot: since 2.5
-# - @blockdev-snapshot-internal-sync: since 1.7
-# - @blockdev-snapshot-sync: since 1.1
-# - @drive-backup: since 1.6
+# @transaction.
 #
 # Since: 1.1
 ##
 { 'union': 'TransactionAction',
+  'base': { 'type': 'TransactionActionKind' },
+  'discriminator': 'type',
   'data': {
-       'abort': 'Abort',
-       'block-dirty-bitmap-add': 'BlockDirtyBitmapAdd',
-       'block-dirty-bitmap-remove': 'BlockDirtyBitmap',
-       'block-dirty-bitmap-clear': 'BlockDirtyBitmap',
-       'block-dirty-bitmap-enable': 'BlockDirtyBitmap',
-       'block-dirty-bitmap-disable': 'BlockDirtyBitmap',
-       'block-dirty-bitmap-merge': 'BlockDirtyBitmapMerge',
-       'blockdev-backup': 'BlockdevBackup',
-       'blockdev-snapshot': 'BlockdevSnapshot',
-       'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal',
-       'blockdev-snapshot-sync': 'BlockdevSnapshotSync',
-       'drive-backup': 'DriveBackup'
+       'abort': 'AbortWrapper',
+       'block-dirty-bitmap-add': 'BlockDirtyBitmapAddWrapper',
+       'block-dirty-bitmap-remove': 'BlockDirtyBitmapWrapper',
+       'block-dirty-bitmap-clear': 'BlockDirtyBitmapWrapper',
+       'block-dirty-bitmap-enable': 'BlockDirtyBitmapWrapper',
+       'block-dirty-bitmap-disable': 'BlockDirtyBitmapWrapper',
+       'block-dirty-bitmap-merge': 'BlockDirtyBitmapMergeWrapper',
+       'blockdev-backup': 'BlockdevBackupWrapper',
+       'blockdev-snapshot': 'BlockdevSnapshotWrapper',
+       'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternalWrapper',
+       'blockdev-snapshot-sync': 'BlockdevSnapshotSyncWrapper',
+       'drive-backup': 'DriveBackupWrapper'
    } }
 
 ##

+ 66 - 6
qapi/ui.json

@@ -824,6 +824,30 @@
             'ac_home', 'ac_back', 'ac_forward', 'ac_refresh', 'ac_bookmarks',
             'lang1', 'lang2' ] }
 
+##
+# @KeyValueKind:
+#
+# Since: 1.3
+##
+{ 'enum': 'KeyValueKind',
+  'data': [ 'number', 'qcode' ] }
+
+##
+# @IntWrapper:
+#
+# Since: 1.3
+##
+{ 'struct': 'IntWrapper',
+  'data': { 'data': 'int' } }
+
+##
+# @QKeyCodeWrapper:
+#
+# Since: 1.3
+##
+{ 'struct': 'QKeyCodeWrapper',
+  'data': { 'data': 'QKeyCode' } }
+
 ##
 # @KeyValue:
 #
@@ -832,9 +856,11 @@
 # Since: 1.3
 ##
 { 'union': 'KeyValue',
+  'base': { 'type': 'KeyValueKind' },
+  'discriminator': 'type',
   'data': {
-    'number': 'int',
-    'qcode': 'QKeyCode' } }
+    'number': 'IntWrapper',
+    'qcode': 'QKeyCodeWrapper' } }
 
 ##
 # @send-key:
@@ -934,6 +960,38 @@
   'data'  : { 'axis'    : 'InputAxis',
               'value'   : 'int' } }
 
+##
+# @InputEventKind:
+#
+# Since: 2.0
+##
+{ 'enum': 'InputEventKind',
+  'data': [ 'key', 'btn', 'rel', 'abs' ] }
+
+##
+# @InputKeyEventWrapper:
+#
+# Since: 2.0
+##
+{ 'struct': 'InputKeyEventWrapper',
+  'data': { 'data': 'InputKeyEvent' } }
+
+##
+# @InputBtnEventWrapper:
+#
+# Since: 2.0
+##
+{ 'struct': 'InputBtnEventWrapper',
+  'data': { 'data': 'InputBtnEvent' } }
+
+##
+# @InputMoveEventWrapper:
+#
+# Since: 2.0
+##
+{ 'struct': 'InputMoveEventWrapper',
+  'data': { 'data': 'InputMoveEvent' } }
+
 ##
 # @InputEvent:
 #
@@ -949,10 +1007,12 @@
 # Since: 2.0
 ##
 { 'union' : 'InputEvent',
-  'data'  : { 'key'     : 'InputKeyEvent',
-              'btn'     : 'InputBtnEvent',
-              'rel'     : 'InputMoveEvent',
-              'abs'     : 'InputMoveEvent' } }
+  'base': { 'type': 'InputEventKind' },
+  'discriminator': 'type',
+  'data'  : { 'key'     : 'InputKeyEventWrapper',
+              'btn'     : 'InputBtnEventWrapper',
+              'rel'     : 'InputMoveEventWrapper',
+              'abs'     : 'InputMoveEventWrapper' } }
 
 ##
 # @input-send-event:

+ 9 - 18
scripts/qapi/expr.py

@@ -171,7 +171,7 @@ def check_defn_name_str(name: str, info: QAPISourceInfo, meta: str) -> None:
       - 'event' names adhere to `check_name_upper()`.
       - 'command' names adhere to `check_name_lower()`.
       - Else, meta is a type, and must pass `check_name_camel()`.
-        These names must not end with ``Kind`` nor ``List``.
+        These names must not end with ``List``.
 
     :param name: Name to check.
     :param info: QAPI schema source file information.
@@ -187,9 +187,9 @@ def check_defn_name_str(name: str, info: QAPISourceInfo, meta: str) -> None:
             permit_underscore=name in info.pragma.command_name_exceptions)
     else:
         check_name_camel(name, info, meta)
-        if name.endswith('Kind') or name.endswith('List'):
+        if name.endswith('List'):
             raise QAPISemError(
-                info, "%s name should not end in '%s'" % (meta, name[-4:]))
+                info, "%s name should not end in 'List'" % meta)
 
 
 def check_keys(value: _JSONObject,
@@ -513,27 +513,18 @@ def check_union(expr: _JSONObject, info: QAPISourceInfo) -> None:
     :return: None, ``expr`` is normalized in-place as needed.
     """
     name = cast(str, expr['union'])  # Checked in check_exprs
-    base = expr.get('base')
-    discriminator = expr.get('discriminator')
+    base = expr['base']
+    discriminator = expr['discriminator']
     members = expr['data']
 
-    if discriminator is None:   # simple union
-        if base is not None:
-            raise QAPISemError(info, "'base' requires 'discriminator'")
-    else:                       # flat union
-        check_type(base, info, "'base'", allow_dict=name)
-        if not base:
-            raise QAPISemError(info, "'discriminator' requires 'base'")
-        check_name_is_str(discriminator, info, "'discriminator'")
+    check_type(base, info, "'base'", allow_dict=name)
+    check_name_is_str(discriminator, info, "'discriminator'")
 
     if not isinstance(members, dict):
         raise QAPISemError(info, "'data' must be an object")
 
     for (key, value) in members.items():
         source = "'data' member '%s'" % key
-        if discriminator is None:
-            check_name_lower(key, info, source)
-        # else: name is in discriminator enum, which gets checked
         check_keys(value, info, source, ['type'], ['if'])
         check_if(value, info, source)
         check_type(value['type'], info, source, allow_array=not base)
@@ -664,8 +655,8 @@ def check_exprs(exprs: List[_JSONObject]) -> List[_JSONObject]:
             check_enum(expr, info)
         elif meta == 'union':
             check_keys(expr, info, meta,
-                       ['union', 'data'],
-                       ['base', 'discriminator', 'if', 'features'])
+                       ['union', 'base', 'discriminator', 'data'],
+                       ['if', 'features'])
             normalize_members(expr.get('base'))
             normalize_members(expr['data'])
             check_union(expr, info)

+ 22 - 79
scripts/qapi/schema.py

@@ -321,8 +321,8 @@ def connect_doc(self, doc=None):
             m.connect_doc(doc)
 
     def is_implicit(self):
-        # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
-        return self.name.endswith('Kind') or self.name == 'QType'
+        # See QAPISchema._def_predefineds()
+        return self.name == 'QType'
 
     def c_type(self):
         return c_name(self.name)
@@ -393,8 +393,7 @@ class QAPISchemaObjectType(QAPISchemaType):
     def __init__(self, name, info, doc, ifcond, features,
                  base, local_members, variants):
         # struct has local_members, optional base, and no variants
-        # flat union has base, variants, and no local_members
-        # simple union has local_members, variants, and no base
+        # union has base, variants, and no local_members
         super().__init__(name, info, doc, ifcond, features)
         self.meta = 'union' if variants else 'struct'
         assert base is None or isinstance(base, str)
@@ -465,15 +464,6 @@ def connect_doc(self, doc=None):
         for m in self.local_members:
             m.connect_doc(doc)
 
-    @property
-    def ifcond(self):
-        assert self._checked
-        if isinstance(self._ifcond, QAPISchemaType):
-            # Simple union wrapper type inherits from wrapped type;
-            # see _make_implicit_object_type()
-            return self._ifcond.ifcond
-        return self._ifcond
-
     def is_implicit(self):
         # See QAPISchema._make_implicit_object_type(), as well as
         # _def_predefineds()
@@ -576,10 +566,9 @@ def visit(self, visitor):
 
 class QAPISchemaVariants:
     def __init__(self, tag_name, info, tag_member, variants):
-        # Flat unions pass tag_name but not tag_member.
-        # Simple unions and alternates pass tag_member but not tag_name.
-        # After check(), tag_member is always set, and tag_name remains
-        # a reliable witness of being used by a flat union.
+        # Unions pass tag_name but not tag_member.
+        # Alternates pass tag_member but not tag_name.
+        # After check(), tag_member is always set.
         assert bool(tag_member) != bool(tag_name)
         assert (isinstance(tag_name, str) or
                 isinstance(tag_member, QAPISchemaObjectTypeMember))
@@ -595,7 +584,7 @@ def set_defined_in(self, name):
             v.set_defined_in(name)
 
     def check(self, schema, seen):
-        if not self.tag_member:  # flat union
+        if self._tag_name:      # union
             self.tag_member = seen.get(c_name(self._tag_name))
             base = "'base'"
             # Pointing to the base type when not implicit would be
@@ -625,11 +614,11 @@ def check(self, schema, seen):
                     self.info,
                     "discriminator member '%s' of %s must not be conditional"
                     % (self._tag_name, base))
-        else:                   # simple union
+        else:                   # alternate
             assert isinstance(self.tag_member.type, QAPISchemaEnumType)
             assert not self.tag_member.optional
             assert not self.tag_member.ifcond.is_present()
-        if self._tag_name:    # flat union
+        if self._tag_name:      # union
             # branches that are not explicitly covered get an empty type
             cases = {v.name for v in self.variants}
             for m in self.tag_member.type.members:
@@ -707,18 +696,10 @@ def describe(self, info):
                 assert role == 'member'
                 role = 'parameter'
             elif defined_in.endswith('-base'):
-                # Implicit type created for a flat union's dict 'base'
+                # Implicit type created for a union's dict 'base'
                 role = 'base ' + role
             else:
-                # Implicit type created for a simple union's branch
-                assert defined_in.endswith('-wrapper')
-                # Unreachable and not implemented
                 assert False
-        elif defined_in.endswith('Kind'):
-            # See QAPISchema._make_implicit_enum_type()
-            # Implicit enum created for simple union's branches
-            assert role == 'value'
-            role = 'branch'
         elif defined_in != info.defn_name:
             return "%s '%s' of type '%s'" % (role, self.name, defined_in)
         return "%s '%s'" % (role, self.name)
@@ -1004,15 +985,6 @@ def _make_enum_members(self, values, info):
                                      QAPISchemaIfCond(v.get('if')))
                 for v in values]
 
-    def _make_implicit_enum_type(self, name, info, ifcond, values):
-        # See also QAPISchemaObjectTypeMember.describe()
-        name = name + 'Kind'    # reserved by check_defn_name_str()
-        self._def_entity(QAPISchemaEnumType(
-            name, info, None, ifcond, None,
-            self._make_enum_members(values, info),
-            None))
-        return name
-
     def _make_array_type(self, element_type, info):
         name = element_type + 'List'    # reserved by check_defn_name_str()
         if not self.lookup_type(name):
@@ -1026,17 +998,9 @@ def _make_implicit_object_type(self, name, info, ifcond, role, members):
         name = 'q_obj_%s-%s' % (name, role)
         typ = self.lookup_entity(name, QAPISchemaObjectType)
         if typ:
-            # The implicit object type has multiple users.  This is
-            # either a duplicate definition (which will be flagged
-            # later), or an implicit wrapper type used for multiple
-            # simple unions.  In the latter case, ifcond should be the
-            # disjunction of its user's ifconds.  Not implemented.
-            # Instead, we always pass the wrapped type's ifcond, which
-            # is trivially the same for all users.  It's also
-            # necessary for the wrapper to compile.  But it's not
-            # tight: the disjunction need not imply it.  We may end up
-            # compiling useless wrapper types.
-            # TODO kill simple unions or implement the disjunction
+            # The implicit object type has multiple users.  This can
+            # only be a duplicate definition, which will be flagged
+            # later.
             pass
         else:
             self._def_entity(QAPISchemaObjectType(
@@ -1084,49 +1048,28 @@ def _def_struct_type(self, expr, info, doc):
     def _make_variant(self, case, typ, ifcond, info):
         return QAPISchemaVariant(case, info, typ, ifcond)
 
-    def _make_simple_variant(self, case, typ, ifcond, info):
-        if isinstance(typ, list):
-            assert len(typ) == 1
-            typ = self._make_array_type(typ[0], info)
-        typ = self._make_implicit_object_type(
-            typ, info, self.lookup_type(typ),
-            'wrapper', [self._make_member('data', typ, None, None, info)])
-        return QAPISchemaVariant(case, info, typ, ifcond)
-
     def _def_union_type(self, expr, info, doc):
         name = expr['union']
+        base = expr['base']
+        tag_name = expr['discriminator']
         data = expr['data']
-        base = expr.get('base')
         ifcond = QAPISchemaIfCond(expr.get('if'))
         features = self._make_features(expr.get('features'), info)
-        tag_name = expr.get('discriminator')
-        tag_member = None
         if isinstance(base, dict):
             base = self._make_implicit_object_type(
                 name, info, ifcond,
                 'base', self._make_members(base, info))
-        if tag_name:
-            variants = [
-                self._make_variant(key, value['type'],
-                                   QAPISchemaIfCond(value.get('if')),
-                                   info)
-                for (key, value) in data.items()]
-            members = []
-        else:
-            variants = [
-                self._make_simple_variant(key, value['type'],
-                                          QAPISchemaIfCond(value.get('if')),
-                                          info)
-                for (key, value) in data.items()]
-            enum = [{'name': v.name, 'if': v.ifcond.ifcond} for v in variants]
-            typ = self._make_implicit_enum_type(name, info, ifcond, enum)
-            tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
-            members = [tag_member]
+        variants = [
+            self._make_variant(key, value['type'],
+                               QAPISchemaIfCond(value.get('if')),
+                               info)
+            for (key, value) in data.items()]
+        members = []
         self._def_entity(
             QAPISchemaObjectType(name, info, doc, ifcond, features,
                                  base, members,
                                  QAPISchemaVariants(
-                                     tag_name, info, tag_member, variants)))
+                                     tag_name, info, None, variants)))
 
     def _def_alternate_type(self, expr, info, doc):
         name = expr['alternate']

+ 1 - 1
tests/qapi-schema/args-union.err

@@ -1,2 +1,2 @@
 args-union.json: In command 'oops':
-args-union.json:3: command's 'data' can take union type 'Uni' only with 'boxed': true
+args-union.json:9: command's 'data' can take union type 'Uni' only with 'boxed': true

+ 7 - 1
tests/qapi-schema/args-union.json

@@ -1,3 +1,9 @@
 # use of union arguments requires 'boxed':true
-{ 'union': 'Uni', 'data': { 'case1': 'int', 'case2': 'str' } }
+{ 'enum': 'Enum', 'data': [ 'case1', 'case2' ] }
+{ 'struct': 'Case1', 'data': { 'data': 'int' } }
+{ 'struct': 'Case2', 'data': { 'data': 'str' } }
+{ 'union': 'Uni',
+  'base': { 'type': 'Enum' },
+  'discriminator': 'type',
+  'data': { 'case1': 'Case1', 'case2': 'Case2' } }
 { 'command': 'oops', 'data': 'Uni' }

+ 1 - 1
tests/qapi-schema/bad-base.err

@@ -1,2 +1,2 @@
 bad-base.json: In struct 'MyType':
-bad-base.json:3: 'base' requires a struct type, union type 'Union' isn't
+bad-base.json:9: 'base' requires a struct type, union type 'Union' isn't

+ 7 - 1
tests/qapi-schema/bad-base.json

@@ -1,3 +1,9 @@
 # we reject a base that is not a struct
-{ 'union': 'Union', 'data': { 'a': 'int', 'b': 'str' } }
+{ 'enum': 'Enum', 'data': [ 'a', 'b' ] }
+{ 'struct': 'Int', 'data': { 'data': 'int' } }
+{ 'struct': 'Str', 'data': { 'data': 'str' } }
+{ 'union': 'Union',
+  'base': { 'type': 'Enum' },
+  'discriminator': 'type',
+  'data': { 'a': 'Int', 'b': 'Str' } }
 { 'struct': 'MyType', 'base': 'Union', 'data': { 'c': 'int' } }

+ 2 - 11
tests/qapi-schema/doc-good.json

@@ -60,8 +60,8 @@
 #
 # @two is undocumented
 ##
-{ 'enum': 'Enum', 'data':
-  [ { 'name': 'one', 'if': 'IFONE' }, 'two' ],
+{ 'enum': 'Enum',
+  'data': [ { 'name': 'one', 'if': 'IFONE' }, 'two' ],
   'features': [ 'enum-feat' ],
   'if': 'IFCOND' }
 
@@ -107,15 +107,6 @@
             'two': { 'type': 'Variant2',
                      'if': { 'any': ['IFONE', 'IFTWO'] } } } }
 
-##
-# @SugaredUnion:
-# Features:
-# @union-feat2: a feature
-##
-{ 'union': 'SugaredUnion',
-  'features': [ 'union-feat2' ],
-  'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } }
-
 ##
 # @Alternate:
 # @i: an integer

+ 0 - 22
tests/qapi-schema/doc-good.out

@@ -32,21 +32,6 @@ object Object
     case two: Variant2
         if {'any': ['IFONE', 'IFTWO']}
     feature union-feat1
-object q_obj_Variant1-wrapper
-    member data: Variant1 optional=False
-object q_obj_Variant2-wrapper
-    member data: Variant2 optional=False
-enum SugaredUnionKind
-    member one
-    member two
-        if IFTWO
-object SugaredUnion
-    member type: SugaredUnionKind optional=False
-    tag type
-    case one: q_obj_Variant1-wrapper
-    case two: q_obj_Variant2-wrapper
-        if IFTWO
-    feature union-feat2
 alternate Alternate
     tag type
     case i: int
@@ -149,13 +134,6 @@ doc symbol=Object
 
     feature=union-feat1
 a feature
-doc symbol=SugaredUnion
-    body=
-
-    arg=type
-
-    feature=union-feat2
-a feature
 doc symbol=Alternate
     body=
 

+ 0 - 20
tests/qapi-schema/doc-good.txt

@@ -130,26 +130,6 @@ Features
    a feature
 
 
-"SugaredUnion" (Object)
------------------------
-
-
-Members
-~~~~~~~
-
-"type"
-   One of "one", "two"
-
-"data": "Variant1" when "type" is ""one""
-"data": "Variant2" when "type" is ""two"" (**If: **"IFTWO")
-
-Features
-~~~~~~~~
-
-"union-feat2"
-   a feature
-
-
 "Alternate" (Alternate)
 -----------------------
 

+ 2 - 2
tests/qapi-schema/enum-if-invalid.json

@@ -1,3 +1,3 @@
 # check invalid 'if' type
-{ 'enum': 'TestIfEnum', 'data':
-  [ 'foo', { 'name' : 'bar', 'if': { 'val': 'foo' } } ] }
+{ 'enum': 'TestIfEnum',
+  'data': [ 'foo', { 'name' : 'bar', 'if': { 'val': 'foo' } } ] }

+ 0 - 2
tests/qapi-schema/flat-union-array-branch.err

@@ -1,2 +0,0 @@
-flat-union-array-branch.json: In union 'TestUnion':
-flat-union-array-branch.json:8: 'data' member 'value1' cannot be an array

+ 0 - 2
tests/qapi-schema/flat-union-bad-base.err

@@ -1,2 +0,0 @@
-flat-union-bad-base.json: In union 'TestUnion':
-flat-union-bad-base.json:8: member 'string' of type 'TestTypeA' collides with base member 'string'

+ 0 - 2
tests/qapi-schema/flat-union-bad-discriminator.err

@@ -1,2 +0,0 @@
-flat-union-bad-discriminator.json: In union 'TestUnion':
-flat-union-bad-discriminator.json:11: 'discriminator' requires a string name

+ 0 - 2
tests/qapi-schema/flat-union-base-any.err

@@ -1,2 +0,0 @@
-flat-union-base-any.json: In union 'TestUnion':
-flat-union-base-any.json:8: 'base' requires a struct type, built-in type 'any' isn't

+ 0 - 2
tests/qapi-schema/flat-union-base-union.err

@@ -1,2 +0,0 @@
-flat-union-base-union.json: In union 'TestUnion':
-flat-union-base-union.json:14: 'base' requires a struct type, union type 'UnionBase' isn't

+ 0 - 2
tests/qapi-schema/flat-union-clash-member.err

@@ -1,2 +0,0 @@
-flat-union-clash-member.json: In union 'TestUnion':
-flat-union-clash-member.json:11: member 'name' of type 'Branch1' collides with member 'name' of type 'Base'

+ 0 - 2
tests/qapi-schema/flat-union-discriminator-bad-name.err

@@ -1,2 +0,0 @@
-flat-union-discriminator-bad-name.json: In union 'MyUnion':
-flat-union-discriminator-bad-name.json:6: discriminator '*switch' is not a member of 'base'

+ 0 - 2
tests/qapi-schema/flat-union-empty.err

@@ -1,2 +0,0 @@
-flat-union-empty.json: In union 'Union':
-flat-union-empty.json:4: union has no branches

+ 0 - 4
tests/qapi-schema/flat-union-empty.json

@@ -1,4 +0,0 @@
-# flat union discriminator cannot be empty
-{ 'enum': 'Empty', 'data': [ ] }
-{ 'struct': 'Base', 'data': { 'type': 'Empty' } }
-{ 'union': 'Union', 'base': 'Base', 'discriminator': 'type', 'data': { } }

+ 0 - 2
tests/qapi-schema/flat-union-inline-invalid-dict.err

@@ -1,2 +0,0 @@
-flat-union-inline-invalid-dict.json: In union 'TestUnion':
-flat-union-inline-invalid-dict.json:7: 'data' member 'value1' misses key 'type'

+ 0 - 2
tests/qapi-schema/flat-union-int-branch.err

@@ -1,2 +0,0 @@
-flat-union-int-branch.json: In union 'TestUnion':
-flat-union-int-branch.json:8: branch 'value1' cannot use built-in type 'int'

+ 0 - 2
tests/qapi-schema/flat-union-invalid-branch-key.err

@@ -1,2 +0,0 @@
-flat-union-invalid-branch-key.json: In union 'TestUnion':
-flat-union-invalid-branch-key.json:13: branch 'value_wrong' is not a value of enum type 'TestEnum'

+ 0 - 2
tests/qapi-schema/flat-union-invalid-discriminator.err

@@ -1,2 +0,0 @@
-flat-union-invalid-discriminator.json: In union 'TestUnion':
-flat-union-invalid-discriminator.json:10: discriminator 'enum_wrong' is not a member of 'base'

+ 0 - 2
tests/qapi-schema/flat-union-invalid-if-discriminator.err

@@ -1,2 +0,0 @@
-flat-union-invalid-if-discriminator.json: In union 'TestUnion':
-flat-union-invalid-if-discriminator.json:10: discriminator member 'enum1' of 'base' must not be conditional

+ 0 - 2
tests/qapi-schema/flat-union-no-base.err

@@ -1,2 +0,0 @@
-flat-union-no-base.json: In union 'TestUnion':
-flat-union-no-base.json:8: 'discriminator' requires 'base'

+ 0 - 2
tests/qapi-schema/flat-union-optional-discriminator.err

@@ -1,2 +0,0 @@
-flat-union-optional-discriminator.json: In union 'MyUnion':
-flat-union-optional-discriminator.json:6: discriminator member 'switch' of base type 'Base' must not be optional

+ 0 - 2
tests/qapi-schema/flat-union-string-discriminator.err

@@ -1,2 +0,0 @@
-flat-union-string-discriminator.json: In union 'TestUnion':
-flat-union-string-discriminator.json:13: discriminator member 'kind' of base type 'TestBase' must be of enum type

+ 0 - 0
tests/qapi-schema/flat-union-string-discriminator.out


+ 15 - 20
tests/qapi-schema/meson.build

@@ -107,22 +107,6 @@ schemas = [
   'features-name-bad-type.json',
   'features-no-list.json',
   'features-unknown-key.json',
-  'flat-union-array-branch.json',
-  'flat-union-bad-base.json',
-  'flat-union-bad-discriminator.json',
-  'flat-union-base-any.json',
-  'flat-union-base-union.json',
-  'flat-union-clash-member.json',
-  'flat-union-discriminator-bad-name.json',
-  'flat-union-empty.json',
-  'flat-union-inline-invalid-dict.json',
-  'flat-union-int-branch.json',
-  'flat-union-invalid-branch-key.json',
-  'flat-union-invalid-discriminator.json',
-  'flat-union-invalid-if-discriminator.json',
-  'flat-union-no-base.json',
-  'flat-union-optional-discriminator.json',
-  'flat-union-string-discriminator.json',
   'funny-char.json',
   'funny-word.json',
   'ident-with-escape.json',
@@ -168,7 +152,6 @@ schemas = [
   'reserved-member-q.json',
   'reserved-member-u.json',
   'reserved-member-underscore.json',
-  'reserved-type-kind.json',
   'reserved-type-list.json',
   'returns-alternate.json',
   'returns-array-bad.json',
@@ -191,16 +174,28 @@ schemas = [
   'unclosed-list.json',
   'unclosed-object.json',
   'unclosed-string.json',
+  'union-array-branch.json',
+  'union-bad-base.json',
+  'union-bad-discriminator.json',
+  'union-base-any.json',
   'union-base-empty.json',
   'union-base-no-discriminator.json',
-  'union-branch-case.json',
+  'union-base-union.json',
   'union-branch-if-invalid.json',
   'union-branch-invalid-dict.json',
-  'union-clash-branches.json',
+  'union-clash-member.json',
+  'union-discriminator-bad-name.json',
   'union-empty.json',
+  'union-inline-invalid-dict.json',
+  'union-int-branch.json',
   'union-invalid-base.json',
+  'union-invalid-branch-key.json',
   'union-invalid-data.json',
-  'union-optional-branch.json',
+  'union-invalid-discriminator.json',
+  'union-invalid-if-discriminator.json',
+  'union-no-base.json',
+  'union-optional-discriminator.json',
+  'union-string-discriminator.json',
   'union-unknown.json',
   'unknown-escape.json',
   'unknown-expr-key.json',

+ 25 - 26
tests/qapi-schema/qapi-schema-test.json

@@ -30,7 +30,7 @@
 { 'struct': 'Empty1', 'data': { } }
 { 'struct': 'Empty2', 'base': 'Empty1', 'data': { } }
 
-# Likewise for an empty flat union
+# Likewise for an empty union
 { 'union': 'Union',
   'base': { 'type': 'EnumOne' }, 'discriminator': 'type',
   'data': { } }
@@ -123,8 +123,7 @@
 # for testing use of 'str' within alternates
 { 'alternate': 'AltStrObj', 'data': { 's': 'str', 'o': 'TestStruct' } }
 
-# for testing lists
-{ 'union': 'UserDefListUnion',
+{ 'struct': 'ArrayStruct',
   'data': { 'integer': ['int'],
             's8': ['int8'],
             's16': ['int16'],
@@ -137,9 +136,9 @@
             'number': ['number'],
             'boolean': ['bool'],
             'string': ['str'],
-            'sizes': ['size'],
-            'any': ['any'],
-            'user': ['Status'] } } # intentional forward ref. to sub-module
+            '*sz': ['size'],
+            '*any': ['any'],
+            '*user': ['Status'] } } # intentional forward ref. to sub-module
 
 # for testing sub-modules
 { 'include': 'include/sub-module.json' }
@@ -159,7 +158,7 @@
   'returns': 'int' }
 { 'command': 'guest-sync', 'data': { 'arg': 'any' }, 'returns': 'any' }
 { 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' }
-{ 'command': 'boxed-union', 'data': 'UserDefListUnion', 'boxed': true }
+{ 'command': 'boxed-union', 'data': 'UserDefFlatUnion', 'boxed': true }
 { 'command': 'boxed-empty', 'boxed': true, 'data': 'Empty1' }
 
 # Smoke test on out-of-band and allow-preconfig-test
@@ -203,11 +202,10 @@
   'data': { '__org.qemu_x-member1': '__org.qemu_x-Enum' } }
 { 'struct': '__org.qemu_x-Struct', 'base': '__org.qemu_x-Base',
   'data': { '__org.qemu_x-member2': 'str', '*wchar-t': 'int' } }
-{ 'union': '__org.qemu_x-Union1', 'data': { '__org.qemu_x-branch': 'str' } }
 { 'alternate': '__org.qemu_x-Alt1', 'data': { '__org.qemu_x-branch': 'str' } }
 { 'struct': '__org.qemu_x-Struct2',
-  'data': { 'array': ['__org.qemu_x-Union1'] } }
-{ 'union': '__org.qemu_x-Union2', 'base': '__org.qemu_x-Base',
+  'data': { 'array': ['__org.qemu_x-Union'] } }
+{ 'union': '__org.qemu_x-Union', 'base': '__org.qemu_x-Base',
   'discriminator': '__org.qemu_x-member1',
   'data': { '__org.qemu_x-value': '__org.qemu_x-Struct2' } }
 { 'alternate': '__org.qemu_x-Alt',
@@ -215,32 +213,33 @@
 { 'event': '__ORG.QEMU_X-EVENT', 'data': '__org.qemu_x-Struct' }
 { 'command': '__org.qemu_x-command',
   'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'],
-            'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' },
-  'returns': '__org.qemu_x-Union1' }
+            'c': '__org.qemu_x-Union', 'd': '__org.qemu_x-Alt' } }
 
 # test 'if' condition handling
 
-{ 'struct': 'TestIfStruct', 'data':
-  { 'foo': 'int',
-    'bar': { 'type': 'int', 'if': 'TEST_IF_STRUCT_BAR'} },
+{ 'struct': 'TestIfStruct',
+  'data': { 'foo': 'int',
+            'bar': { 'type': 'int', 'if': 'TEST_IF_STRUCT_BAR'} },
   'if': 'TEST_IF_STRUCT' }
 
-{ 'enum': 'TestIfEnum', 'data':
-  [ 'foo', { 'name' : 'bar', 'if': 'TEST_IF_ENUM_BAR' } ],
+{ 'enum': 'TestIfEnum',
+  'data': [ 'foo', { 'name' : 'bar', 'if': 'TEST_IF_ENUM_BAR' } ],
   'if': 'TEST_IF_ENUM' }
 
-{ 'union': 'TestIfUnion', 'data':
-  { 'foo': 'TestStruct',
-    'bar': { 'type': 'str', 'if': 'TEST_IF_UNION_BAR'} },
+{ 'union': 'TestIfUnion',
+  'base': { 'type': 'TestIfEnum' },
+  'discriminator': 'type',
+  'data': { 'foo': 'TestStruct',
+            'bar': { 'type': 'UserDefZero', 'if': 'TEST_IF_ENUM_BAR'} },
   'if': { 'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT'] } }
 
 { 'command': 'test-if-union-cmd',
   'data': { 'union-cmd-arg': 'TestIfUnion' },
   'if': { 'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT'] } }
 
-{ 'alternate': 'TestIfAlternate', 'data':
-  { 'foo': 'int',
-    'bar': { 'type': 'TestStruct', 'if': 'TEST_IF_ALT_BAR'} },
+{ 'alternate': 'TestIfAlternate',
+  'data': { 'foo': 'int',
+            'bar': { 'type': 'TestStruct', 'if': 'TEST_IF_ALT_BAR'} },
   'if': { 'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT'] } }
 
 { 'command': 'test-if-alternate-cmd',
@@ -256,9 +255,9 @@
 
 { 'command': 'test-cmd-return-def-three', 'returns': 'UserDefThree' }
 
-{ 'event': 'TEST_IF_EVENT', 'data':
-  { 'foo': 'TestIfStruct',
-    'bar': { 'type': ['TestIfEnum'], 'if': 'TEST_IF_EVT_BAR' } },
+{ 'event': 'TEST_IF_EVENT',
+  'data': { 'foo': 'TestIfStruct',
+            'bar': { 'type': ['TestIfEnum'], 'if': 'TEST_IF_EVT_BAR' } },
   'if': { 'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT'] } }
 
 { 'event': 'TEST_IF_EVENT2', 'data': {},

+ 28 - 88
tests/qapi-schema/qapi-schema-test.out

@@ -125,70 +125,22 @@ alternate AltStrObj
     tag type
     case s: str
     case o: TestStruct
-object q_obj_intList-wrapper
-    member data: intList optional=False
-object q_obj_int8List-wrapper
-    member data: int8List optional=False
-object q_obj_int16List-wrapper
-    member data: int16List optional=False
-object q_obj_int32List-wrapper
-    member data: int32List optional=False
-object q_obj_int64List-wrapper
-    member data: int64List optional=False
-object q_obj_uint8List-wrapper
-    member data: uint8List optional=False
-object q_obj_uint16List-wrapper
-    member data: uint16List optional=False
-object q_obj_uint32List-wrapper
-    member data: uint32List optional=False
-object q_obj_uint64List-wrapper
-    member data: uint64List optional=False
-object q_obj_numberList-wrapper
-    member data: numberList optional=False
-object q_obj_boolList-wrapper
-    member data: boolList optional=False
-object q_obj_strList-wrapper
-    member data: strList optional=False
-object q_obj_sizeList-wrapper
-    member data: sizeList optional=False
-object q_obj_anyList-wrapper
-    member data: anyList optional=False
-object q_obj_StatusList-wrapper
-    member data: StatusList optional=False
-enum UserDefListUnionKind
-    member integer
-    member s8
-    member s16
-    member s32
-    member s64
-    member u8
-    member u16
-    member u32
-    member u64
-    member number
-    member boolean
-    member string
-    member sizes
-    member any
-    member user
-object UserDefListUnion
-    member type: UserDefListUnionKind optional=False
-    tag type
-    case integer: q_obj_intList-wrapper
-    case s8: q_obj_int8List-wrapper
-    case s16: q_obj_int16List-wrapper
-    case s32: q_obj_int32List-wrapper
-    case s64: q_obj_int64List-wrapper
-    case u8: q_obj_uint8List-wrapper
-    case u16: q_obj_uint16List-wrapper
-    case u32: q_obj_uint32List-wrapper
-    case u64: q_obj_uint64List-wrapper
-    case number: q_obj_numberList-wrapper
-    case boolean: q_obj_boolList-wrapper
-    case string: q_obj_strList-wrapper
-    case sizes: q_obj_sizeList-wrapper
-    case any: q_obj_anyList-wrapper
-    case user: q_obj_StatusList-wrapper
+object ArrayStruct
+    member integer: intList optional=False
+    member s8: int8List optional=False
+    member s16: int16List optional=False
+    member s32: int32List optional=False
+    member s64: int64List optional=False
+    member u8: uint8List optional=False
+    member u16: uint16List optional=False
+    member u32: uint32List optional=False
+    member u64: uint64List optional=False
+    member number: numberList optional=False
+    member boolean: boolList optional=False
+    member string: strList optional=False
+    member sz: sizeList optional=True
+    member any: anyList optional=True
+    member user: StatusList optional=True
 include include/sub-module.json
 command user-def-cmd None -> None
     gen=True success_response=True boxed=False oob=False preconfig=False
@@ -216,7 +168,7 @@ command guest-sync q_obj_guest-sync-arg -> any
     gen=True success_response=True boxed=False oob=False preconfig=False
 command boxed-struct UserDefZero -> None
     gen=True success_response=True boxed=True oob=False preconfig=False
-command boxed-union UserDefListUnion -> None
+command boxed-union UserDefFlatUnion -> None
     gen=True success_response=True boxed=True oob=False preconfig=False
 command boxed-empty Empty1 -> None
     gen=True success_response=True boxed=True oob=False preconfig=False
@@ -263,21 +215,13 @@ object __org.qemu_x-Struct
     base __org.qemu_x-Base
     member __org.qemu_x-member2: str optional=False
     member wchar-t: int optional=True
-object q_obj_str-wrapper
-    member data: str optional=False
-enum __org.qemu_x-Union1Kind
-    member __org.qemu_x-branch
-object __org.qemu_x-Union1
-    member type: __org.qemu_x-Union1Kind optional=False
-    tag type
-    case __org.qemu_x-branch: q_obj_str-wrapper
 alternate __org.qemu_x-Alt1
     tag type
     case __org.qemu_x-branch: str
-array __org.qemu_x-Union1List __org.qemu_x-Union1
+array __org.qemu_x-UnionList __org.qemu_x-Union
 object __org.qemu_x-Struct2
-    member array: __org.qemu_x-Union1List optional=False
-object __org.qemu_x-Union2
+    member array: __org.qemu_x-UnionList optional=False
+object __org.qemu_x-Union
     base __org.qemu_x-Base
     tag __org.qemu_x-member1
     case __org.qemu_x-value: __org.qemu_x-Struct2
@@ -291,9 +235,9 @@ array __org.qemu_x-StructList __org.qemu_x-Struct
 object q_obj___org.qemu_x-command-arg
     member a: __org.qemu_x-EnumList optional=False
     member b: __org.qemu_x-StructList optional=False
-    member c: __org.qemu_x-Union2 optional=False
+    member c: __org.qemu_x-Union optional=False
     member d: __org.qemu_x-Alt optional=False
-command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1
+command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> None
     gen=True success_response=True boxed=False oob=False preconfig=False
 object TestIfStruct
     member foo: int optional=False
@@ -305,19 +249,15 @@ enum TestIfEnum
     member bar
         if TEST_IF_ENUM_BAR
     if TEST_IF_ENUM
-object q_obj_TestStruct-wrapper
-    member data: TestStruct optional=False
-enum TestIfUnionKind
-    member foo
-    member bar
-        if TEST_IF_UNION_BAR
+object q_obj_TestIfUnion-base
+    member type: TestIfEnum optional=False
     if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']}
 object TestIfUnion
-    member type: TestIfUnionKind optional=False
+    base q_obj_TestIfUnion-base
     tag type
-    case foo: q_obj_TestStruct-wrapper
-    case bar: q_obj_str-wrapper
-        if TEST_IF_UNION_BAR
+    case foo: TestStruct
+    case bar: UserDefZero
+        if TEST_IF_ENUM_BAR
     if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']}
 object q_obj_test-if-union-cmd-arg
     member union-cmd-arg: TestIfUnion optional=False

+ 1 - 1
tests/qapi-schema/reserved-member-u.json

@@ -2,6 +2,6 @@
 # We reject use of 'u' as a member name, to allow it for internal use in
 # putting union branch members in a separate namespace from QMP members.
 # This is true even for non-unions, because it is possible to convert a
-# struct to flat union while remaining backwards compatible in QMP.
+# struct to union while remaining backwards compatible in QMP.
 # TODO - we could munge the member name to 'q_u' to avoid the collision
 { 'struct': 'Oops', 'data': { '*u': 'str' } }

+ 0 - 2
tests/qapi-schema/reserved-type-kind.err

@@ -1,2 +0,0 @@
-reserved-type-kind.json: In enum 'UnionKind':
-reserved-type-kind.json:2: enum name should not end in 'Kind'

+ 0 - 2
tests/qapi-schema/reserved-type-kind.json

@@ -1,2 +0,0 @@
-# we reject types that would conflict with implicit union enum
-{ 'enum': 'UnionKind', 'data': [ 'oops' ] }

+ 0 - 0
tests/qapi-schema/reserved-type-kind.out


+ 15 - 5
tests/qapi-schema/test-qapi.py

@@ -132,6 +132,17 @@ def test_frontend(fname):
             print('    section=%s\n%s' % (section.name, section.text))
 
 
+def open_test_result(dir_name, file_name, update):
+    mode = 'r+' if update else 'r'
+    try:
+        fp = open(os.path.join(dir_name, file_name), mode)
+    except FileNotFoundError:
+        if not update:
+            raise
+        fp = open(os.path.join(dir_name, file_name), 'w+')
+    return fp
+
+
 def test_and_diff(test_name, dir_name, update):
     sys.stdout = StringIO()
     try:
@@ -148,13 +159,12 @@ def test_and_diff(test_name, dir_name, update):
         sys.stdout.close()
         sys.stdout = sys.__stdout__
 
-    mode = 'r+' if update else 'r'
     try:
-        outfp = open(os.path.join(dir_name, test_name + '.out'), mode)
-        errfp = open(os.path.join(dir_name, test_name + '.err'), mode)
+        outfp = open_test_result(dir_name, test_name + '.out', update)
+        errfp = open_test_result(dir_name, test_name + '.err', update)
         expected_out = outfp.readlines()
         expected_err = errfp.readlines()
-    except IOError as err:
+    except OSError as err:
         print("%s: can't open '%s': %s"
               % (sys.argv[0], err.filename, err.strerror),
               file=sys.stderr)
@@ -180,7 +190,7 @@ def test_and_diff(test_name, dir_name, update):
         errfp.truncate(0)
         errfp.seek(0)
         errfp.writelines(actual_err)
-    except IOError as err:
+    except OSError as err:
         print("%s: can't write '%s': %s"
               % (sys.argv[0], err.filename, err.strerror),
               file=sys.stderr)

+ 2 - 0
tests/qapi-schema/union-array-branch.err

@@ -0,0 +1,2 @@
+union-array-branch.json: In union 'TestUnion':
+union-array-branch.json:8: 'data' member 'value1' cannot be an array

+ 1 - 1
tests/qapi-schema/flat-union-array-branch.json → tests/qapi-schema/union-array-branch.json

@@ -1,4 +1,4 @@
-# we require flat union branches to be a struct
+# we require union branches to be a struct
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
 { 'struct': 'Base',

+ 0 - 0
tests/qapi-schema/flat-union-array-branch.out → tests/qapi-schema/union-array-branch.out


+ 2 - 0
tests/qapi-schema/union-bad-base.err

@@ -0,0 +1,2 @@
+union-bad-base.json: In union 'TestUnion':
+union-bad-base.json:8: member 'string' of type 'TestTypeA' collides with base member 'string'

+ 0 - 0
tests/qapi-schema/flat-union-bad-base.json → tests/qapi-schema/union-bad-base.json


+ 0 - 0
tests/qapi-schema/flat-union-bad-base.out → tests/qapi-schema/union-bad-base.out


+ 2 - 0
tests/qapi-schema/union-bad-discriminator.err

@@ -0,0 +1,2 @@
+union-bad-discriminator.json: In union 'TestUnion':
+union-bad-discriminator.json:11: 'discriminator' requires a string name

+ 0 - 0
tests/qapi-schema/flat-union-bad-discriminator.json → tests/qapi-schema/union-bad-discriminator.json


+ 0 - 0
tests/qapi-schema/flat-union-bad-discriminator.out → tests/qapi-schema/union-bad-discriminator.out


+ 2 - 0
tests/qapi-schema/union-base-any.err

@@ -0,0 +1,2 @@
+union-base-any.json: In union 'TestUnion':
+union-base-any.json:8: 'base' requires a struct type, built-in type 'any' isn't

+ 0 - 0
tests/qapi-schema/flat-union-base-any.json → tests/qapi-schema/union-base-any.json


+ 0 - 0
tests/qapi-schema/flat-union-base-any.out → tests/qapi-schema/union-base-any.out


+ 1 - 1
tests/qapi-schema/union-base-empty.json

@@ -1,4 +1,4 @@
-# Flat union with empty base and therefore without discriminator
+# Union with empty base and therefore without discriminator
 
 { 'struct': 'Empty', 'data': { } }
 

+ 1 - 1
tests/qapi-schema/union-base-no-discriminator.err

@@ -1,2 +1,2 @@
 union-base-no-discriminator.json: In union 'TestUnion':
-union-base-no-discriminator.json:11: 'base' requires 'discriminator'
+union-base-no-discriminator.json:11: union misses key 'discriminator'

+ 1 - 1
tests/qapi-schema/union-base-no-discriminator.json

@@ -1,4 +1,4 @@
-# we reject simple unions with a base (or flat unions without discriminator)
+# we reject unions without discriminator
 { 'struct': 'TestTypeA',
   'data': { 'string': 'str' } }
 

+ 2 - 0
tests/qapi-schema/union-base-union.err

@@ -0,0 +1,2 @@
+union-base-union.json: In union 'TestUnion':
+union-base-union.json:17: 'base' requires a struct type, union type 'UnionBase' isn't

+ 3 - 0
tests/qapi-schema/flat-union-base-union.json → tests/qapi-schema/union-base-union.json

@@ -8,7 +8,10 @@
   'data': { 'string': 'str' } }
 { 'struct': 'TestTypeB',
   'data': { 'integer': 'int' } }
+{ 'enum': 'Enum', 'data': [ 'kind1', 'kind2' ] }
 { 'union': 'UnionBase',
+  'base': { 'type': 'Enum' },
+  'discriminator': 'type',
   'data': { 'kind1': 'TestTypeA',
             'kind2': 'TestTypeB' } }
 { 'union': 'TestUnion',

+ 0 - 0
tests/qapi-schema/flat-union-base-union.out → tests/qapi-schema/union-base-union.out


+ 0 - 2
tests/qapi-schema/union-branch-case.err

@@ -1,2 +0,0 @@
-union-branch-case.json: In union 'Uni':
-union-branch-case.json:2: name of 'data' member 'Branch' must not use uppercase or '_'

+ 0 - 2
tests/qapi-schema/union-branch-case.json

@@ -1,2 +0,0 @@
-# Branch names should be 'lower-case'
-{ 'union': 'Uni', 'data': { 'Branch': 'int' } }

+ 0 - 0
tests/qapi-schema/union-branch-case.out


+ 1 - 1
tests/qapi-schema/union-branch-invalid-dict.err

@@ -1,2 +1,2 @@
 union-branch-invalid-dict.json: In union 'UnionInvalidBranch':
-union-branch-invalid-dict.json:2: 'data' member 'integer' misses key 'type'
+union-branch-invalid-dict.json:4: 'data' member 'integer' misses key 'type'

+ 4 - 0
tests/qapi-schema/union-branch-invalid-dict.json

@@ -1,4 +1,8 @@
 # Long form of member must have a value member 'type'
+{ 'enum': 'TestEnum',
+  'data': [ 'integer', 's8' ] }
 { 'union': 'UnionInvalidBranch',
+  'base': { 'type': 'TestEnum' },
+  'discriminator': 'type',
   'data': { 'integer': { 'if': 'foo'},
             's8': 'int8' } }

+ 0 - 2
tests/qapi-schema/union-clash-branches.err

@@ -1,2 +0,0 @@
-union-clash-branches.json: In union 'TestUnion':
-union-clash-branches.json:6: name of 'data' member 'a_b' must not use uppercase or '_'

+ 0 - 7
tests/qapi-schema/union-clash-branches.json

@@ -1,7 +0,0 @@
-# Union branch name collision
-# Naming rules make collision impossible (even with the pragma).  If
-# that wasn't the case, then we'd get collisions in generated C: two
-# union members a_b, and two enum members TEST_UNION_A_B.
-{ 'pragma': { 'member-name-exceptions': [ 'TestUnion' ] } }
-{ 'union': 'TestUnion',
-  'data': { 'a-b': 'int', 'a_b': 'str' } }

+ 0 - 0
tests/qapi-schema/union-clash-branches.out


+ 2 - 0
tests/qapi-schema/union-clash-member.err

@@ -0,0 +1,2 @@
+union-clash-member.json: In union 'TestUnion':
+union-clash-member.json:11: member 'name' of type 'Branch1' collides with member 'name' of type 'Base'

+ 0 - 0
tests/qapi-schema/flat-union-clash-member.json → tests/qapi-schema/union-clash-member.json


+ 0 - 0
tests/qapi-schema/flat-union-clash-member.out → tests/qapi-schema/union-clash-member.out


+ 2 - 0
tests/qapi-schema/union-discriminator-bad-name.err

@@ -0,0 +1,2 @@
+union-discriminator-bad-name.json: In union 'MyUnion':
+union-discriminator-bad-name.json:6: discriminator '*switch' is not a member of 'base'

+ 0 - 0
tests/qapi-schema/flat-union-discriminator-bad-name.json → tests/qapi-schema/union-discriminator-bad-name.json


+ 0 - 0
tests/qapi-schema/flat-union-discriminator-bad-name.out → tests/qapi-schema/union-discriminator-bad-name.out


+ 1 - 1
tests/qapi-schema/union-empty.err

@@ -1,2 +1,2 @@
 union-empty.json: In union 'Union':
-union-empty.json:2: union has no branches
+union-empty.json:4: union has no branches

+ 4 - 2
tests/qapi-schema/union-empty.json

@@ -1,2 +1,4 @@
-# simple unions cannot be empty
-{ 'union': 'Union', 'data': { } }
+# union discriminator enum cannot be empty
+{ 'enum': 'Empty', 'data': [ ] }
+{ 'struct': 'Base', 'data': { 'type': 'Empty' } }
+{ 'union': 'Union', 'base': 'Base', 'discriminator': 'type', 'data': { } }

+ 2 - 0
tests/qapi-schema/union-inline-invalid-dict.err

@@ -0,0 +1,2 @@
+union-inline-invalid-dict.json: In union 'TestUnion':
+union-inline-invalid-dict.json:7: 'data' member 'value1' misses key 'type'

+ 0 - 0
tests/qapi-schema/flat-union-inline-invalid-dict.json → tests/qapi-schema/union-inline-invalid-dict.json


+ 0 - 0
tests/qapi-schema/flat-union-empty.out → tests/qapi-schema/union-inline-invalid-dict.out


+ 2 - 0
tests/qapi-schema/union-int-branch.err

@@ -0,0 +1,2 @@
+union-int-branch.json: In union 'TestUnion':
+union-int-branch.json:8: branch 'value1' cannot use built-in type 'int'

+ 1 - 1
tests/qapi-schema/flat-union-int-branch.json → tests/qapi-schema/union-int-branch.json

@@ -1,4 +1,4 @@
-# we require flat union branches to be a struct
+# we require union branches to be a struct
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
 { 'struct': 'Base',

+ 0 - 0
tests/qapi-schema/flat-union-inline-invalid-dict.out → tests/qapi-schema/union-int-branch.out


+ 2 - 0
tests/qapi-schema/union-invalid-branch-key.err

@@ -0,0 +1,2 @@
+union-invalid-branch-key.json: In union 'TestUnion':
+union-invalid-branch-key.json:13: branch 'value_wrong' is not a value of enum type 'TestEnum'

+ 0 - 0
tests/qapi-schema/flat-union-invalid-branch-key.json → tests/qapi-schema/union-invalid-branch-key.json


+ 0 - 0
tests/qapi-schema/flat-union-int-branch.out → tests/qapi-schema/union-invalid-branch-key.out


+ 2 - 0
tests/qapi-schema/union-invalid-discriminator.err

@@ -0,0 +1,2 @@
+union-invalid-discriminator.json: In union 'TestUnion':
+union-invalid-discriminator.json:10: discriminator 'enum_wrong' is not a member of 'base'

+ 0 - 0
tests/qapi-schema/flat-union-invalid-discriminator.json → tests/qapi-schema/union-invalid-discriminator.json


+ 0 - 0
tests/qapi-schema/flat-union-invalid-branch-key.out → tests/qapi-schema/union-invalid-discriminator.out


+ 2 - 0
tests/qapi-schema/union-invalid-if-discriminator.err

@@ -0,0 +1,2 @@
+union-invalid-if-discriminator.json: In union 'TestUnion':
+union-invalid-if-discriminator.json:10: discriminator member 'enum1' of 'base' must not be conditional

+ 0 - 0
tests/qapi-schema/flat-union-invalid-if-discriminator.json → tests/qapi-schema/union-invalid-if-discriminator.json


+ 0 - 0
tests/qapi-schema/flat-union-invalid-discriminator.out → tests/qapi-schema/union-invalid-if-discriminator.out


+ 2 - 0
tests/qapi-schema/union-no-base.err

@@ -0,0 +1,2 @@
+union-no-base.json: In union 'TestUnion':
+union-no-base.json:8: union misses key 'base'

+ 1 - 1
tests/qapi-schema/flat-union-no-base.json → tests/qapi-schema/union-no-base.json

@@ -1,4 +1,4 @@
-# flat unions require a base
+# unions require a base
 { 'struct': 'TestTypeA',
   'data': { 'string': 'str' } }
 { 'struct': 'TestTypeB',

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác