PR c++/95768 - pretty-printer ICE on -Wuninitialized with allocated storage

gcc/c-family/ChangeLog:

	PR c++/95768
	* c-pretty-print.c (c_pretty_printer::primary_expression): For
	SSA_NAMEs print VLA names and GIMPLE defining statements.
	(print_mem_ref): New function.
	(c_pretty_printer::unary_expression): Call it.

gcc/cp/ChangeLog:

	PR c++/95768
	* error.c (dump_expr): Call c_pretty_printer::unary_expression.

gcc/testsuite/ChangeLog:

	PR c++/95768
	* g++.dg/pr95768.C: New test.
	* g++.dg/warn/Wuninitialized-12.C: New test.
	* gcc.dg/uninit-38.c: New test.
This commit is contained in:
Martin Sebor 2021-01-06 13:44:27 -07:00
parent fd64f348a6
commit abb1b6058c
5 changed files with 324 additions and 50 deletions

View File

@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see
#include "system.h"
#include "coretypes.h"
#include "c-pretty-print.h"
#include "gimple-pretty-print.h"
#include "diagnostic.h"
#include "stor-layout.h"
#include "stringpool.h"
@ -1334,6 +1335,34 @@ c_pretty_printer::primary_expression (tree e)
pp_c_right_paren (this);
break;
case SSA_NAME:
if (SSA_NAME_VAR (e))
{
tree var = SSA_NAME_VAR (e);
const char *name = IDENTIFIER_POINTER (SSA_NAME_IDENTIFIER (e));
const char *dot;
if (DECL_ARTIFICIAL (var) && (dot = strchr (name, '.')))
{
/* Print the name without the . suffix (such as in VLAs).
Use pp_c_identifier so that it can be converted into
the appropriate encoding. */
size_t size = dot - name;
char *ident = XALLOCAVEC (char, size + 1);
memcpy (ident, name, size);
ident[size] = '\0';
pp_c_identifier (this, ident);
}
else
primary_expression (var);
}
else
{
/* Print only the right side of the GIMPLE assignment. */
gimple *def_stmt = SSA_NAME_DEF_STMT (e);
pp_gimple_stmt_1 (this, def_stmt, 0, TDF_RHS_ONLY);
}
break;
default:
/* FIXME: Make sure we won't get into an infinite loop. */
if (location_wrapper_p (e))
@ -1780,6 +1809,139 @@ pp_c_call_argument_list (c_pretty_printer *pp, tree t)
pp_c_right_paren (pp);
}
/* 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
designed to include both the element index as well as any
misalignment by preferring
((int*)((char*)p + 1))[2]
over
*(int*)((char*)p + 9)
The former is more verbose but makes it clearer that the access
to the third element of the array is misaligned by one byte. */
static void
print_mem_ref (c_pretty_printer *pp, tree e)
{
tree arg = TREE_OPERAND (e, 0);
/* The byte offset. Initially equal to the MEM_REF offset, then
adjusted to the remainder of the division by the byte size of
the access. */
offset_int byte_off = wi::to_offset (TREE_OPERAND (e, 1));
/* The result of dividing BYTE_OFF by the size of the access. */
offset_int elt_idx = 0;
/* True to include a cast to char* (for a nonzero final BYTE_OFF). */
bool char_cast = false;
const bool addr = TREE_CODE (arg) == ADDR_EXPR;
if (addr)
{
arg = TREE_OPERAND (arg, 0);
if (byte_off == 0)
{
pp->expression (arg);
return;
}
}
const tree access_type = TREE_TYPE (e);
tree arg_type = TREE_TYPE (TREE_TYPE (arg));
if (TREE_CODE (arg_type) == ARRAY_TYPE)
arg_type = TREE_TYPE (arg_type);
if (tree access_size = TYPE_SIZE_UNIT (access_type))
{
/* For naturally aligned accesses print the nonzero offset
in units of the accessed type, in the form of an index.
For unaligned accesses also print the residual byte offset. */
offset_int asize = wi::to_offset (access_size);
offset_int szlg2 = wi::floor_log2 (asize);
elt_idx = byte_off >> szlg2;
byte_off = byte_off - (elt_idx << szlg2);
}
/* True to include a cast to the accessed type. */
const bool access_cast = VOID_TYPE_P (arg_type)
|| !gimple_canonical_types_compatible_p (access_type, arg_type);
if (byte_off != 0)
{
/* When printing the byte offset for a pointer to a type of
a different size than char, include a cast to char* first,
before printing the cast to a pointer to the accessed type. */
tree arg_type = TREE_TYPE (TREE_TYPE (arg));
if (TREE_CODE (arg_type) == ARRAY_TYPE)
arg_type = TREE_TYPE (arg_type);
offset_int arg_size = 0;
if (tree size = TYPE_SIZE (arg_type))
arg_size = wi::to_offset (size);
if (arg_size != BITS_PER_UNIT)
char_cast = true;
}
if (elt_idx == 0)
{
if (!addr)
pp_c_star (pp);
}
else if (access_cast || char_cast)
pp_c_left_paren (pp);
if (access_cast)
{
/* Include a cast to the accessed type if it isn't compatible
with the type of the referenced object (or if the object
is typeless). */
pp_c_left_paren (pp);
pp->type_id (access_type);
pp_c_star (pp);
pp_c_right_paren (pp);
}
if (byte_off != 0)
pp_c_left_paren (pp);
if (char_cast)
{
/* Include a cast to char*. */
pp_c_left_paren (pp);
pp->type_id (char_type_node);
pp_c_star (pp);
pp_c_right_paren (pp);
}
pp->unary_expression (arg);
if (byte_off != 0)
{
pp_space (pp);
pp_plus (pp);
pp_space (pp);
tree off = wide_int_to_tree (ssizetype, byte_off);
pp->constant (off);
pp_c_right_paren (pp);
}
if (elt_idx != 0)
{
if (byte_off == 0 && char_cast)
pp_c_right_paren (pp);
pp_c_right_paren (pp);
if (addr)
{
pp_space (pp);
pp_plus (pp);
pp_space (pp);
}
else
pp_c_left_bracket (pp);
tree idx = wide_int_to_tree (ssizetype, elt_idx);
pp->constant (idx);
if (!addr)
pp_c_right_bracket (pp);
}
}
/* unary-expression:
postfix-expression
++ cast-expression
@ -1837,30 +1999,7 @@ c_pretty_printer::unary_expression (tree e)
break;
case MEM_REF:
if (TREE_CODE (TREE_OPERAND (e, 0)) == ADDR_EXPR
&& integer_zerop (TREE_OPERAND (e, 1)))
expression (TREE_OPERAND (TREE_OPERAND (e, 0), 0));
else
{
pp_c_star (this);
if (!integer_zerop (TREE_OPERAND (e, 1)))
{
pp_c_left_paren (this);
tree type = TREE_TYPE (TREE_TYPE (TREE_OPERAND (e, 0)));
if (TYPE_SIZE_UNIT (type) == NULL_TREE
|| !integer_onep (TYPE_SIZE_UNIT (type)))
pp_c_type_cast (this, ptr_type_node);
}
pp_c_cast_expression (this, TREE_OPERAND (e, 0));
if (!integer_zerop (TREE_OPERAND (e, 1)))
{
pp_plus (this);
pp_c_integer_constant (this,
fold_convert (ssizetype,
TREE_OPERAND (e, 1)));
pp_c_right_paren (this);
}
}
print_mem_ref (this, e);
break;
case TARGET_MEM_REF:

View File

@ -2417,32 +2417,8 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
break;
case MEM_REF:
if (TREE_CODE (TREE_OPERAND (t, 0)) == ADDR_EXPR
&& integer_zerop (TREE_OPERAND (t, 1)))
dump_expr (pp, TREE_OPERAND (TREE_OPERAND (t, 0), 0), flags);
else
{
pp_cxx_star (pp);
if (!integer_zerop (TREE_OPERAND (t, 1)))
{
pp_cxx_left_paren (pp);
if (!integer_onep (TYPE_SIZE_UNIT
(TREE_TYPE (TREE_TYPE (TREE_OPERAND (t, 0))))))
{
pp_cxx_left_paren (pp);
dump_type (pp, ptr_type_node, flags);
pp_cxx_right_paren (pp);
}
}
dump_expr (pp, TREE_OPERAND (t, 0), flags);
if (!integer_zerop (TREE_OPERAND (t, 1)))
{
pp_cxx_ws_string (pp, "+");
dump_expr (pp, fold_convert (ssizetype, TREE_OPERAND (t, 1)),
flags);
pp_cxx_right_paren (pp);
}
}
/* Delegate to the base "C" pretty printer. */
pp->c_pretty_printer::unary_expression (t);
break;
case TARGET_MEM_REF:

