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).