diff mbox

tweak -Wplacement-new to fix #69662

Message ID 56B3B312.6040503@gmail.com
State New
Headers show

Commit Message

Martin Sebor Feb. 4, 2016, 8:22 p.m. UTC
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

Comments

Jason Merrill Feb. 4, 2016, 9:10 p.m. UTC | #1
On 02/04/2016 03:22 PM, Martin Sebor wrote:
> +  /* 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;

This doesn't make sense to me.  There should never be variables of types 
with flexible array members; such types should be allocated with malloc 
or the equivalent, along with enough extra space for the trailing array. 
  Indeed, the C compiler gives an error for initialization of a flexible 
array member.

Jason
Martin Sebor Feb. 4, 2016, 9:35 p.m. UTC | #2
On 02/04/2016 02:10 PM, Jason Merrill wrote:
> On 02/04/2016 03:22 PM, Martin Sebor wrote:
>> +  /* 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;
>
> This doesn't make sense to me.  There should never be variables of types
> with flexible array members; such types should be allocated with malloc
> or the equivalent, along with enough extra space for the trailing array.
>   Indeed, the C compiler gives an error for initialization of a flexible
> array member.

Gcc (in C mode) accepts, as an extension, statically initialized
objects of structs with flexible array members.  It rejects them
in other contexts (such as auto variables).

G++ accepts initialized objects of structs with flexible array
members in all contexts.  I don't know for sure if this difference
is intended or accidental but since C++ allows more safe constructs
than C does, and I can't think of anything wrong with allowing it,
the patch handles this case.  If we decide that initializing such
structs is a bad idea in C++ the special handling can be removed.

FWIW, I've been opening bugs for problems in this area as I find
them hoping to not just document them but also get a more complete
understanding of what's allowed as an extension and what's likely
a bug (and why), and eventually submit fixes.

For example, 68489 points out a problem with allowing arrays of
flexible array members.  Since there's no good way to fix that
problem such arrays should be rejected.  But I haven't been able
to come up with a reason why individual automatic structs with
flexible array members should be disallowed.  If there is such
a reason, we can start diagnosing them.  But I would be reluctant
to start outright rejecting them if they're potentially useful
because it could break working programs.

Martin
Jason Merrill Feb. 5, 2016, 1:36 p.m. UTC | #3
On 02/04/2016 04:35 PM, Martin Sebor wrote:
> On 02/04/2016 02:10 PM, Jason Merrill wrote:
>> On 02/04/2016 03:22 PM, Martin Sebor wrote:
>>> +  /* 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;
>>
>> This doesn't make sense to me.  There should never be variables of types
>> with flexible array members; such types should be allocated with malloc
>> or the equivalent, along with enough extra space for the trailing array.
>>   Indeed, the C compiler gives an error for initialization of a flexible
>> array member.
>
> Gcc (in C mode) accepts, as an extension, statically initialized
> objects of structs with flexible array members.  It rejects them
> in other contexts (such as auto variables).

That seems right.

> G++ accepts initialized objects of structs with flexible array
> members in all contexts.  I don't know for sure if this difference
> is intended or accidental but since C++ allows more safe constructs
> than C does, and I can't think of anything wrong with allowing it,
> the patch handles this case.  If we decide that initializing such
> structs is a bad idea in C++ the special handling can be removed.
>
> FWIW, I've been opening bugs for problems in this area as I find
> them hoping to not just document them but also get a more complete
> understanding of what's allowed as an extension and what's likely
> a bug (and why), and eventually submit fixes.
>
> For example, 68489 points out a problem with allowing arrays of
> flexible array members.  Since there's no good way to fix that
> problem such arrays should be rejected.  But I haven't been able
> to come up with a reason why individual automatic structs with
> flexible array members should be disallowed.  If there is such
> a reason, we can start diagnosing them.  But I would be reluctant
> to start outright rejecting them if they're potentially useful
> because it could break working programs.

struct A
{
   int i, ar[];
};

int main()
{
   int k = 24;
   struct A a = { 1, 2, 3, 4 };
   int j = 42;
   return a.ar[1];
}

G++ accepts this testcase and happily puts k and j in the same stack 
slots as elements of a.ar[], while GCC rejects it.  We shouldn't accept 
it.  In any case, we should usually follow the C front end's lead on 
this C compatibility feature.

Jason
Martin Sebor Feb. 5, 2016, 4:41 p.m. UTC | #4
> struct A
> {
>    int i, ar[];
> };
>
> int main()
> {
>    int k = 24;
>    struct A a = { 1, 2, 3, 4 };
>    int j = 42;
>    return a.ar[1];
> }
>
> G++ accepts this testcase and happily puts k and j in the same stack
> slots as elements of a.ar[], while GCC rejects it.  We shouldn't accept
> it.  In any case, we should usually follow the C front end's lead on
> this C compatibility feature.

I agree with that approach.  Going forward, after the 6.0 release,
I'd like to get back to this area and fix these remaining problems
and where it makes sense tighten up the C++ requirements to bring
them closer to C's.

But since GCC does allow global/static objects of structs with
flexible array members to be initialized, and (presumably) will
continue to even after the above is rejected in C++, the code
in the patch that detects overflowing such variables will continue
to serve its purpose.  By way of an example, I this is diagnosed
with the patch and should continue to be:

   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 []; } ax = { 1, { 2, 3, 4 } };

   void foo () {
     new (ax.a) short;
     new (ax.a) int;
   }
   t.c:6:51: warning: initialization of a flexible array member [-Wpedantic]
    struct Ax { char n, a []; } ax = { 1, { 2, 3, 4 } };
                                                    ^
   t.c: In function ‘void foo()’:
   t.c:10:16: warning: placement new constructing an object of type 
‘int’ and size ‘4’ in a region of type ‘char []’ and size ‘3’ 
[-Wplacement-new=]
        new (ax.a) int;
                   ^~~

That said, I'm interested in improving the -Wplacement-new warning
after 6.0 is done.  But unless there is a problem with the patch,
I would like to (need to) get back to my other projects that have
been put on hold to help with the release.

Martin

PS As an aside, I believe the root cause of the bug in your test
case is the same as in 28865 that was fixed in the C front end
just a couple of years ago by rejecting initialization of auto
variables with flexible array members(*).  An alternate solution
would have been to correct the .size directive to reflect the
size of the object rather than the size of the type and continue
to accept the construct.

[*] GCC in C mode still allows the following which has the same
problem as 28865.

struct A
{
   int i, ar[];
} aa = { 1, 2, 3, 4 };

int main()
{
   int k = 24;
   struct A a = aa;
   int j = 42;
   return a.ar[1];
}
Jason Merrill Feb. 5, 2016, 5:59 p.m. UTC | #5
On 02/05/2016 11:41 AM, Martin Sebor wrote:
> But since GCC does allow global/static objects of structs with
> flexible array members to be initialized, and (presumably) will
> continue to even after the above is rejected in C++, the code
> in the patch that detects overflowing such variables will continue
> to serve its purpose.

Good point.

> +@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

I'd say "undefined" rather than "invalid" here.

OK with that change.

Jason
diff mbox

Patch

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

gcc/testsuite/ChangeLog:
2016-02-03  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.

gcc/cp/ChangeLog:
2016-02-03  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.

gcc/c-family/ChangeLog:
2016-02-03  Martin Sebor  <msebor@redhat.com>

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

gcc/ChangeLog:
2016-02-03  Martin Sebor  <msebor@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" }
+}