Integer built-in types are currently canonicalized to "int" in
query-qmp-schema. Consequently, clients cannot distinguish types such
as int8, uint64, and size, even though they accept different ranges.

Preserve the individual integer types during schema analysis and add an
optional "integer" member to SchemaInfoBuiltin. It reports whether the
type is signed and its width in bits. This also preserves the actual
element type for arrays of integers.

Teach the introspection generator to emit integer values and update the
documentation to describe the extended schema information.

For example for introspection, for all kind of int, before:
{"name": "int", "json-type": "int", "meta-type": "builtin"}

After, there are various int types such as:
{"name": "int64", "integer": {"bits": 64, "signed": true}, "json-type":
"int", "meta-type": "builtin"}

Signed-off-by: Marc-André Lureau <[email protected]>
---
 docs/devel/qapi-code-gen.rst    | 24 ++++++++++++++----------
 qapi/introspect.json            | 20 +++++++++++++++++++-
 scripts/qapi/introspect.py      | 29 ++++++++++++++++++++++++++---
 scripts/qapi/schema_analysis.py | 16 ----------------
 4 files changed, 59 insertions(+), 30 deletions(-)

diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst
index d176238fc2ef..a158f017d9a7 100644
--- a/docs/devel/qapi-code-gen.rst
+++ b/docs/devel/qapi-code-gen.rst
@@ -1400,14 +1400,19 @@ Example: the SchemaInfo for str ::
 
     { "name": "str", "meta-type": "builtin", "json-type": "string" }
 
-The QAPI schema supports a number of integer types that only differ in
-how they map to C.  They are identical as far as SchemaInfo is
-concerned.  Therefore, they get all mapped to a single type "int" in
-SchemaInfo.
+The QAPI schema supports a number of integer types with different widths
+and signedness.  Each has its own SchemaInfo with "json-type" "int" and
+variant member "integer" specifying its "signed" flag and width in
+"bits".
+
+Example: the SchemaInfo for uint8 ::
+
+    { "name": "uint8", "meta-type": "builtin", "json-type": "int",
+      "integer": { "signed": false, "bits": 8 } }
 
 As explained above, type names are not part of the wire ABI.  Not even
-the names of built-in types.  Clients should examine member
-"json-type" instead of hard-coding names of built-in types.
+the names of built-in types.  Clients should examine members "json-type"
+and "integer" instead of hard-coding names of built-in types.
 
 
 Compatibility considerations
@@ -2130,10 +2135,9 @@ Each ``QAPITypeInfo`` struct has the following fields:
 ``masked_name``
     For user-defined types, the masked name used in
     ``query-qmp-schema`` output. For array types, the corresponding
-    bracketed introspection name. Built-in types use their QAPI name;
-    note that introspection canonicalizes the integer built-in types
-    to ``int``. Internal types not present in introspection use
-    ``NULL``.
+    bracketed introspection name. Built-in types, including the distinct
+    integer types, use their QAPI name. Internal types not present in
+    introspection use ``NULL``.
 
 ``lookup``
     For enum types, a pointer to the corresponding ``QEnumLookup``
diff --git a/qapi/introspect.json b/qapi/introspect.json
index c8432c8ed8ec..8b5848862c4e 100644
--- a/qapi/introspect.json
+++ b/qapi/introspect.json
@@ -117,10 +117,28 @@
 #
 # @json-type: the JSON type used for this type on the wire.
 #
+# @integer: the integer type's representation.  Present exactly when
+#     @json-type is 'int'.  (since 11.2)
+#
 # Since: 2.5
 ##
 { 'struct': 'SchemaInfoBuiltin',
-  'data': { 'json-type': 'JSONType' } }
+  'data': { 'json-type': 'JSONType',
+            '*integer': 'SchemaInfoBuiltinInteger' } }
+
+##
+# @SchemaInfoBuiltinInteger:
+#
+# Representation of an integer built-in type.
+#
+# @signed: whether the integer is signed.
+#
+# @bits: the integer's width in bits.
+#
+# Since: 11.2
+##
+{ 'struct': 'SchemaInfoBuiltinInteger',
+  'data': { 'signed': 'bool', 'bits': 'int' } }
 
 ##
 # @JSONType:
diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py
index ce37ce787567..e3749d38ced5 100644
--- a/scripts/qapi/introspect.py
+++ b/scripts/qapi/introspect.py
@@ -49,7 +49,7 @@
 # A complexity over JSON is that our values may or may not be annotated.
 #
 # Un-annotated values may be:
