diff mbox

qapi-visit: Honor prefix of discriminator enum

Message ID 1455665965-27638-1-git-send-email-eblake@redhat.com
State New
Headers show

Commit Message

Eric Blake Feb. 16, 2016, 11:39 p.m. UTC
When we added support for a user-specified prefix for an enum
type (commit 351d36e), we forgot to teach the qapi-visit code
to honor that prefix in the case of using a prefixed enum as
the discriminator for a flat union.  While there is still some
on-list debate on whether we want to keep prefixes, we should
at least make it work as long as it is still part of the code
base.

Reported-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
---
 scripts/qapi-visit.py                   | 3 ++-
 tests/qapi-schema/qapi-schema-test.json | 9 ++++++---
 tests/qapi-schema/qapi-schema-test.out  | 7 +++++--
 3 files changed, 13 insertions(+), 6 deletions(-)

Comments

Markus Armbruster Feb. 17, 2016, 12:05 p.m. UTC | #1
Eric Blake <eblake@redhat.com> writes:

> When we added support for a user-specified prefix for an enum
> type (commit 351d36e), we forgot to teach the qapi-visit code
> to honor that prefix in the case of using a prefixed enum as
> the discriminator for a flat union.  While there is still some
> on-list debate on whether we want to keep prefixes, we should
> at least make it work as long as it is still part of the code
> base.
>
> Reported-by: Daniel P. Berrange <berrange@redhat.com>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> ---
>  scripts/qapi-visit.py                   | 3 ++-
>  tests/qapi-schema/qapi-schema-test.json | 9 ++++++---
>  tests/qapi-schema/qapi-schema-test.out  | 7 +++++--
>  3 files changed, 13 insertions(+), 6 deletions(-)
>
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 0cc9b08..2bdb5a1 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -293,7 +293,8 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>      case %(case)s:
>  ''',
>                       case=c_enum_const(variants.tag_member.type.name,
> -                                       var.name))
> +                                       var.name,
> +                                       variants.tag_member.type.prefix))
>          if simple_union_type:
>              ret += mcgen('''
>          visit_type_%(c_type)s(v, "data", &(*obj)->u.%(c_name)s, &err);

Yup.

Let's check for completeness.  Calls of c_enum_const():

* QAPISchemaEnumType.c_null() and (with your patch) gen_visit_union()
  call it like

    c_enum_const(TYPE.name, MEMBER, TYPE.prefix)

  where MEMBER is a member of enumeration type TYPE.

  As your patch shows, the prefix is easy to forget.  A safer function
  would take just TYPE and MEMBER:

    TYPE.c_member(MEMBER)

* gen_event_send() calls

    c_enum_const(event_enum_name, name)

  where name is member of the enum type named event_enum_name.  That's
  okay because the type is auto-generated without a prefix.  Regardless,
  we could do something like

    schema.lookup_type(event_enum_name).c_member(name)

  Requires actually constructing the type, which is probably a good idea
  anyway, because it gets us the necessary collision checks.  Replacing
  global event_enum_name by event_enum_type would be nice then.  Out of
  scope for this patch.

* gen_enum_lookup() and gen_enum() work on name, values, prefix instead
  of the type.  I figure they do to support qapi-event.py.  If we clean
  it up to create the type, these functions could use the type as well,
  and then c_enum_const() could be dropped.

Bottom line for this patch: the fix is complete.

> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index 4b89527..353a34e 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -73,14 +73,17 @@
>    'base': 'UserDefZero',
>    'data': { 'string': 'str', 'enum1': 'EnumOne' } }
>
> +{ 'struct': 'UserDefUnionBase2',
> +  'base': 'UserDefZero',
> +  'data': { 'string': 'str', 'enum1': 'QEnumTwo' } }
> +
>  # this variant of UserDefFlatUnion defaults to a union that uses fields with
>  # allocated types to test corner cases in the cleanup/dealloc visitor
>  { 'union': 'UserDefFlatUnion2',
> -  'base': 'UserDefUnionBase',
> +  'base': 'UserDefUnionBase2',
>    'discriminator': 'enum1',
>    'data': { 'value1' : 'UserDefC', # intentional forward reference
> -            'value2' : 'UserDefB',
> -            'value3' : 'UserDefA' } }
> +            'value2' : 'UserDefB' } }
>
>  { 'alternate': 'UserDefAlternate',
>    'data': { 'uda': 'UserDefA', 's': 'str', 'i': 'int' } }
> diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
> index 2c546b7..241aadb 100644
> --- a/tests/qapi-schema/qapi-schema-test.out
> +++ b/tests/qapi-schema/qapi-schema-test.out
> @@ -121,11 +121,10 @@ object UserDefFlatUnion
>      case value2: UserDefB
>      case value3: UserDefB
>  object UserDefFlatUnion2
> -    base UserDefUnionBase
> +    base UserDefUnionBase2
>      tag enum1
>      case value1: UserDefC
>      case value2: UserDefB
> -    case value3: UserDefA
>  object UserDefNativeListUnion
>      member type: UserDefNativeListUnionKind optional=False
>      case integer: :obj-intList-wrapper
> @@ -167,6 +166,10 @@ object UserDefUnionBase
>      base UserDefZero
>      member string: str optional=False
>      member enum1: EnumOne optional=False
> +object UserDefUnionBase2
> +    base UserDefZero
> +    member string: str optional=False
> +    member enum1: QEnumTwo optional=False
>  object UserDefZero
>      member integer: int optional=False
>  event __ORG.QEMU_X-EVENT __org.qemu_x-Struct

