PR c++/69662 - -Wplacement-new on allocated one element array members

gcc/testsuite/ChangeLog:
	PR c++/69662
	* g++.dg/warn/Wplacement-new-size-1.C: New test.
	* g++.dg/warn/Wplacement-new-size-2.C: New test.

gcc/cp/ChangeLog:
	PR c++/69662
	* init.c (find_field_init): New function.
	(warn_placement_new_too_small): Call it.  Handle one-element arrays
        at ends of structures special.

gcc/c-family/ChangeLog:
	PR c++/69662
	* c.opt (Warning options): Update -Wplacement-new to take
        an optional argument.

gcc/ChangeLog:
	PR c++/69662
	* doc/invoke.texi: Update -Wplacement-new to take an optional
        argument.

From-SVN: r233190
This commit is contained in:
Martin Sebor 2016-02-05 22:27:37 +00:00 committed by Martin Sebor
parent be2083eab7
commit 46cb933227
9 changed files with 478 additions and 15 deletions

View File

@ -1,3 +1,9 @@
2016-02-05 Martin Sebor <msebor@redhat.com>
PR c++/69662
* doc/invoke.texi: Update -Wplacement-new to take an optional
argument.
2016-02-06 Richard Henderson <rth@redhat.com>
PR c/69643

View File

@ -1,3 +1,9 @@
2016-02-05 Martin Sebor <msebor@redhat.com>
PR c++/69662
* c.opt (Warning options): Update -Wplacement-new to take
an optional argument.
2016-02-01 Jakub Jelinek <jakub@redhat.com>
PR preprocessor/69543

View File

@ -777,7 +777,11 @@ ObjC ObjC++ Var(warn_protocol) Init(1) Warning
Warn if inherited methods are unimplemented.
Wplacement-new
C++ Var(warn_placement_new) Init(1) Warning
C++ Warning Alias(Wplacement-new=, 1, 0)
Warn for placement new expressions with undefined behavior.
Wplacement-new=
C++ Joined RejectNegative UInteger Var(warn_placement_new) Init(-1) Warning
Warn for placement new expressions with undefined behavior.
Wredundant-decls

View File

@ -1,3 +1,10 @@
2016-02-05 Martin Sebor <msebor@redhat.com>
PR c++/69662
* init.c (find_field_init): New function.
(warn_placement_new_too_small): Call it. Handle one-element arrays
at ends of structures special.
2016-02-05 Jason Merrill <jason@redhat.com>
PR c++/68948

View File