-#     Scalar: str, bool, None.
+#     Scalar: str, bool, int, None.
 #     Non-scalar: List, Dict
 # _value = Union[str, bool, None, Dict[str, JSONValue], List[JSONValue]]
 #
@@ -59,11 +59,25 @@
 # Sadly, mypy does not support recursive types; so the _Stub alias is used to
 # mark the imprecision in the type model where we'd otherwise use JSONValue.
 _Stub = Any  # pylint: disable=invalid-name
-_Scalar = Union[str, bool, None]
+_Scalar = Union[str, bool, int, None]
 _NonScalar = Union[Dict[str, _Stub], List[_Stub]]
 _Value = Union[_Scalar, _NonScalar]
 JSONValue = Union[_Value, 'Annotated[_Value]']
 
+
+_INTEGER_TYPE_INFO = {
+    'int': (True, 64),
+    'int8': (True, 8),
+    'int16': (True, 16),
+    'int32': (True, 32),
+    'int64': (True, 64),
+    'uint8': (False, 8),
+    'uint16': (False, 16),
+    'uint32': (False, 32),
+    'uint64': (False, 64),
+    'size': (False, 64),
+}
+
 # These types are based on structures defined in QEMU's schema, so we
 # lack precise types for them here. Python 3.6 does not offer
 # TypedDict constructs, so they are broadly typed here as simple
@@ -135,6 +149,8 @@ def indent(level: int) -> str:
         ret += f"QLIT_QSTR({to_c_string(obj)})"
     elif isinstance(obj, bool):
         ret += f"QLIT_QBOOL({str(obj).lower()})"
+    elif isinstance(obj, int):
+        ret += f"QLIT_QNUM({obj})"
 
     # Non-scalars:
     elif isinstance(obj, list):
@@ -271,7 +287,14 @@ def _gen_variant(self, variant: QAPISchemaVariant
 
     def visit_builtin_type(self, name: str, info: Optional[QAPISourceInfo],
                            json_type: str) -> None:
-        self._gen_tree(name, 'builtin', {'json-type': json_type})
+        obj: Dict[str, object] = {'json-type': json_type}
+        if json_type == 'int':
+            signed, bits = _INTEGER_TYPE_INFO[name]
+            obj['integer'] = {
+                'signed': signed,
+                'bits': bits,
+            }
+        self._gen_tree(name, 'builtin', obj)
 
     def visit_enum_type(self, name: str, info: Optional[QAPISourceInfo],
                         ifcond: QAPISchemaIfCond,
diff --git a/scripts/qapi/schema_analysis.py b/scripts/qapi/schema_analysis.py
index 1d12306f61e2..1aad2dc80380 100644
--- a/scripts/qapi/schema_analysis.py
+++ b/scripts/qapi/schema_analysis.py
@@ -123,27 +123,12 @@ def visit_alternate_type(
 
     def _register_type(self, typ: QAPISchemaType) -> None:
         """Record a type for introspection (idempotent)."""
-        typ = self._canonicalize_type(typ)
         if typ not in self._types_set:
             self._types.append(typ)
             self._types_set.add(typ)
             if isinstance(typ, QAPISchemaArrayType):
                 self._register_type(typ.element_type)
 
-    def _canonicalize_type(self, typ: QAPISchemaType) -> QAPISchemaType:
-        """Canonicalize integer types to plain int."""
-        assert self._schema is not None
-        if typ.json_type() == 'int':
-            type_int = self._schema.lookup_type('int')
-            assert type_int
-            return type_int
-        if (isinstance(typ, QAPISchemaArrayType) and
-                typ.element_type.json_type() == 'int'):
-            type_intlist = self._schema.lookup_type('intList')
-            assert type_intlist
-            return type_intlist
-        return typ
-
     def masked_name(self, name: str) -> str:
         """Return the masked name for a non-builtin, non-array type."""
         assert name in self._name_map, \
@@ -152,7 +137,6 @@ def masked_name(self, name: str) -> str:
 
     def introspection_name(self, typ: QAPISchemaType) -> str:
         """Return the introspection name for a type."""
-        typ = self._canonicalize_type(typ)
         if isinstance(typ, QAPISchemaBuiltinType):
             return typ.name
         if isinstance(typ, QAPISchemaArrayType):

-- 
2.55.0.543.g5ebe2ebe4ea8


Reply via email to