View File

@ -0,0 +1,32 @@
/* PR c++/95768 - pretty-printer ICE on -Wuninitialized with allocated storage
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
extern "C" void *malloc (__SIZE_TYPE__);
struct f
{
int i;
static int e (int);
void operator= (int) { e (i); }
};
struct m {
int i;
f length;
};
struct n {
m *o() { return (m *)this; }
};
struct p {
n *header;
p () {
header = (n *)malloc (0);
m b = *header->o(); // { dg-warning "\\\[-Wuninitialized" }
b.length = 0;
}
};
void detach2() { p(); }

View File

@ -0,0 +1,40 @@
/* Verify that -Wuninitialized warnings about accesses to objects via
pointers and offsets mention valid expressions.
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
typedef __INT16_TYPE__ int16_t;
typedef __INT32_TYPE__ int32_t;
void sink (int);
/* Verify properly aligned accesses at offsets that are multiples of
the access size. */
void test_aligned (void)
{
char *p1 = (char*)__builtin_malloc (32);
p1 += sizeof (int32_t);
int16_t *p2 = (int16_t*)p1;
sink (p2[1]); // { dg-warning "'\\(\\(int16_t\\*\\)p1\\)\\\[3]' is used uninitialized" }
int32_t *p4 = (int32_t*)p1;
sink (p4[1]); // { dg-warning "'\\(\\(int32_t\\*\\)p1\\)\\\[2]' is used uninitialized" }
}
/* Verify misaligned accesses at offsets that aren't multiples of
the access size. */
void test_misaligned (void)
{
char *p1 = (char*)__builtin_malloc (32);
p1 += 1;
int16_t *p2 = (int16_t*)p1;
sink (p2[1]); // { dg-warning "'\\(\\(int16_t\\*\\)\\(p1 \\+ 1\\)\\)\\\[1]' is used uninitialized" }
int32_t *p4 = (int32_t*)p1;
sink (p4[1]); // { dg-warning "'\\(\\(int32_t\\*\\)\\(p1 \\+ 1\\)\\)\\\[1]' is used uninitialized" }
}