Applied to qapi-next, thanks!
Eric Blake Feb. 17, 2016, 1:43 p.m. UTC | #2
On 02/17/2016 05:05 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> When we added support for a user-specified prefix for an enum
>> type (commit 351d36e), we forgot to teach the qapi-visit code
>> to honor that prefix in the case of using a prefixed enum as
>> the discriminator for a flat union.  While there is still some
>> on-list debate on whether we want to keep prefixes, we should
>> at least make it work as long as it is still part of the code
>> base.
>>

> Let's check for completeness.  Calls of c_enum_const():
> 
> * QAPISchemaEnumType.c_null() and (with your patch) gen_visit_union()
>   call it like
> 
>     c_enum_const(TYPE.name, MEMBER, TYPE.prefix)
> 
>   where MEMBER is a member of enumeration type TYPE.
> 
>   As your patch shows, the prefix is easy to forget.  A safer function
>   would take just TYPE and MEMBER:
> 
>     TYPE.c_member(MEMBER)

Yes, that would be nicer, but more invasive...

> 
> * gen_event_send() calls
> 
>     c_enum_const(event_enum_name, name)
> 
>   where name is member of the enum type named event_enum_name.  That's
>   okay because the type is auto-generated without a prefix.  Regardless,
>   we could do something like
> 
>     schema.lookup_type(event_enum_name).c_member(name)
> 
>   Requires actually constructing the type, which is probably a good idea
>   anyway, because it gets us the necessary collision checks.  Replacing
>   global event_enum_name by event_enum_type would be nice then.  Out of
>   scope for this patch.

...and that's why the quick patch now rather than a more invasive
refactor is reasonable.  I've added this refactor to my list of things
that might be worth doing some later day, but certainly not before the
current queue is flushed.

> 
> * gen_enum_lookup() and gen_enum() work on name, values, prefix instead
>   of the type.  I figure they do to support qapi-event.py.  If we clean
>   it up to create the type, these functions could use the type as well,
>   and then c_enum_const() could be dropped.
> 
> Bottom line for this patch: the fix is complete.

Good, your analysis matched mine.
Eric Blake March 7, 2016, 11:23 p.m. UTC | #3
On 02/17/2016 05:05 AM, Markus Armbruster wrote:
> Let's check for completeness.  Calls of c_enum_const():
> 
> * QAPISchemaEnumType.c_null() and (with your patch) gen_visit_union()
>   call it like
> 
>     c_enum_const(TYPE.name, MEMBER, TYPE.prefix)
> 
>   where MEMBER is a member of enumeration type TYPE.
> 
>   As your patch shows, the prefix is easy to forget.  A safer function
>   would take just TYPE and MEMBER:
> 
>     TYPE.c_member(MEMBER)
> 

I took a look at this email again today, and while it still sounds like
a nice action, I wasn't able to get it done quickly (certainly not 2.6
material, at any rate).

> * gen_event_send() calls
> 
>     c_enum_const(event_enum_name, name)
> 
>   where name is member of the enum type named event_enum_name.  That's
>   okay because the type is auto-generated without a prefix.  Regardless,
>   we could do something like
> 
>     schema.lookup_type(event_enum_name).c_member(name)
> 
>   Requires actually constructing the type, which is probably a good idea
>   anyway, because it gets us the necessary collision checks.  Replacing
>   global event_enum_name by event_enum_type would be nice then.  Out of
>   scope for this patch.
> 
> * gen_enum_lookup() and gen_enum() work on name, values, prefix instead
>   of the type.  I figure they do to support qapi-event.py.  If we clean
>   it up to create the type, these functions could use the type as well,
>   and then c_enum_const() could be dropped.

Well, they also do it to support qapi-types.py, where the
visit_enum_type() visitor callback has already broken things out into
name/values/prefix instead of providing a type, and where we don't have
easy access to the schema to do schema.lookup_type(name).

As I've worked more with the visitors, I'm really wondering if
visit_enum_type(self, type, info) would be a saner callback interface
than visit_enum_type(self, type.name, info, type.values, type.prefix)
Markus Armbruster March 8, 2016, 8:28 a.m. UTC | #4
Eric Blake <eblake@redhat.com> writes:

