ctf-reader: Strip qualification from a qualified array type
authorGuillermo E. Martinez <guillermo.e.martinez@oracle.com>
Thu, 17 Nov 2022 03:43:03 +0000 (21:43 -0600)
committerDodji Seketeli <dodji@redhat.com>
Wed, 30 Nov 2022 09:22:45 +0000 (10:22 +0100)
Sometimes, GCC emits some redundant const qualifiers around arrays.

For instance, consider this function:

    $ cat -n test.c
 1 const char a[32];
 2
 3 char
 4 foo()
 5 {
 6   return a[0];
 7 }
    $

Notice how at line 1, the type of the variable 'a' is "array of const
char".

Let's compile the function and emit CTF debug info:

    $ gcc -gctf -c test.c
    $

Let's see what IR libabigail emits from the CTF information:

    $ abidw --ctf --annotate test.o  | cat -n
 1 <abi-corpus version='2.1' path='test.o' architecture='elf-amd-x86_64'>
 2   <elf-function-symbols>
 3     <!-- foo -->
 4     <elf-symbol name='foo' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 5   </elf-function-symbols>
 6   <elf-variable-symbols>
 7     <!-- signed char -->
 8     <elf-symbol name='a' size='32' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 9   </elf-variable-symbols>
10   <abi-instr address-size='64' language='LANG_C'>
11     <!-- char -->
12     <type-decl name='char' size-in-bits='8' alignment-in-bits='8' id='type-id-1'/>
13     <!-- const char[32] -->
14     <array-type-def dimensions='1' type-id='type-id-2' size-in-bits='256' alignment-in-bits='8' id='type-id-3'>
15       <!-- <anonymous range>[32] -->
16       <subrange length='32' type-id='type-id-4' id='type-id-5'/>
17     </array-type-def>
18     <!-- unsigned long int -->
19     <type-decl name='unsigned long int' size-in-bits='64' alignment-in-bits='64' id='type-id-4'/>
20     <!-- const char -->
21     <qualified-type-def type-id='type-id-1' const='yes' id='type-id-2'/>
22     <!-- const char[32] const -->
23     <qualified-type-def type-id='type-id-3' const='yes' id='type-id-6'/>
24     <!-- const char[32] const a -->
25     <var-decl name='a' type-id='type-id-6' mangled-name='a' visibility='default' elf-symbol-id='a'/>
26     <!-- char foo() -->
27     <function-decl name='foo' visibility='default' binding='global' size-in-bits='64' alignment-in-bits='8' elf-symbol-id='foo'>
28       <!-- char -->
29       <return type-id='type-id-1'/>
30     </function-decl>
31   </abi-instr>
32 </abi-corpus>
    $

Notice how at line 25, the variable 'a' is described as having the
type which ID is 'type-id-6' defined at line 23.  It's a "const array
of const char".

GCC has thus added a redundant "const" qualifier to the array.

The C language specification in paragraph [6.7.3]/8 says:

    If the specification of an array type includes any type
    qualifiers, the element type is so- qualified, not the array type.

This means that a "const array of char" is the same as an "array of
const char".  So a "const array of const char" is the same an "array
of const char".

This patch performs that removal of redundant qualifier.

* src/abg-ctf-reader.cc (maybe_strip_qualification): New
definition.
(process_ctf_qualified_type): Strip redundant qualifiers.
* tests/data/test-read-ctf/test-const-array.abi: New test.
* tests/data/test-read-ctf/test-const-array.c: Likewise.
* tests/data/test-read-ctf/test-const-array.o: Likewise.
* tests/Makefile.am: Add the new test material to source
distribution.

Signed-off-by: Guillermo E. Martinez <guillermo.e.martinez@oracle.com>
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
src/abg-ctf-reader.cc
tests/data/Makefile.am
tests/data/test-read-ctf/test-const-array.abi [new file with mode: 0644]
tests/data/test-read-ctf/test-const-array.c [new file with mode: 0644]
tests/data/test-read-ctf/test-const-array.o [new file with mode: 0644]
tests/test-read-ctf.cc

index d000556b88ebca9f2ec4e02b9740c1b67425e7e5..c48e61a46af123e47fd9f1fe7e25db8a44fcc551 100644 (file)
@@ -1251,6 +1251,63 @@ process_ctf_array_type(reader *rdr,
   return result;
 }
 
