https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98597

--- Comment #8 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
So, before starting to fix all the bugs in the print_mem_ref routine, I think
we should add something like:
--- gcc/c-family/c-pretty-print.c.jj    2021-01-13 08:02:09.425498954 +0100
+++ gcc/c-family/c-pretty-print.c       2021-01-13 14:31:34.048591447 +0100
@@ -1809,6 +1809,103 @@ pp_c_call_argument_list (c_pretty_printe
   pp_c_right_paren (pp);
 }

+/* Try to fold *(type *)&op into op.fld.fld2[1] if possible.
+   Only used for printing expressions.  Should punt if ambiguous
+   (e.g. in unions).  */
+
+static tree
+c_fold_indirect_ref_for_warn (location_t loc, tree type, tree op,
+                             offset_int off)
+{
+  tree optype = TREE_TYPE (op);
+  if (off == 0)
+    {
+      if (lang_hooks.types_compatible_p (optype, type))
+       return op;
+      /* Also handle conversion to an empty base class, which
+        is represented with a NOP_EXPR.  */
+      /* *(foo *)&complexfoo => __real__ complexfoo */
+      else if (TREE_CODE (optype) == COMPLEX_TYPE
+              && lang_hooks.types_compatible_p (type, TREE_TYPE (optype)))
+       return build1_loc (loc, REALPART_EXPR, type, op);
+    }
+  /* ((foo*)&complexfoo)[1] => __imag__ complexfoo */
+  else if (TREE_CODE (optype) == COMPLEX_TYPE
+          && lang_hooks.types_compatible_p (type, TREE_TYPE (optype))
+          && tree_to_uhwi (TYPE_SIZE_UNIT (type)) == off)
+    return build1_loc (loc, IMAGPART_EXPR, type, op);
+  /* ((foo *)&fooarray)[x] => fooarray[x] */
+  if (TREE_CODE (optype) == ARRAY_TYPE
+      && TYPE_SIZE_UNIT (TREE_TYPE (optype))
+      && TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (optype))) == INTEGER_CST
+      && !integer_zerop (TYPE_SIZE_UNIT (TREE_TYPE (optype))))
+    {
+      tree type_domain = TYPE_DOMAIN (optype);
+      tree min_val = size_zero_node;
+      if (type_domain && TYPE_MIN_VALUE (type_domain))
+       min_val = TYPE_MIN_VALUE (type_domain);
+      offset_int el_sz = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (optype)));
+      offset_int idx = off / el_sz;
+      offset_int rem = off % el_sz;
+      if (TREE_CODE (min_val) == INTEGER_CST)
+       {
+         tree index
+           = wide_int_to_tree (sizetype, idx + wi::to_offset (min_val));
+         op = build4_loc (loc, ARRAY_REF, TREE_TYPE (optype), op, index,
+                          NULL_TREE, NULL_TREE);
+         return c_fold_indirect_ref_for_warn (loc, type, op, rem);
+       }
+    }
+  /* ((foo *)&struct_with_foo_field)[x] => COMPONENT_REF */
+  else if (TREE_CODE (optype) == RECORD_TYPE)
+    {
+      for (tree field = TYPE_FIELDS (optype);
+          field; field = DECL_CHAIN (field))
+       if (TREE_CODE (field) == FIELD_DECL
+           && TREE_TYPE (field) != error_mark_node
+           && TYPE_SIZE_UNIT (TREE_TYPE (field))
+           && TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (field))) == INTEGER_CST)
+         {
+           tree pos = byte_position (field);
+           if (TREE_CODE (pos) != INTEGER_CST)
+             continue;
+           offset_int upos = wi::to_offset (pos);
+           offset_int el_sz
+             = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (field)));
+           if (upos <= off && off < upos + el_sz)
+             {
+               tree cop = build3_loc (loc, COMPONENT_REF, TREE_TYPE (field),
+                                      op, field, NULL_TREE);
+               if (tree ret = c_fold_indirect_ref_for_warn (loc, type, cop,
+                                                            off - upos))
+                 return ret;
+             }
+         }
+    }
+  /* Similarly for unions, but in this case try to be very conservative,
+     only match if some field has type compatible with type and it is the
+     only such field.  */
+  else if (TREE_CODE (optype) == UNION_TYPE)
+    {
+      tree fld = NULL_TREE;
+      for (tree field = TYPE_FIELDS (optype);
+          field; field = DECL_CHAIN (field))
+       if (TREE_CODE (field) == FIELD_DECL
+           && TREE_TYPE (field) != error_mark_node
+           && lang_hooks.types_compatible_p (TREE_TYPE (field), type))
+         {
+           if (fld)
+             return NULL_TREE;
+           else
+             fld = field;
+         }
+      if (fld)
+       return build3_loc (loc, COMPONENT_REF, TREE_TYPE (fld), op, fld,
+                          NULL_TREE);
+    }
+
+  return NULL_TREE;
+}
 /* Print the MEM_REF expression REF, including its type and offset.
    Apply casts as necessary if the type of the access is different
    from the type of the accessed object.  Produce compact output
@@ -1836,6 +1933,14 @@ print_mem_ref (c_pretty_printer *pp, tre
   const bool addr = TREE_CODE (arg) == ADDR_EXPR;
   if (addr)
     {
+      tree op
+       = c_fold_indirect_ref_for_warn (EXPR_LOCATION (e), TREE_TYPE (e),
+                                       TREE_OPERAND (arg, 0), byte_off);
+      if (op)
+       {
+         pp->expression (op);
+         return;
+       }
       arg = TREE_OPERAND (arg, 0);
       if (byte_off == 0)
        {
which in itself will fix 3 out of the 5 cases in the above testcase to the IMHO
optimum form,
t.u.b, t.v and s[1] are valid C/C++ and accurate descriptions on what is
uninitialized.
Now, a question is if this c_fold_indirect_ref_for_warn needs to return
non-NULL only on type match (for unions I'd prefer to), or also whenever the
type just fully fits into the range.  In the latter case I guess it should have
the byte offset passed by reference and update it on non-NULL return to be
relative to what it returned.  E.g. for the t.u.d which is read through
incompatible type,
it could still return t.u.d and set byte_off to 0, but e.g. for *(int *)(p +
__builtin_offsetof (struct T, u) + 1) it would need to stop at t.u and byte_off
1.
In this case pp->expression (op) should be called only if byte_off == 0 &&
lang_hooks.types.types_compatible (TREE_TYPE (e), TREE_TYPE (op)) as the fast
path, but code later on could print e.g. *(unsigned *)&t.u.d or *(int *)((char
*)&t.u + 1).

Reply via email to