From c0644771ebedbd8f47f3c24816445e30111d226b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 22 May 2017 18:42:15 +0200 Subject: qapi: Reject alternates that can't work with keyval_parse() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Alternates are sum types like unions, but use the JSON type on the wire / QType in QObject instead of an explicit tag. That's why we require alternate members to have distinct QTypes. The recently introduced keyval_parse() (commit d454dbe) can only produce string scalars. The qobject_input_visitor_new_keyval() input visitor mostly hides the difference, so code using a QObject input visitor doesn't have to care whether its input was parsed from JSON or KEY=VALUE,... The difference leaks for alternates, as noted in commit 0ee9ae7: a non-string, non-enum scalar alternate value can't currently be expressed. In part, this is just our insufficiently sophisticated implementation. Consider alternate type 'GuestFileWhence'. It has an integer member and a 'QGASeek' member. The latter is an enumeration with values 'set', 'cur', 'end'. The meaning of b=set, b=cur, b=end, b=0, b=1 and so forth is perfectly obvious. However, our current implementation falls apart at run time for b=0, b=1, and so forth. Fixable, but not today; add a test case and a TODO comment. Now consider an alternate type with a string and an integer member. What's the meaning of a=42? Is it the string "42" or the integer 42? Whichever meaning you pick makes the other inexpressible. This isn't just an implementation problem, it's fundamental. Our current implementation will pick string. So far, we haven't needed such alternates. To make sure we stop and think before we add one that cannot sanely work with keyval_parse(), let's require alternate members to have sufficiently distinct representation in KEY=VALUE,... syntax: * A string member clashes with any other scalar member * An enumeration member clashes with bool members when it has value 'on' or 'off'. * An enumeration member clashes with numeric members when it has a value that starts with '-', '+', or a decimal digit. This is a rather lazy approximation of the actual number syntax accepted by the visitor. Note that enumeration values starting with '-' and '+' are rejected elsewhere already, but better safe than sorry. Signed-off-by: Markus Armbruster Message-Id: <1495471335-23707-5-git-send-email-armbru@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Marc-André Lureau --- tests/qapi-schema/alternate-conflict-dict.json | 2 +- tests/qapi-schema/alternate-conflict-enum-bool.err | 1 + tests/qapi-schema/alternate-conflict-enum-bool.exit | 1 + tests/qapi-schema/alternate-conflict-enum-bool.json | 6 ++++++ tests/qapi-schema/alternate-conflict-enum-bool.out | 0 tests/qapi-schema/alternate-conflict-enum-int.err | 1 + tests/qapi-schema/alternate-conflict-enum-int.exit | 1 + tests/qapi-schema/alternate-conflict-enum-int.json | 6 ++++++ tests/qapi-schema/alternate-conflict-enum-int.out | 0 tests/qapi-schema/alternate-conflict-string.err | 2 +- tests/qapi-schema/alternate-conflict-string.json | 6 ++---- tests/qapi-schema/qapi-schema-test.json | 4 +++- tests/qapi-schema/qapi-schema-test.out | 4 ++-- 13 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 tests/qapi-schema/alternate-conflict-enum-bool.err create mode 100644 tests/qapi-schema/alternate-conflict-enum-bool.exit create mode 100644 tests/qapi-schema/alternate-conflict-enum-bool.json create mode 100644 tests/qapi-schema/alternate-conflict-enum-bool.out create mode 100644 tests/qapi-schema/alternate-conflict-enum-int.err create mode 100644 tests/qapi-schema/alternate-conflict-enum-int.exit create mode 100644 tests/qapi-schema/alternate-conflict-enum-int.json create mode 100644 tests/qapi-schema/alternate-conflict-enum-int.out (limited to 'tests/qapi-schema') diff --git a/tests/qapi-schema/alternate-conflict-dict.json b/tests/qapi-schema/alternate-conflict-dict.json index d566cca816..3d7881279c 100644 --- a/tests/qapi-schema/alternate-conflict-dict.json +++ b/tests/qapi-schema/alternate-conflict-dict.json @@ -1,4 +1,4 @@ -# we reject alternates with multiple object branches +# alternate branches of object type conflict with each other { 'struct': 'One', 'data': { 'name': 'str' } } { 'struct': 'Two', diff --git a/tests/qapi-schema/alternate-conflict-enum-bool.err b/tests/qapi-schema/alternate-conflict-enum-bool.err new file mode 100644 index 0000000000..0dfc00242d --- /dev/null +++ b/tests/qapi-schema/alternate-conflict-enum-bool.err @@ -0,0 +1 @@ +tests/qapi-schema/alternate-conflict-enum-bool.json:4: Alternate 'Alt' member 'two' can't be distinguished from member 'one' diff --git a/tests/qapi-schema/alternate-conflict-enum-bool.exit b/tests/qapi-schema/alternate-conflict-enum-bool.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/alternate-conflict-enum-bool.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/alternate-conflict-enum-bool.json b/tests/qapi-schema/alternate-conflict-enum-bool.json new file mode 100644 index 0000000000..bff25c3147 --- /dev/null +++ b/tests/qapi-schema/alternate-conflict-enum-bool.json @@ -0,0 +1,6 @@ +# alternate branch of 'enum' type that conflicts with bool +{ 'enum': 'Enum', + 'data': [ 'aus', 'off' ] } +{ 'alternate': 'Alt', + 'data': { 'one': 'Enum', + 'two': 'bool' } } diff --git a/tests/qapi-schema/alternate-conflict-enum-bool.out b/tests/qapi-schema/alternate-conflict-enum-bool.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/alternate-conflict-enum-int.err b/tests/qapi-schema/alternate-conflict-enum-int.err new file mode 100644 index 0000000000..2cc8e7b9aa --- /dev/null +++ b/tests/qapi-schema/alternate-conflict-enum-int.err @@ -0,0 +1 @@ +tests/qapi-schema/alternate-conflict-enum-int.json:4: Alternate 'Alt' member 'two' can't be distinguished from member 'one' diff --git a/tests/qapi-schema/alternate-conflict-enum-int.exit b/tests/qapi-schema/alternate-conflict-enum-int.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/alternate-conflict-enum-int.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/alternate-conflict-enum-int.json b/tests/qapi-schema/alternate-conflict-enum-int.json new file mode 100644 index 0000000000..beb428c10b --- /dev/null +++ b/tests/qapi-schema/alternate-conflict-enum-int.json @@ -0,0 +1,6 @@ +# alternate branches of 'enum' type that conflicts with numbers +{ 'enum': 'Enum', + 'data': [ '1', '2', '3' ] } +{ 'alternate': 'Alt', + 'data': { 'one': 'Enum', + 'two': 'int' } } diff --git a/tests/qapi-schema/alternate-conflict-enum-int.out b/tests/qapi-schema/alternate-conflict-enum-int.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/alternate-conflict-string.err b/tests/qapi-schema/alternate-conflict-string.err index fc523b0879..fe2f188295 100644 --- a/tests/qapi-schema/alternate-conflict-string.err +++ b/tests/qapi-schema/alternate-conflict-string.err @@ -1 +1 @@ -tests/qapi-schema/alternate-conflict-string.json:4: Alternate 'Alt' member 'two' can't be distinguished from member 'one' +tests/qapi-schema/alternate-conflict-string.json:2: Alternate 'Alt' member 'two' can't be distinguished from member 'one' diff --git a/tests/qapi-schema/alternate-conflict-string.json b/tests/qapi-schema/alternate-conflict-string.json index 72f04a820a..85adbd4adc 100644 --- a/tests/qapi-schema/alternate-conflict-string.json +++ b/tests/qapi-schema/alternate-conflict-string.json @@ -1,6 +1,4 @@ -# we reject alternates with multiple string-like branches -{ 'enum': 'Enum', - 'data': [ 'hello', 'world' ] } +# alternate branches of 'str' type conflict with all scalar types { 'alternate': 'Alt', 'data': { 'one': 'str', - 'two': 'Enum' } } + 'two': 'int' } } diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 303f2b23cd..17649c6398 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -101,12 +101,14 @@ # for testing use of 'number' within alternates { 'alternate': 'AltEnumBool', 'data': { 'e': 'EnumOne', 'b': 'bool' } } { 'alternate': 'AltEnumNum', 'data': { 'e': 'EnumOne', 'n': 'number' } } -{ 'alternate': 'AltNumStr', 'data': { 'n': 'number', 's': 'str' } } { 'alternate': 'AltNumEnum', 'data': { 'n': 'number', 'e': 'EnumOne' } } { 'alternate': 'AltEnumInt', 'data': { 'e': 'EnumOne', 'i': 'int' } } { 'alternate': 'AltIntNum', 'data': { 'i': 'int', 'n': 'number' } } { 'alternate': 'AltNumInt', 'data': { 'n': 'number', 'i': 'int' } } +# for testing use of 'str' within alternates +{ 'alternate': 'AltStrObj', 'data': { 's': 'str', 'o': 'TestStruct' } } + # for testing native lists { 'union': 'UserDefNativeListUnion', 'data': { 'integer': ['int'], diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 3081091818..9f68610dc2 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -22,10 +22,10 @@ alternate AltNumInt tag type case n: number case i: int -alternate AltNumStr +alternate AltStrObj tag type - case n: number case s: str + case o: TestStruct event EVENT_A None boxed=False event EVENT_B None -- cgit v1.2.3