+/// Strip qualification from a qualified type, when it makes sense.
+///
+/// The C language specification says in [6.7.3]/8:
+///
+///     [If the specification of an array type includes any type
+///      qualifiers, the element type is so- qualified, not the
+///      array type.]
+///
+/// In more mundane words, a const array of int is the same as an
+/// array of const int.
+///
+/// This function thus removes the qualifiers of the array and applies
+/// them to the array element.  The function then pretends that the
+/// array itself it not qualified.
+///
+/// It might contain code to strip other cases like this in the
+/// future.
+///
+/// @param t the type to strip const qualification from.
+///
+/// @return the stripped type or just return @p t.
+static decl_base_sptr
+maybe_strip_qualification(const qualified_type_def_sptr t)
+{
+  if (!t)
+    return t;
+
+  decl_base_sptr result = t;
+  type_base_sptr u = t->get_underlying_type();
+
+  if (is_array_type(u))
+    {
+      // Let's apply the qualifiers of the array to the array element
+      // and pretend that the array itself is not qualified, as per
+      // section [6.7.3]/8 of the C specification.
+
+      array_type_def_sptr array = is_array_type(u);
+      ABG_ASSERT(array);
+      // We should not be editing types that are already canonicalized.
+      ABG_ASSERT(!array->get_canonical_type());
+      type_base_sptr element_type = array->get_element_type();
+
+      if (qualified_type_def_sptr qualified = is_qualified_type(element_type))
+        {
+          qualified_type_def::CV quals = qualified->get_cv_quals();
+          quals |= t->get_cv_quals();
+         // So we apply the qualifiers of the array to the array
+         // element.
+          qualified->set_cv_quals(quals);
+         // Let's pretend that the array is no more qualified.
+          result = is_decl(u);
+        }
+    }
+
+  return result;
+}
+
 /// Build and return a qualified type libabigail IR.
 ///
 /// @param rdr the read context.
@@ -1293,8 +1350,15 @@ process_ctf_qualified_type(reader *rdr,
   result.reset(new qualified_type_def(utype, qualifiers, location()));
   if (result)
     {
-      decl_base_sptr qualified_type_decl = get_type_declaration(result);
-      add_decl_to_scope(qualified_type_decl, tunit->get_global_scope());
+      // Strip some potentially redundant type qualifiers from
+      // the qualified type we just built.
+      decl_base_sptr d = maybe_strip_qualification(is_qualified_type(result));
+      if (!d)
+        d = get_type_declaration(result);
+      ABG_ASSERT(d);
+
+      add_decl_to_scope(d, tunit->get_global_scope());
+      result = is_type(d);
       rdr->add_type(ctf_dictionary, ctf_type, result);
     }
 
index b89a4dd8bf3148e0eb3d2620846efd29aa0c8252..8d4a2b8f71821e6ce034d38995df1ce5030b671a 100644 (file)
@@ -710,6 +710,9 @@ test-read-ctf/test-bitfield.o               \
 test-read-ctf/test-bitfield-enum.abi   \
 test-read-ctf/test-bitfield-enum.c     \
 test-read-ctf/test-bitfield-enum.o     \
+test-read-ctf/test-const-array.abi     \
+test-read-ctf/test-const-array.c       \
+test-read-ctf/test-const-array.o       \
 \
 test-annotate/test0.abi                        \
 test-annotate/test1.abi                        \
diff --git a/tests/data/test-read-ctf/test-const-array.abi b/tests/data/test-read-ctf/test-const-array.abi
new file mode 100644 (file)
index 0000000..bd60b09
--- /dev/null
@@ -0,0 +1,14 @@
+<abi-corpus version='2.1' path='data/test-read-ctf/test-const-array.o'>
+  <elf-variable-symbols>
+    <elf-symbol name='a' size='32' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-variable-symbols>
+  <abi-instr address-size='64' language='LANG_C'>
+    <type-decl name='char' size-in-bits='8' id='type-id-1'/>
+    <array-type-def dimensions='1' type-id='type-id-2' size-in-bits='256' id='type-id-3'>
+      <subrange length='32' type-id='type-id-4' id='type-id-5'/>
+    </array-type-def>
+    <type-decl name='unsigned long int' size-in-bits='64' id='type-id-4'/>
+    <qualified-type-def type-id='type-id-1' const='yes' id='type-id-2'/>
+    <var-decl name='a' type-id='type-id-3' mangled-name='a' visibility='default' elf-symbol-id='a'/>
+  </abi-instr>
+</abi-corpus>
diff --git a/tests/data/test-read-ctf/test-const-array.c b/tests/data/test-read-ctf/test-const-array.c
new file mode 100644 (file)
index 0000000..4ffecf8
--- /dev/null
@@ -0,0 +1,2 @@
+/* gcc -gctf -c test-const-array.c -o test-const-array.o */
+const char a[32];
diff --git a/tests/data/test-read-ctf/test-const-array.o b/tests/data/test-read-ctf/test-const-array.o
new file mode 100644 (file)
index 0000000..b50e2fe
Binary files /dev/null and b/tests/data/test-read-ctf/test-const-array.o differ
index a0b399fcd337936c0ab3d3c7511b81f555b693fe..f2d529e27b6424bf3be547f6cbb77873815c3dbd 100644 (file)
@@ -362,7 +362,16 @@ static InOutSpec in_out_specs[] =
     "data/test-read-ctf/test-bitfield-enum.abi",
     "output/test-read-ctf/test-bitfield-enum.abi",
     "--ctf",
-   },
+  },
+  {
+    "data/test-read-ctf/test-const-array.o",
+    "",
+    "",
+    SEQUENCE_TYPE_ID_STYLE,
+    "data/test-read-ctf/test-const-array.abi",
+    "output/test-read-ctf/test-const-array.abi",
+    "--ctf",
+  },
   // This should be the last entry.
   {NULL, NULL, NULL, SEQUENCE_TYPE_ID_STYLE, NULL, NULL, NULL}
 };