diff mbox

[v2,14/19] qapi: Don't special-case simple union wrappers

Message ID 1456443528-13901-15-git-send-email-eblake@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

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

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

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

Comments

Markus Armbruster March 3, 2016, 10:59 a.m. UTC | #1
Eric Blake <eblake@redhat.com> writes:

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

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

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

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

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

Can we work that into the commit message?

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

Suggest "is also affected by the layout change".

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

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

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

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

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

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

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

Before:

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

Special treatment for simple unions: don't unbox.

After:

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

Special treatment for simple unions: instead of a member

    TypeOfBranch name_of_branch;

we generate one

    struct {
        TypeOfBranch data;
    } name_of_branch;

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

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

qapi-schema-test.out:

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

where

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

qapi-types.h:

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

Without the special case, we'd get

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Separate function for implicit type?

Before:

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

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

After:

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

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

Should it exist?

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

On to the boring part.

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

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

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

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

Yes, definitely.


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

I may have missed something, but it is a compiler error, so someone
would flag it pretty quickly if they are set up to compile code that I
am not.
diff mbox

Patch

diff that looks like:

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

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

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

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

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

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

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

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

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


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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

     assert(peer);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

     test_io_channel(async, listen_addr, connect_addr, true);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

     return addr;