View File

@ -0,0 +1,87 @@
/* Verify that dereferencing uninitialized allocated objects and VLAs
correctly reflects offsets into the objects.
The test's main purpose is to exercise the formatting of MEM_REFs.
If -Wuninitialized gets smarter and detects uninitialized accesses
before they're turned into MEM_REFs the test will likely need to
be adjusted. Ditto if -Wuninitialized output changes for some
other reason.
{ dg-do compile { target { { lp64 || ilp32 } || llp64 } } }
{ dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
#define CONCAT(x, y) x ## y
#define CAT(x, y) CONCAT(x, y)
#define UNIQ(name) CAT (name, __LINE__)
typedef __SIZE_TYPE__ size_t;
extern void* malloc (size_t);
void sink (void*, ...);
#undef T
#define T(Type, idx, off) \
__attribute__ ((noipa)) \
void UNIQ (test_)(int n) \
{ \
void *p = malloc (n); \
Type *q = (Type*)((char*)p + off); \
sink (p, q[idx]); \
} \
typedef void dummy_type
T (int, 0, 0); // { dg-warning "'\\*\\(int\\*\\)p' is used uninitialized" }
T (int, 0, 1); // { dg-warning "'\\*\\(int\\*\\)\\(\\(char\\*\\)p \\+ 1\\)'" }
T (int, 0, 2); // { dg-warning "'\\*\\(int\\*\\)\\(\\(char\\*\\)p \\+ 2\\)'" }
T (int, 0, 3); // { dg-warning "'\\*\\(int\\*\\)\\(\\(char\\*\\)p \\+ 3\\)'" }
T (int, 0, 4); // { dg-warning "'\\(\\(int\\*\\)p\\)\\\[1]'" }
T (int, 0, 5); // { dg-warning "'\\(\\(int\\*\\)\\(\\(char\\*\\)p \\+ 1\\)\\)\\\[1]'" }
T (int, 0, 6); // { dg-warning "'\\(\\(int\\*\\)\\(\\(char\\*\\)p \\+ 2\\)\\)\\\[1]'" }
T (int, 0, 7); // { dg-warning "'\\(\\(int\\*\\)\\(\\(char\\*\\)p \\+ 3\\)\\)\\\[1]'" }
T (int, 0, 8); // { dg-warning "'\\(\\(int\\*\\)p\\)\\\[2]'" }
T (int, 0, 9); // { dg-warning "'\\(\\(int\\*\\)\\(\\(char\\*\\)p \\+ 1\\)\\)\\\[2]'" }
T (int, 1, 0); // { dg-warning "'\\(\\(int\\*\\)p\\)\\\[1]' is used uninitialized" }
T (int, 1, 1); // { dg-warning "'\\(\\(int\\*\\)\\(\\(char\\*\\)p \\+ 1\\)\\)\\\[1]'" }
T (int, 1, 2); // { dg-warning "'\\(\\(int\\*\\)\\(\\(char\\*\\)p \\+ 2\\)\\)\\\[1]'" }
T (int, 1, 3); // { dg-warning "'\\(\\(int\\*\\)\\(\\(char\\*\\)p \\+ 3\\)\\)\\\[1]'" }
T (int, 1, 4); // { dg-warning "'\\(\\(int\\*\\)p\\)\\\[2]'" }
T (int, 1, 5); // { dg-warning "'\\(\\(int\\*\\)\\(\\(char\\*\\)p \\+ 1\\)\\)\\\[2]'" }
T (int, 1, 6); // { dg-warning "'\\(\\(int\\*\\)\\(\\(char\\*\\)p \\+ 2\\)\\)\\\[2]'" }
T (int, 1, 7); // { dg-warning "'\\(\\(int\\*\\)\\(\\(char\\*\\)p \\+ 3\\)\\)\\\[2]'" }
T (int, 1, 8); // { dg-warning "'\\(\\(int\\*\\)p\\)\\\[3]'" }
T (int, 1, 9); // { dg-warning "'\\(\\(int\\*\\)\\(\\(char\\*\\)p \\+ 1\\)\\)\\\[3]'" }
#undef T
#define T(Type, idx, off) \
__attribute__ ((noipa)) \
void UNIQ (test_)(int n) \
{ \
char a[n], *p = a; \
Type *q = (Type*)((char*)p + off); \
sink (p, q[idx]); \
} \
typedef void dummy_type
T (int, 0, 0); // { dg-warning "'\\*\\(int\\*\\)a' is used uninitialized" }
T (int, 0, 1); // { dg-warning "'\\*\\(int\\*\\)\\(a \\+ 1\\)'" }
T (int, 0, 2); // { dg-warning "'\\*\\(int\\*\\)\\(a \\+ 2\\)'" }
T (int, 0, 3); // { dg-warning "'\\*\\(int\\*\\)\\(a \\+ 3\\)'" }
T (int, 0, 4); // { dg-warning "'\\(\\(int\\*\\)a\\)\\\[1]'" }
T (int, 0, 5); // { dg-warning "'\\(\\(int\\*\\)\\(a \\+ 1\\)\\)\\\[1]'" }
T (int, 0, 6); // { dg-warning "'\\(\\(int\\*\\)\\(a \\+ 2\\)\\)\\\[1]'" }
T (int, 0, 7); // { dg-warning "'\\(\\(int\\*\\)\\(a \\+ 3\\)\\)\\\[1]'" }
T (int, 0, 8); // { dg-warning "'\\(\\(int\\*\\)a\\)\\\[2]'" }
T (int, 0, 9); // { dg-warning "'\\(\\(int\\*\\)\\(a \\+ 1\\)\\)\\\[2]'" }
T (int, 1, 0); // { dg-warning "'\\(\\(int\\*\\)a\\)\\\[1]' is used uninitialized" }
T (int, 1, 1); // { dg-warning "'\\(\\(int\\*\\)\\(a \\+ 1\\)\\)\\\[1]'" }
T (int, 1, 2); // { dg-warning "'\\(\\(int\\*\\)\\(a \\+ 2\\)\\)\\\[1]'" }
T (int, 1, 3); // { dg-warning "'\\(\\(int\\*\\)\\(a \\+ 3\\)\\)\\\[1]'" }
T (int, 1, 4); // { dg-warning "'\\(\\(int\\*\\)a\\)\\\[2]'" }
T (int, 1, 5); // { dg-warning "'\\(\\(int\\*\\)\\(a \\+ 1\\)\\)\\\[2]'" }
T (int, 1, 6); // { dg-warning "'\\(\\(int\\*\\)\\(a \\+ 2\\)\\)\\\[2]'" }
T (int, 1, 7); // { dg-warning "'\\(\\(int\\*\\)\\(a \\+ 3\\)\\)\\\[2]'" }
T (int, 1, 8); // { dg-warning "'\\(\\(int\\*\\)a\\)\\\[3]'" }
T (int, 1, 9); // { dg-warning "'\\(\\(int\\*\\)\\(a \\+ 1\\)\\)\\\[3]'" }