The attached patch implements the changes to the warning we discussed in IRC yesterday.
With the patch GCC is by default silent about the test case in bug 69662 and diagnoses it only with -Wplacement-new=2. Arrays of other sizes and statically allocated objects are diagnosed as before. Tested on x86_64. Martin
PR c++/69662 - -Wplacement-new on allocated one element array members gcc/testsuite/ChangeLog: 2016-02-03 Martin Sebor <mse...@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. gcc/cp/ChangeLog: 2016-02-03 Martin Sebor <mse...@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. gcc/c-family/ChangeLog: 2016-02-03 Martin Sebor <mse...@redhat.com> PR c++/69662 * c.opt (Warning options): Update -Wplacement-new to take an optional argument. gcc/ChangeLog: 2016-02-03 Martin Sebor <mse...@redhat.com> PR c++/69662 * doc/invoke.texi: Update -Wplacement-new to take an optional argument. Index: gcc/c-family/c.opt =================================================================== --- gcc/c-family/c.opt (revision 233145) +++ gcc/c-family/c.opt (working copy) @@ -777,7 +777,11 @@ ObjC ObjC++ Var(warn_protocol) Init(1) W 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 Index: gcc/cp/init.c =================================================================== --- gcc/cp/init.c (revision 233145) +++ gcc/cp/init.c (working copy) @@ -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, 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, { /* 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, 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, { 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_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, 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" Index: gcc/doc/invoke.texi =================================================================== --- gcc/doc/invoke.texi (revision 233145) +++ gcc/doc/invoke.texi (working copy) @@ -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}, 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,34 @@ 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 invalid constructs that +GCC allows as extensions for compatibility with legacy code. For example, +the following invalid @code{new} expression is not diagnosed at this level. +@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 Index: gcc/testsuite/g++.dg/warn/Wplacement-new-size-1.C =================================================================== --- gcc/testsuite/g++.dg/warn/Wplacement-new-size-1.C (revision 0) +++ gcc/testsuite/g++.dg/warn/Wplacement-new-size-1.C (working copy) @@ -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" } +} Index: gcc/testsuite/g++.dg/warn/Wplacement-new-size-2.C =================================================================== --- gcc/testsuite/g++.dg/warn/Wplacement-new-size-2.C (revision 0) +++ gcc/testsuite/g++.dg/warn/Wplacement-new-size-2.C (working copy) @@ -0,0 +1,137 @@ +// 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 (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; // { dg-warning "placement" } + new (r1.a) Int32; // { dg-warning "placement" } +} + +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; // { 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" } +}