@ -2285,6 +2285,33 @@ throw_bad_array_new_length (void)
return build_cxx_call (fn, 0, NULL, tf_warning_or_error);
}
/* Attempt to find the initializer for field T in the initializer INIT,
when non-null. Returns the initializer when successful and NULL
otherwise. */
static tree
find_field_init (tree t, tree init)
{
if (!init)
return NULL_TREE;
unsigned HOST_WIDE_INT idx;
tree field, elt;
/* Iterate over all top-level initializer elements. */
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), idx, field, elt)
{
/* If the member T is found, return it. */
if (field == t)
return elt;
/* Otherwise continue and/or recurse into nested initializers. */
if (TREE_CODE (elt) == CONSTRUCTOR
&& (init = find_field_init (t, elt)))
return init;
}
return NULL_TREE;
}
/* Attempt to verify that the argument, OPER, of a placement new expression
refers to an object sufficiently large for an object of TYPE or an array
of NELTS of such objects when NELTS is non-null, and issue a warning when
@ -2375,10 +2402,25 @@ warn_placement_new_too_small (tree type, tree nelts, tree size, tree oper)
oper = TREE_OPERAND (oper, 0);
}
/* Refers to the declared object that constains the subobject referenced
by OPER. When the object is initialized, makes it possible to determine
the actual size of a flexible array member used as the buffer passed
as OPER to placement new. */
tree var_decl = NULL_TREE;
/* True when operand is a COMPONENT_REF, to distinguish flexible array
members from arrays of unspecified size. */
bool compref = TREE_CODE (oper) == COMPONENT_REF;
/* Descend into a struct or union to find the member whose address
is being used as the agument. */
while (TREE_CODE (oper) == COMPONENT_REF)
oper = TREE_OPERAND (oper, 1);
{
tree op0 = oper;
while (TREE_CODE (op0 = TREE_OPERAND (op0, 0)) == COMPONENT_REF);
if (TREE_CODE (op0) == VAR_DECL)
var_decl = op0;
oper = TREE_OPERAND (oper, 1);
}
if ((addr_expr || !POINTER_TYPE_P (TREE_TYPE (oper)))
&& (TREE_CODE (oper) == VAR_DECL
@ -2387,7 +2429,7 @@ warn_placement_new_too_small (tree type, tree nelts, tree size, tree oper)
{
/* A possibly optimistic estimate of the number of bytes available
in the destination buffer. */
unsigned HOST_WIDE_INT bytes_avail;
unsigned HOST_WIDE_INT bytes_avail = 0;
/* True when the estimate above is in fact the exact size
of the destination buffer rather than an estimate. */
bool exact_size = true;
@ -2410,20 +2452,45 @@ warn_placement_new_too_small (tree type, tree nelts, tree size, tree oper)
as the optimistic estimate of the available space in it. */
bytes_avail = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (oper)));
}
else if (var_decl)
{
/* Constructing into a buffer provided by the flexible array
member of a declared object (which is permitted as a G++
extension). If the array member has been initialized,
determine its size from the initializer. Otherwise,
the array size is zero. */
bytes_avail = 0;
if (tree init = find_field_init (oper, DECL_INITIAL (var_decl)))
bytes_avail = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (init)));
}
else
{
/* Bail if neither the size of the object nor its type is known. */
return;
}
/* Avoid diagnosing flexible array members (accepted as an extension
and diagnosed with -Wpedantic).
Constructing objects that appear to overflow the C99 equivalent of
flexible array members (i.e., array members of size zero or one)
are diagnosed in C++ since their declaration cannot be diagnosed. */
if (bytes_avail == 0 && TREE_CODE (TREE_TYPE (oper)) == ARRAY_TYPE)
return;
tree_code oper_code = TREE_CODE (TREE_TYPE (oper));
if (compref && oper_code == ARRAY_TYPE)
{
/* Avoid diagnosing flexible array members (which are accepted
as an extension and diagnosed with -Wpedantic) and zero-length
arrays (also an extension).
Overflowing construction in one-element arrays is diagnosed
only at level 2. */
if (bytes_avail == 0 && !var_decl)
return;
tree nelts = array_type_nelts_top (TREE_TYPE (oper));
tree nelts_cst = maybe_constant_value (nelts);
if (TREE_CODE (nelts_cst) == INTEGER_CST
&& integer_onep (nelts_cst)
&& !var_decl
&& warn_placement_new < 2)
return;
}
/* The size of the buffer can only be adjusted down but not up. */
gcc_checking_assert (0 <= adjust);
@ -2452,7 +2519,7 @@ warn_placement_new_too_small (tree type, tree nelts, tree size, tree oper)
{
if (nelts)
if (CONSTANT_CLASS_P (nelts))
warning_at (loc, OPT_Wplacement_new,
warning_at (loc, OPT_Wplacement_new_,
exact_size ?
"placement new constructing an object of type "
"%<%T [%wu]%> and size %qwu in a region of type %qT "
@ -2464,7 +2531,7 @@ warn_placement_new_too_small (tree type, tree nelts, tree size, tree oper)
TREE_TYPE (oper),
bytes_avail);
else
warning_at (loc, OPT_Wplacement_new,
warning_at (loc, OPT_Wplacement_new_,
exact_size ?
"placement new constructing an array of objects "
"of type %qT and size %qwu in a region of type %qT "
@ -2475,7 +2542,7 @@ warn_placement_new_too_small (tree type, tree nelts, tree size, tree oper)
type, bytes_need, TREE_TYPE (oper),
bytes_avail);
else
warning_at (loc, OPT_Wplacement_new,
warning_at (loc, OPT_Wplacement_new_,
exact_size ?
"placement new constructing an object of type %qT "
"and size %qwu in a region of type %qT and size %qwi"

View File

@ -281,7 +281,8 @@ Objective-C and Objective-C++ Dialects}.
-Woverride-init-side-effects -Woverlength-strings @gol
-Wpacked -Wpacked-bitfield-compat -Wpadded @gol
-Wparentheses -Wno-pedantic-ms-format @gol
-Wplacement-new -Wpointer-arith -Wno-pointer-to-int-cast @gol
-Wplacement-new -Wplacement-new=@var{n} @gol
-Wpointer-arith -Wno-pointer-to-int-cast @gol
-Wno-pragmas -Wredundant-decls -Wno-return-local-addr @gol
-Wreturn-type -Wsequence-point -Wshadow -Wno-shadow-ivar @gol
-Wshift-overflow -Wshift-overflow=@var{n} @gol
@ -4894,6 +4895,7 @@ width specifiers @code{I32}, @code{I64}, and @code{I} used on Windows targets,
which depend on the MS runtime.
@item -Wplacement-new
@itemx -Wplacement-new=@var{n}
@opindex Wplacement-new
@opindex Wno-placement-new
Warn about placement new expressions with undefined behavior, such as
@ -4906,7 +4908,36 @@ char buf [64];
new (buf) int[64];
@end smallexample
This warning is enabled by default.
@table @gcctabopt
@item -Wplacement-new=1
This is the default warning level of @option{-Wplacement-new}. At this
level the warning is not issued for some strictly undefined constructs that
GCC allows as extensions for compatibility with legacy code. For example,
the following @code{new} expression is not diagnosed at this level even
though it has undefined behavior according to the C++ standard because
it writes past the end of the one-element array.
@smallexample
struct S @{ int n, a[1]; @};
S *s = (S *)malloc (sizeof *s + 31 * sizeof s->a[0]);
new (s->a)int [32]();
@end smallexample
@item -Wplacement-new=2
At this level, in addition to diagnosing all the same constructs as at level
1, a diagnostic is also issued for placement new expressions that construct
an object in the last member of structure whose type is an array of a single
element and whose size is less than the size of the object being constructed.
While the previous example would be diagnosed, the following construct makes
use of the flexible member array extension to avoid the warning at level 2.
@smallexample
struct S @{ int n, a[]; @};
S *s = (S *)malloc (sizeof *s + 32 * sizeof s->a[0]);
new (s->a)int [32]();
@end smallexample
@end table
@item -Wpointer-arith
@opindex Wpointer-arith
@opindex Wno-pointer-arith

View File

@ -1,3 +1,9 @@
2016-02-05 Martin Sebor <msebor@redhat.com>
PR c++/69662
* g++.dg/warn/Wplacement-new-size-1.C: New test.
* g++.dg/warn/Wplacement-new-size-2.C: New test.
2016-02-06 Richard HEnderson <rth@redhat.com>
PR c/69643

View File

@ -0,0 +1,139 @@
// PR c++/69662 - -Wplacement-new on allocated one element array members
// Exercising the more permissive -Wplacement-new=1. The difference
// between -Wplacement-new=1 is denoted by "no warning at level 1" in
// the comments below.
// { dg-do compile }
// { dg-options "-Wno-pedantic -Wplacement-new=1" }
typedef __typeof__ (sizeof 0) size_t;
void* operator new (size_t, void *p) { return p; }
void* operator new[] (size_t, void *p) { return p; }
struct Ax { char n, a []; };
struct A0 { char n, a [0]; };
struct A1 { char n, a [1]; };
struct A2 { char n, a [2]; };
typedef __INT16_TYPE__ Int16;
typedef __INT32_TYPE__ Int32;
void fAx (Ax *px, Ax &rx)
{
Ax ax;
new (ax.a) Int32; // { dg-warning "placement" }
new (px->a) Int32;
new (rx.a) Int32;
}
void fAx2 ()
{
Ax ax2 = { 1, { 2, 3 } };
new (ax2.a) Int16;
new (ax2.a) Int32; // { dg-warning "placement" }
}
void fA0 (A0 *p0, A0 &r0)
{
A0 a0;
new (a0.a) Int32; // { dg-warning "placement" }
new (p0->a) Int32;
new (r0.a) Int32;
}
void fA1 (A1 *p1, A1 &r1)
{
A1 a1;
new (a1.a) Int32; // { dg-warning "placement" }
new (p1->a) Int32; // no warning at level 1
new (r1.a) Int32; // no warning at level 1
}
void fA2 (A2 *p2, A2 &r2)
{
A2 a2;
new (a2.a) Int32; // { dg-warning "placement" }
new (p2->a) Int32; // { dg-warning "placement" }
new (r2.a) Int32; // { dg-warning "placement" }
}
struct BAx { int i; Ax ax; };
struct BA0 { int i; A0 a0; };
struct BA1 { int i; A1 a1; };
struct BA2 { int i; A2 a2; };
void fBx (BAx *pbx, BAx &rbx)
{
BAx bax;
new (bax.ax.a) char; // { dg-warning "placement" }
new (bax.ax.a) Int16; // { dg-warning "placement" }
new (bax.ax.a) Int32; // { dg-warning "placement" }
new (pbx->ax.a) char;
new (rbx.ax.a) char;
new (pbx->ax.a) Int16;
new (rbx.ax.a) Int16;
new (pbx->ax.a) Int32;
new (rbx.ax.a) Int32;
new (pbx->ax.a) int[1234];
new (rbx.ax.a) int[5678];
}
void fBx1 ()
{
BAx bax1 = { 1, /* Ax = */ { 2, /* a[] = */ { 3 } } };
new (bax1.ax.a) char;
new (bax1.ax.a) char[2]; // { dg-warning "placement" }
new (bax1.ax.a) Int16; // { dg-warning "placement" }
new (bax1.ax.a) Int32; // { dg-warning "placement" }
}
void fBx2 ()
{
BAx bax2 = { 1, /* Ax = */ { 2, /* a[] = */ { 3, 4 } } };
new (bax2.ax.a) char;
new (bax2.ax.a) char[2];
new (bax2.ax.a) char[3]; // { dg-warning "placement" }
new (bax2.ax.a) Int16;
new (bax2.ax.a) char[4]; // { dg-warning "placement" }
new (bax2.ax.a) Int32; // { dg-warning "placement" }
}
void fBx3 ()
{
BAx bax2 = { 1, /* Ax = */ { 3, /* a[] = */ { 4, 5, 6 } } };
new (bax2.ax.a) char;
new (bax2.ax.a) char[2];
new (bax2.ax.a) Int16;
new (bax2.ax.a) char[3];
new (bax2.ax.a) char[4]; // { dg-warning "placement" }
new (bax2.ax.a) Int32; // { dg-warning "placement" }
}
void fB0 (BA0 *pb0, BA0 &rb0)
{
BA0 ba0;
new (ba0.a0.a) Int32; // { dg-warning "placement" }
new (pb0->a0.a) Int32;
new (rb0.a0.a) Int32;
}
void fB1 (BA1 *pb1, BA1 &rb1)
{
BA1 ba1;
new (ba1.a1.a) Int32; // { dg-warning "placement" }
new (pb1->a1.a) Int32; // no warning at level 1
new (rb1.a1.a) Int32; // no warning at level 1
}
void fB2 (BA2 *pb2, BA2 &rb2)
{
BA2 ba2;
new (ba2.a2.a) Int32; // { dg-warning "placement" }
new (pb2->a2.a) Int32; // { dg-warning "placement" }
new (rb2.a2.a) Int32; // { dg-warning "placement" }
}

View File

@ -0,0 +1,197 @@
// PR c++/69662 - -Wplacement-new on allocated one element array members
// Exercising -Wplacement-new=2.
// { dg-do compile }
// { dg-options "-Wno-pedantic -Wplacement-new=2" }
typedef __typeof__ (sizeof 0) size_t;
void* operator new (size_t, void *p) { return p; }
void* operator new[] (size_t, void *p) { return p; }
struct Ax { char n, a []; };
struct A0 { char n, a [0]; };
struct A1 { char n, a [1]; };
struct A2 { char n, a [2]; };
typedef __INT16_TYPE__ Int16;
typedef __INT32_TYPE__ Int32;
void fAx (Ax *px, Ax &rx)
{
Ax ax;
new (ax.a) Int32; // { dg-warning "placement" }
new (ax.a) Int32[1]; // { dg-warning "placement" }
new (px->a) Int32;
new (px->a) Int32[1];
new (rx.a) Int32;
new (rx.a) Int32[2];
}
void fAx2 ()
{
// Initialization of non-static objects with flexible array members
// isn't allowed in C and should perhaps be disallowed in C++ as
// well to avoid c++/69696 - incorrect initialization of block-scope
// flexible array members.
Ax ax2 = { 1, { 2, 3 } };
new (ax2.a) Int16;
new (ax2.a) Int16[1];
new (ax2.a) Int16[2]; // { dg-warning "placement" }
new (ax2.a) Int32; // { dg-warning "placement" }
new (ax2.a) Int32[2]; // { dg-warning "placement" }
}
void fAx3 ()
{
static Ax ax3 = { 1, { 2, 3, 4 } };
new (ax3.a) Int16;
new (ax3.a) Int16[1];
new (ax3.a) Int16[2]; // { dg-warning "placement" }
new (ax3.a) Int32; // { dg-warning "placement" }
new (ax3.a) Int32[1]; // { dg-warning "placement" }
}
static Ax ax4 = { 1, { 2, 3, 4, 5 } };
void fAx4 ()
{
new (ax4.a) Int16;
new (ax4.a) Int16[1];
new (ax4.a) Int16[2];
new (ax4.a) Int32;
new (ax4.a) Int32[1];
new (ax4.a) Int32[2]; // { dg-warning "placement" }
}
void fA0 (A0 *p0, A0 &r0)
{
A0 a0;
new (a0.a) Int32; // { dg-warning "placement" }
new (a0.a) Int32[1]; // { dg-warning "placement" }
new (p0->a) Int32;
new (p0->a) Int32[1];
new (p0->a) Int32[2];
new (r0.a) Int32;
new (r0.a) Int32[1];
new (r0.a) Int32[2];
}
void fA1 (A1 *p1, A1 &r1)
{
A1 a1;
new (a1.a) Int32; // { dg-warning "placement" }
new (a1.a) Int32[1]; // { dg-warning "placement" }
new (p1->a) Int32; // { dg-warning "placement" }
new (p1->a) Int32[1]; // { dg-warning "placement" }
new (p1->a) Int32[2]; // { dg-warning "placement" }
new (r1.a) Int32; // { dg-warning "placement" }
new (r1.a) Int32[1]; // { dg-warning "placement" }
new (r1.a) Int32[2]; // { dg-warning "placement" }
}
void fA2 (A2 *p2, A2 &r2)
{
A2 a2;
new (a2.a) Int32; // { dg-warning "placement" }
new (a2.a) Int32[1]; // { dg-warning "placement" }
new (a2.a) Int32[2]; // { dg-warning "placement" }
new (p2->a) Int32; // { dg-warning "placement" }
new (p2->a) Int32[1]; // { dg-warning "placement" }
new (p2->a) Int32[2]; // { dg-warning "placement" }
new (r2.a) Int32; // { dg-warning "placement" }
new (r2.a) Int32[1]; // { dg-warning "placement" }
new (r2.a) Int32[2]; // { dg-warning "placement" }
}
struct BAx { int i; Ax ax; };
struct BA0 { int i; A0 a0; };
struct BA1 { int i; A1 a1; };
struct BA2 { int i; A2 a2; };
void fBx (BAx *pbx, BAx &rbx)
{
BAx bax;
new (bax.ax.a) char; // { dg-warning "placement" }
new (bax.ax.a) Int16; // { dg-warning "placement" }
new (bax.ax.a) Int32; // { dg-warning "placement" }
new (pbx->ax.a) char;
new (rbx.ax.a) char;
new (pbx->ax.a) Int16;
new (rbx.ax.a) Int16;
new (pbx->ax.a) Int32;
new (rbx.ax.a) Int32;
new (pbx->ax.a) int[1234];
new (rbx.ax.a) int[5678];
}
void fBx1 ()
{
BAx bax1 = { 1, /* Ax = */ { 2, /* a[] = */ { 3 } } };
new (bax1.ax.a) char;
new (bax1.ax.a) char[2]; // { dg-warning "placement" }
new (bax1.ax.a) Int16; // { dg-warning "placement" }
new (bax1.ax.a) Int32; // { dg-warning "placement" }
}
void fBx2 ()
{
BAx bax2 = { 1, /* Ax = */ { 2, /* a[] = */ { 3, 4 } } };
new (bax2.ax.a) char;
new (bax2.ax.a) char[2];
new (bax2.ax.a) char[3]; // { dg-warning "placement" }
new (bax2.ax.a) Int16;
new (bax2.ax.a) char[4]; // { dg-warning "placement" }
new (bax2.ax.a) Int32; // { dg-warning "placement" }
}
void fBx3 ()
{
BAx bax2 = { 1, /* Ax = */ { 3, /* a[] = */ { 4, 5, 6 } } };
new (bax2.ax.a) char;
new (bax2.ax.a) char[2];
new (bax2.ax.a) Int16;
new (bax2.ax.a) char[3];
new (bax2.ax.a) char[4]; // { dg-warning "placement" }
new (bax2.ax.a) Int32; // { dg-warning "placement" }
}
void fB0 (BA0 *pb0, BA0 &rb0)
{
BA0 ba0;
new (ba0.a0.a) Int32; // { dg-warning "placement" }
new (pb0->a0.a) Int32;
new (rb0.a0.a) Int32;
}
void fB1 (BA1 *pb1, BA1 &rb1)
{
BA1 ba1;
new (ba1.a1.a) Int32; // { dg-warning "placement" }
new (pb1->a1.a) Int32; // { dg-warning "placement" }
new (rb1.a1.a) Int32; // { dg-warning "placement" }
}
void fB2 (BA2 *pb2, BA2 &rb2)
{
BA2 ba2;
new (ba2.a2.a) Int32; // { dg-warning "placement" }
new (pb2->a2.a) Int32; // { dg-warning "placement" }
new (rb2.a2.a) Int32; // { dg-warning "placement" }
}