All of lore.kernel.org
 help / color / mirror / Atom feed
From: Markus Armbruster <armbru@redhat.com>
To: Eric Blake <eblake@redhat.com>
Cc: qemu-devel@nongnu.org, Michael Roth <mdroth@linux.vnet.ibm.com>
Subject: Re: [Qemu-devel] [PATCH v9 06/17] qapi-visit: Remove redundant functions for flat union base
Date: Thu, 22 Oct 2015 10:32:06 +0200	[thread overview]
Message-ID: <8737x3b8xl.fsf@blackfin.pond.sub.org> (raw)
In-Reply-To: <5627E0FE.60207@redhat.com> (Eric Blake's message of "Wed, 21 Oct 2015 13:01:18 -0600")

Eric Blake <eblake@redhat.com> writes:

> On 10/21/2015 11:36 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> The code for visiting the base class of a child struct created
>>> visit_type_Base_fields() which covers all fields of Base; while
>>> the code for visiting the base class of a flat union created
>>> visit_type_Union_fields() covering all fields of the base
>>> except the discriminator.  But if the base class were to always
>>> visit all its fields, then we wouldn't need a separate visit of
>>> the discriminator for a union.  Not only is consistently
>>> visiting all fields easier to understand, it lets us share code.
>>>
>>> Now that gen_visit_struct_fields() can potentially collect more
>>> than one function into 'ret', a regular expression searching for
>>> whether a label was used may hit a false positive within the
>>> body of the first function.  But using a regex was overkill,
>>> since we can easily determine when we jumped to a label.
>>>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>>
>
>>> +++ b/scripts/qapi-visit.py
>>> @@ -90,7 +90,7 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e
>>>
>>>      ret += gen_visit_fields(members, prefix='(*obj)->')
>>>
>>> -    if re.search('^ *goto out;', ret, re.MULTILINE):
>>> +    if base or members:
>> 
>> What if we have an empty base and no members?  Empty base is a
>> pathological case, admittedly.
>
> I'm going to filter the re.search cleanups into its own prereq patch.
> But yes, it will need to care for empty base and no members (hmm, I
> really ought to add positive tests to qapi-schema-test for an empty
> inherited struct, to make sure I'm getting it right - even if we don't
> want that patch in the final series).

Don't take my reluctance to take some positive tests as general
opposition towards positive tests!  Positive tests for corner cases like
"empty base" are valuable.

>> Diff is confusing (not your fault).  Let me compare code before and
>> after real slow.
>
> I also plan for v10 to include a diff of the generated code in the
> commit message, if that will help make the change more obvious.
>
>> 
>> = Before =
>> 
>>   def gen_visit_union(name, base, variants):
>>       ret = ''
>> 
>> 0. base is None if and only if the union is simple.
>> 
>> 1. If it's a flat union, generate its visit_type_NAME_fields().  This
>
> where NAME is the union name.
>
>> function visits the union's non-variant members *except* the
>> discriminator.  Since a simple union has no non-variant members other
>> than the discriminator, generate it only for flat unions.
>> 
>>       if base:
>>           members = [m for m in base.members if m != variants.tag_member]
>>           ret += gen_visit_struct_fields(name, None, members)
>> 
>> 2. Generate the visit_type_implicit_FOO() we're going to need.
>> 
>>       for var in variants.variants:
>>           # Ugly special case for simple union TODO get rid of it
>>           if not var.simple_union_type():
>>               ret += gen_visit_implicit_struct(var.type)
>
> Could be made slightly simpler by generating these while we iterate over
> cases (but we'd have to be careful to generate into multiple variables,
> and then concat together at the end, since we can't generate one
> function in the body of the other).

I doubt it would be an improvement.  The loop is trivial, so
de-duplicating it doesn't have much value.  Having generator code
arranged in the same order as the generated code *does* have value.

>> 3. Generate its visit_type_NAME().
>> 
>
>> 
>> 3.a. If it's a flat union, generate the call of
>> visit_type_NAME_fields().  Not necessary for simple unions, see 1.
>
> Again, important to note that this was visit_type_UNION_fields().
>
>> 3.b. Generate visit of discriminator.
>> 
>
>> 
>> 3.c. Generate visit of the active variant.
>> 
>
>> = After =
>> 
>>   def gen_visit_union(name, base, variants):
>>       ret = ''
>> 
>> 0. base is None if and only if the union is simple.
>> 
>> 1. If it's a flat union, generate its visit_type_NAME_fields().  This
>> function visits the union's non-variant members *including* the
>> discriminator.  However, we generate it only for flat unions.  Simple
>> unions have no non-variant members other than the discriminator.
>> 
>>       if base:
>>           ret += gen_visit_struct_fields(base.name, base.base,
>>                                          base.local_members)
>
> Note that this creates visit_type_BASE_fields() (a different function).

Missed this detail, thanks.  The old visit_type_UNION_fields() is
visit_type_BASE_fields() less the tag visit.  Reusing
visit_type_BASE_fields() instead behaves as I described above, so my
analysis remains valid regardless.

visit_type_BASE_fields() should be generated when gen_visit_struct()
processes BASE.  Here, we should only generate a forward declaration, if
necessary.

>> 
>> 2. Generate the visit_type_implicit_FOO() we're going to need.
>> 
>>       for var in variants.variants:
>>           # Ugly special case for simple union TODO get rid of it
>>           if not var.simple_union_type():
>>               ret += gen_visit_implicit_struct(var.type)
>> 
>> 3. Generate its visit_type_NAME().
>> 
>
>> 
>> 3.a. If it's a flat union, generate the call of
>> visit_type_NAME_fields().  Not done for simple unions, see 1.
>
> Again, now NAME is the base name rather than the union name.  Subtle but
> important difference!

Indeed.

Until now, having a separate visit_type_NAME_fields() function was
pretty pointless: it was only called from visit_type_NAME().  Now, it's
also called from visit_type_UNION() when NAME is UNION's base.  Bonus:
we don't duplicate the code visiting NAME's members into every union
using it as base (this is what the commit message refers to when it says
"it lets us share code").

Can we do the same for structs, please?

The nice thing about generating code is that you don't have to
copy/paste so much to get all the duplication your heart desires.

>> 
>>       if base:
>>           ret += mcgen('''
>>       visit_type_%(c_name)s_fields(v, (%(c_name)s **)obj, &err);
>>   ''',
>>                        c_name=base.c_name())
>> 
>> 3.b. If it's a simple union, generate the visit of the sole non-variant
>> member inline.
>> 
>
>> 
>> 3.a+b. Generate the error check for visit of non-variant members
>> 
>>       ret += gen_err_check(label='out_obj')
>> 
>> 3.c. Generate visit of the active variant.
>> 
>
>> 
>> Okay, the change to gen_visit_union() looks sane.
>
> Yes, you got it all correct.
>
>> 
>> Can we go one step further and generate and use visit_type_NAME_fields()
>> even for simple unions?
>
> Not easily.  Remember, for flat unions, we are calling
> visit_type_BASE_fields, not visit_type_UNION_fields.  There is no base
> for a simple union.

Scratch the idea, I was confused.

> What I _am_ planning for future patches is the following:
>
> Change QAPISchemaObjectType for simple unions and alternates to set
> .local_members to the one-element implicit discriminator (right now, it
> is always an empty array, and we even assert that bool(.local_members)
> and bool(.variants) are mutually-exclusive in at least qapi-types.py
> visit_object_type()).  Flat unions would keep .local_members as an empty
> array (there is no local member, just the inherited members from the
> base class, which includes the inherited discriminator).
>
> Then merge gen_visit_struct() and gen_visit_union() to look like:
>
> if base:
>     # includes discriminator for flat union
>     call visit_type_BASE_fields()
> for m in .local_members
>     # includes discriminator for simple union
>     call visit_type_MEMBER()
> if variants
>     emit switch statement to visit each branch
>
> But if we want, that 'for m in .local_members' block can indeed be
> implemented via a call to visit_type_UNION_fields(), if that is any more
> efficient to implement.  I guess it also boils down to deciding if
> visit_type_FOO_fields() should continue to be unconditional (either for
> all types, or at least for all types with non-empty .local_members).

Keeping the implicit tag implicit by not including it in local_members
was a conscious design decision, but I'm quite open to revisit it.

But perhaps we don't have to!  We got simple unions because "first past
the post": they solved immediate needs.  When other needs arose, flat
unions got bolted on.  We kept simple unions because the bolted-on flat
unions are bothersome to define.  If we can fuse struct and union types
into a reasonably pleasant variant record type, we may not need a simple
union sugar anymore.

  reply	other threads:[~2015-10-22  8:32 UTC|newest]

Thread overview: 57+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-10-16  4:15 [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 01/17] qapi: Add tests for reserved name abuse Eric Blake
2015-10-19 16:05   ` Markus Armbruster
2015-10-20 16:23     ` Eric Blake
2015-10-21 12:08       ` Markus Armbruster
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 02/17] qapi: Reserve '*List' type names for arrays Eric Blake
2015-10-19 16:14   ` Markus Armbruster
2015-10-20 18:12     ` Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 03/17] qapi: Reserve 'u' and 'has[-_]*' member names Eric Blake
2015-10-19 17:19   ` Markus Armbruster
2015-10-20 21:29     ` Eric Blake
2015-10-21 13:08       ` Markus Armbruster
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 04/17] vnc: hoist allocation of VncBasicInfo to callers Eric Blake
2015-10-20  7:38   ` Markus Armbruster
2015-10-20  8:54     ` Gerd Hoffmann
2015-10-20 14:46       ` Markus Armbruster
2015-10-20 22:53         ` Eric Blake
2015-10-21 11:02           ` Markus Armbruster
2015-10-21 11:16           ` Daniel P. Berrange
2015-10-23 13:13             ` Markus Armbruster
2015-10-20 22:56     ` Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 05/17] qapi: Unbox base members Eric Blake
2015-10-16 19:12   ` [Qemu-devel] [PATCH v9 05.5/17] fixup to " Eric Blake
2015-10-20 12:09   ` [Qemu-devel] [PATCH v9 05/17] " Markus Armbruster
2015-10-20 16:08     ` Eric Blake
2015-10-21 13:34       ` Markus Armbruster
2015-10-21 21:16         ` Eric Blake
2015-10-22  6:28           ` Markus Armbruster
2015-10-23  1:50         ` Eric Blake
2015-10-23  6:26           ` Markus Armbruster
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 06/17] qapi-visit: Remove redundant functions for flat union base Eric Blake
2015-10-21 17:36   ` Markus Armbruster
2015-10-21 19:01     ` Eric Blake
2015-10-22  8:32       ` Markus Armbruster [this message]
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 07/17] qapi: Start converting to new qapi union layout Eric Blake
2015-10-22 13:54   ` Markus Armbruster
2015-10-22 14:09     ` Eric Blake
2015-10-22 14:44       ` Markus Armbruster
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 08/17] tests: Convert " Eric Blake
2015-10-22 14:01   ` Markus Armbruster
2015-10-22 14:22     ` Eric Blake
2015-10-22 14:57       ` Markus Armbruster
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 09/17] block: " Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 10/17] nbd: " Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 11/17] net: " Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 12/17] char: " Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 13/17] input: " Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 14/17] memory: " Eric Blake
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 15/17] tpm: " Eric Blake
2015-10-22 14:19   ` Markus Armbruster
2015-10-22 14:26     ` Eric Blake
2015-10-22 16:40       ` Eric Blake
2015-10-23  6:24         ` Markus Armbruster
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 16/17] qapi: Finish converting " Eric Blake
2015-10-22 14:50   ` Markus Armbruster
2015-10-16  4:15 ` [Qemu-devel] [PATCH v9 17/17] qapi: Simplify gen_struct_field() Eric Blake
2015-10-22 15:13 ` [Qemu-devel] [PATCH v9 00/17] qapi collision reduction (post-introspection subset B') Markus Armbruster

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8737x3b8xl.fsf@blackfin.pond.sub.org \
    --to=armbru@redhat.com \
    --cc=eblake@redhat.com \
    --cc=mdroth@linux.vnet.ibm.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.