> On 02/17/2016 05:05 AM, Markus Armbruster wrote:
>> Let's check for completeness.  Calls of c_enum_const():
>> 
>> * QAPISchemaEnumType.c_null() and (with your patch) gen_visit_union()
>>   call it like
>> 
>>     c_enum_const(TYPE.name, MEMBER, TYPE.prefix)
>> 
>>   where MEMBER is a member of enumeration type TYPE.
>> 
>>   As your patch shows, the prefix is easy to forget.  A safer function
>>   would take just TYPE and MEMBER:
>> 
>>     TYPE.c_member(MEMBER)
>> 
>
> I took a look at this email again today, and while it still sounds like
> a nice action, I wasn't able to get it done quickly (certainly not 2.6
> material, at any rate).
>
>> * gen_event_send() calls
>> 
>>     c_enum_const(event_enum_name, name)
>> 
>>   where name is member of the enum type named event_enum_name.  That's
>>   okay because the type is auto-generated without a prefix.  Regardless,
>>   we could do something like
>> 
>>     schema.lookup_type(event_enum_name).c_member(name)
>> 
>>   Requires actually constructing the type, which is probably a good idea
>>   anyway, because it gets us the necessary collision checks.  Replacing
>>   global event_enum_name by event_enum_type would be nice then.  Out of
>>   scope for this patch.
>> 
>> * gen_enum_lookup() and gen_enum() work on name, values, prefix instead
>>   of the type.  I figure they do to support qapi-event.py.  If we clean
>>   it up to create the type, these functions could use the type as well,
>>   and then c_enum_const() could be dropped.
>
> Well, they also do it to support qapi-types.py, where the
> visit_enum_type() visitor callback has already broken things out into
> name/values/prefix instead of providing a type, and where we don't have
> easy access to the schema to do schema.lookup_type(name).
>
> As I've worked more with the visitors, I'm really wondering if
> visit_enum_type(self, type, info) would be a saner callback interface
> than visit_enum_type(self, type.name, info, type.values, type.prefix)

Maybe it is, but I'm not sure.  The existing visit_enum_type() forces
discipline: you can't just use whatever property of the type you like.
Discipline is good until you overdo it and it becomes a straightjacket.

Let's not worry about it now, but concentrate on keeping the existing,
lengthy QAPI queue moving.
diff mbox

Patch

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 0cc9b08..2bdb5a1 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -293,7 +293,8 @@  void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
     case %(case)s:
 ''',
                      case=c_enum_const(variants.tag_member.type.name,
-                                       var.name))
+                                       var.name,
+                                       variants.tag_member.type.prefix))
         if simple_union_type:
             ret += mcgen('''
         visit_type_%(c_type)s(v, "data", &(*obj)->u.%(c_name)s, &err);
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 4b89527..353a34e 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -73,14 +73,17 @@ 
   'base': 'UserDefZero',
   'data': { 'string': 'str', 'enum1': 'EnumOne' } }

+{ 'struct': 'UserDefUnionBase2',
+  'base': 'UserDefZero',
+  'data': { 'string': 'str', 'enum1': 'QEnumTwo' } }
+
 # this variant of UserDefFlatUnion defaults to a union that uses fields with
 # allocated types to test corner cases in the cleanup/dealloc visitor
 { 'union': 'UserDefFlatUnion2',
-  'base': 'UserDefUnionBase',
+  'base': 'UserDefUnionBase2',
   'discriminator': 'enum1',
   'data': { 'value1' : 'UserDefC', # intentional forward reference
-            'value2' : 'UserDefB',
-            'value3' : 'UserDefA' } }
+            'value2' : 'UserDefB' } }

 { 'alternate': 'UserDefAlternate',
   'data': { 'uda': 'UserDefA', 's': 'str', 'i': 'int' } }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 2c546b7..241aadb 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -121,11 +121,10 @@  object UserDefFlatUnion
     case value2: UserDefB
     case value3: UserDefB
 object UserDefFlatUnion2
-    base UserDefUnionBase
+    base UserDefUnionBase2
     tag enum1
     case value1: UserDefC
     case value2: UserDefB
-    case value3: UserDefA
 object UserDefNativeListUnion
     member type: UserDefNativeListUnionKind optional=False
     case integer: :obj-intList-wrapper
@@ -167,6 +166,10 @@  object UserDefUnionBase
     base UserDefZero
     member string: str optional=False
     member enum1: EnumOne optional=False
+object UserDefUnionBase2
+    base UserDefZero
+    member string: str optional=False
+    member enum1: QEnumTwo optional=False
 object UserDefZero
     member integer: int optional=False
 event __ORG.QEMU_X-EVENT __org.qemu_x-Struct