public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
@ 2017-04-29 22:10 Martin Sebor
       [not found] ` <alpine.DEB.2.20.1704302338540.1461@digraph.polyomino.org.uk>
       [not found] ` <656ca1db-1082-b1ed-a911-ba7bf48f09c0@redhat.com>
  0 siblings, 2 replies; 46+ messages in thread
From: Martin Sebor @ 2017-04-29 22:10 UTC (permalink / raw)
  To: Gcc Patch List, Jason Merrill

[-- Attachment #1: Type: text/plain, Size: 1574 bytes --]

Calling memset, memcpy, or similar to write to an object of
a non-trivial type (such as one that defines a ctor or dtor,
or has such a member) can break the invariants otherwise
maintained by the class and cause undefined behavior.

The motivating example that prompted this work was a review of
a change that added to a plain old struct a new member with a ctor
and dtor (in this instance the member was of type std::vector).

To help catch problems of this sort some projects (such as GDB)
have apparently even devised their own clever solutions to detect
them: https://sourceware.org/ml/gdb-patches/2017-04/msg00378.html.

The attached patch adds a new warning, -Wnon-trivial-memaccess,
that has GCC detect these mistakes.  The patch also fixes up
a handful of instances of the problem in GCC.  These instances
correspond to the two patterns below:

   struct A
   {
     void *p;
     void foo (int n) { p = malloc (n); }
     ~A () { free (p); }
   };

   void init (A *a)
   {
     memset (a, 0, sizeof *a);
   }

and

   struct B
   {
     int i;
     ~A ();
   };

   void copy (B *p, const B *q)
   {
     memcpy (p, q, sizeof *p);
     ...
    }

These aren't undefined and the patch could be tweaked to allow
them.  I decided not to invest effort into it because, although
not strictly erroneous, I think they represent poor practice.
The code would be more clearly (and more in the spirit of "good"
C++) written in terms of the default constructor and assignment
operator.  The first one like so:

   *a = A ();

and the second one like so:

   *b = *q;

Martin

[-- Attachment #2: gcc-80560.diff --]
[-- Type: text/x-patch, Size: 10905 bytes --]

PR c++/80560 - warn on undefined memory operations involving non-trivial types

gcc/c-family/ChangeLog:

	PR c++/80560
	* c.opt (-Wnon-trivial-memaccess): New option.

gcc/cp/ChangeLog:

	PR c++/80560
	* call.c (maybe_warn_nontrivial_memaccess): New function.
	(build_cxx_call): Call it.

gcc/ChangeLog:

	PR c++/80560
	* doc/invoke.texi (Wnon-trivial-memaccess): Document new option.

libitm/ChangeLog:

	PR c++/80560
	* beginend.cc (GTM::gtm_thread::rollback): Avoid calling memset
	on an object of a non-trivial type.

libcpp/ChangeLog:

	PR c++/80560
	* line-map.c (line_maps::~line_maps): Avoid calling htab_delete
	with a null pointer.
	(linemap_init): Avoid calling memset on an object of a non-trivial
	type.

gcc/testsuite/ChangeLog:

	PR c++/80560
	* g++.dg/Wnon-trivial-memaccess.C: New test.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 9ad2f6e..dae5e80 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -792,6 +792,10 @@ Wnon-template-friend
 C++ ObjC++ Var(warn_nontemplate_friend) Init(1) Warning
 Warn when non-templatized friend functions are declared within a template.
 
+Wnon-trivial-memaccess
+C++ ObjC++ Var(warn_nontrival_memaccess) Warning LangEnabledBy(C++ ObjC++, Wall)
+Warn for raw memory writes to objects of non-trivial types.
+
 Wnon-virtual-dtor
 C++ ObjC++ Var(warn_nonvdtor) Warning LangEnabledBy(C++ ObjC++,Weffc++)
 Warn about non-virtual destructors.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index c15b8e4..8655b53 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8152,6 +8152,43 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
   return call;
 }
 
+/* Issue a warning on a call to the built-in function FNDECL if it is
+   a memory write whose destination is an object of a non-trivial type.  */
+
+static void
+maybe_warn_nontrivial_memaccess (location_t loc, tree fndecl, tree dest)
+{
+  /* Warn about raw memory operations whose destination is an object
+     of a non-trivial type because they are undefined.  */
+  bool memfunc = false;
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    case BUILT_IN_BZERO:
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMPCPY:
+    case BUILT_IN_MEMSET:
+      memfunc = true;
+      break;
+
+    default:
+      break;
+    }
+
+  if (memfunc)
+    {
+      if (TREE_CODE (dest) == NOP_EXPR)
+	dest = TREE_OPERAND (dest, 0);
+
+      tree desttype = TREE_TYPE (TREE_TYPE (dest));
+
+      if (COMPLETE_TYPE_P (desttype) && !trivial_type_p (desttype))
+	warning_at (loc, OPT_Wnon_trivial_memaccess,
+		    "calling %qD with a pointer to a non-trivial type %#qT",
+		    fndecl, desttype);
+    }
+}
+
 /* Build and return a call to FN, using NARGS arguments in ARGARRAY.
    This function performs no overload resolution, conversion, or other
    high-level operations.  */
@@ -8184,6 +8221,9 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
       if (!check_builtin_function_arguments (EXPR_LOCATION (fn), vNULL, fndecl,
 					     nargs, argarray))
 	return error_mark_node;
+
+      /* Warn if the built-in writes to an object of a non-trivial type.  */
+      maybe_warn_nontrivial_memaccess (loc, fndecl, argarray[0]);
     }
 
     /* If it is a built-in array notation function, then the return type of
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 0eeea7b..e1d01a9 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -215,7 +215,8 @@ in the following sections.
 -Wabi=@var{n}  -Wabi-tag  -Wconversion-null  -Wctor-dtor-privacy @gol
 -Wdelete-non-virtual-dtor  -Wliteral-suffix  -Wmultiple-inheritance @gol
 -Wnamespaces  -Wnarrowing @gol
--Wnoexcept  -Wnoexcept-type  -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
+-Wnoexcept  -Wnoexcept-type  -Wnon-trivial-memaccess @gol
+-Wnon-virtual-dtor  -Wreorder  -Wregister @gol
 -Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
 -Wno-non-template-friend  -Wold-style-cast @gol
 -Woverloaded-virtual  -Wno-pmf-conversions @gol
@@ -2911,6 +2912,21 @@ void g() noexcept;
 void h() @{ f(g); @} // in C++14 calls f<void(*)()>, in C++1z calls f<void(*)()noexcept>
 @end smallexample
 
+@item -Wnon-trivial-memaccess @r{(C++ and Objective-C++ only)}
+@opindex Wnon-trivial-memaccess
+Warn when the destination of a call to a raw memory function such as
+@code{memset} or @code{memcpy} is an object of a non-trivial class type.
+Modifying the representation of such an object may violate invariants
+maintained by member functions of the class.
+For example, the call to @code{memset} below is undefined becase it
+modifies a non-trivial class object and is, therefore, diagnosed.
+The safe way to either initialize or "reset" objects of non-trivial
+types is by using the appropriate constructor.
+@smallexample
+std::string str = "abc";
+memset (&str, 0, 3);
+@end smallexample
+The @option{-Wnon-trivial-memaccess} option is enabled by @option{-Wall}.
 
 @item -Wnon-virtual-dtor @r{(C++ and Objective-C++ only)}
 @opindex Wnon-virtual-dtor
diff --git a/gcc/testsuite/g++.dg/Wnon-trivial-memaccess.C b/gcc/testsuite/g++.dg/Wnon-trivial-memaccess.C
new file mode 100644
index 0000000..812a9eb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wnon-trivial-memaccess.C
@@ -0,0 +1,122 @@
+/* PR c++/80560 - warn on undefined memory operations involving non-trivial
+   types
+   { dg-do compile }
+   { dg-options "-Wnon-trivial-memaccess" } */
+
+struct Trivial { int i; char *s; char a[4]; };
+struct HasDefaultCtor { HasDefaultCtor (); };
+struct HasCopyCtor { HasCopyCtor (); };
+struct HasDtor { HasDtor (); };
+struct HasAssign { void operator= (HasAssign&); };
+
+typedef __SIZE_TYPE__ size_t;
+
+extern "C" {
+  void bzero (void*, size_t);
+  void* memcpy (void*, const void*, size_t);
+  void* memmove (void*, const void*, size_t);
+  void* mempcpy (void*, const void*, size_t);
+  void* memset (void*, int, size_t);
+}
+
+void sink (void*);
+
+#define T(fn, arglist) (fn arglist, sink (p))
+
+void test (Trivial *p, void *q)
+{
+  T (bzero, (p, sizeof *p));
+  T (bzero, (q, sizeof *p));
+
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (q, p, sizeof *p));
+
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (q, 0, sizeof *p));
+
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (q, p, sizeof *p));
+}
+
+void test (HasDefaultCtor *p, const void *q)
+{
+  T (bzero, (p, sizeof *p));   // { dg-warning "calling .void bzero(\[^\n\r\]*). with a pointer to a non-trivial type 'struct HasDefaultCtor." }
+
+  T (memcpy, (p, q, sizeof *p));  // { dg-warning "calling .void\\* memcpy" }
+
+  T (memset, (p, 0, sizeof *p));  // { dg-warning "calling .void\\* memset" }
+
+  T (memmove, (p, q, sizeof *p)); // { dg-warning "calling .void\\* memmove" }
+
+  T (mempcpy, (p, q, sizeof *p)); // { dg-warning "calling .void\\* mempcpy" }
+}
+
+void test (HasCopyCtor *p, const void *q)
+{
+  T (bzero, (p, sizeof *p));      // { dg-warning "calling .void bzero" }
+
+  T (memcpy, (p, q, sizeof *p));  // { dg-warning "calling .void\\* memcpy" }
+
+  T (memset, (p, 0, sizeof *p));  // { dg-warning "calling .void\\* memset" }
+
+  T (memmove, (p, q, sizeof *p)); // { dg-warning "calling .void\\* memmove" }
+
+  T (mempcpy, (p, q, sizeof *p)); // { dg-warning "calling .void\\* mempcpy" }
+}
+
+void test (HasDtor *p, const void *q)
+{
+  T (bzero, (p, sizeof *p));      // { dg-warning "calling .void bzero" }
+
+  T (memcpy, (p, q, sizeof *p));  // { dg-warning "calling .void\\* memcpy" }
+
+  T (memset, (p, 0, sizeof *p));  // { dg-warning "calling .void\\* memset" }
+
+  T (memmove, (p, q, sizeof *p)); // { dg-warning "calling .void\\* memmove" }
+
+  T (mempcpy, (p, q, sizeof *p)); // { dg-warning "calling .void\\* mempcpy" }
+}
+
+void test (HasAssign *p, const void *q)
+{
+  T (bzero, (p, sizeof *p));     // { dg-warning "calling .void bzero" }
+
+  T (memcpy, (p, q, sizeof *p));  // { dg-warning "calling .void\\* memcpy" }
+
+  T (memset, (p, 0, sizeof *p));  // { dg-warning "calling .void\\* memset" }
+
+  T (memmove, (p, q, sizeof *p)); // { dg-warning "calling .void\\* memmove" }
+
+  T (mempcpy, (p, q, sizeof *p)); // { dg-warning "calling .void\\* mempcpy" }
+}
+
+
+void test_expr (int i)
+{
+  struct TestClass: HasDefaultCtor { };
+  TestClass a, b;
+
+  static void *p;
+
+  T (bzero, (i < 0 ? &a : &b, 1));  // { dg-warning "calling .void bzero" }
+}
+
+void test_this ()
+{
+#undef T
+#define T(fn, arglist) (fn arglist, sink (this))
+
+  static const void *p;
+
+  struct TestDefaultCtor: HasDefaultCtor
+  {
+    TestDefaultCtor ()
+    {
+      T (bzero, (this, sizeof *this)); // { dg-warning "calling .void bzero" }
+      T (memset, (this, 0, sizeof *this)); // { dg-warning "calling .void\\* memset" }
+      T (memcpy, (this, p, sizeof *this)); // { dg-warning "calling .void\\* memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "calling .void\\* memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "calling .void\\* mempcpy" }
+    }
+  };
+}
diff --git a/libcpp/line-map.c b/libcpp/line-map.c
index 949489e..4e36e38 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -62,7 +62,8 @@ extern unsigned num_macro_tokens_counter;
 
 line_maps::~line_maps ()
 {
-  htab_delete (location_adhoc_data_map.htab);
+  if (location_adhoc_data_map.htab)
+    htab_delete (location_adhoc_data_map.htab);
 }
 
 /* Hash function for location_adhoc_data hashtable.  */
@@ -347,7 +348,7 @@ void
 linemap_init (struct line_maps *set,
 	      source_location builtin_location)
 {
-  memset (set, 0, sizeof (struct line_maps));
+  *set = line_maps ();
   set->highest_location = RESERVED_LOCATION_COUNT - 1;
   set->highest_line = RESERVED_LOCATION_COUNT - 1;
   set->location_adhoc_data_map.htab =
diff --git a/libitm/beginend.cc b/libitm/beginend.cc
index d04f3e9..c6550a3 100644
--- a/libitm/beginend.cc
+++ b/libitm/beginend.cc
@@ -431,7 +431,7 @@ GTM::gtm_transaction_cp::save(gtm_thread* tx)
   // Save everything that we might have to restore on restarts or aborts.
   jb = tx->jb;
   undolog_size = tx->undolog.size();
-  memcpy(&alloc_actions, &tx->alloc_actions, sizeof(alloc_actions));
+  alloc_actions = tx->alloc_actions;
   user_actions_size = tx->user_actions.size();
   id = tx->id;
   prop = tx->prop;
@@ -449,7 +449,7 @@ GTM::gtm_transaction_cp::commit(gtm_thread* tx)
   // commits of nested transactions. Allocation actions must be committed
   // before committing the snapshot.
   tx->jb = jb;
-  memcpy(&tx->alloc_actions, &alloc_actions, sizeof(alloc_actions));
+  tx->alloc_actions = alloc_actions;
   tx->id = id;
   tx->prop = prop;
 }
@@ -485,7 +485,7 @@ GTM::gtm_thread::rollback (gtm_transaction_cp *cp, bool aborting)
       prop = cp->prop;
       if (cp->disp != abi_disp())
 	set_abi_disp(cp->disp);
-      memcpy(&alloc_actions, &cp->alloc_actions, sizeof(alloc_actions));
+      alloc_actions = cp->alloc_actions;
       nesting = cp->nesting;
     }
   else

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
       [not found] ` <656ca1db-1082-b1ed-a911-ba7bf48f09c0@redhat.com>
@ 2017-05-01 15:49   ` Jason Merrill
  2017-05-11 20:03     ` Martin Sebor
  2017-05-11 16:34   ` Martin Sebor
  2017-07-05 20:58   ` Andrew Pinski
  2 siblings, 1 reply; 46+ messages in thread
From: Jason Merrill @ 2017-05-01 15:49 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Martin Sebor, Gcc Patch List

Pedro's suggestions all sound good to me.

Jason

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
       [not found] ` <alpine.DEB.2.20.1704302338540.1461@digraph.polyomino.org.uk>
@ 2017-05-03 16:18   ` Martin Sebor
  0 siblings, 0 replies; 46+ messages in thread
From: Martin Sebor @ 2017-05-03 16:18 UTC (permalink / raw)
  To: Joseph Myers; +Cc: Gcc Patch List, Jason Merrill

On 04/30/2017 05:39 PM, Joseph Myers wrote:
> On Sat, 29 Apr 2017, Martin Sebor wrote:
>
>> +The safe way to either initialize or "reset" objects of non-trivial
>
> Should use TeX quotes in Texinfo files, ``reset''.

Heh :) I wrestled with Emacs to get rid of those,  It kept replacing
my plain quotes with the TeX kind but in the end I won.  Sounds like
I should have trusted it.  Let me restore it in the follow-on patch.

Martin

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
       [not found] ` <656ca1db-1082-b1ed-a911-ba7bf48f09c0@redhat.com>
  2017-05-01 15:49   ` Jason Merrill
@ 2017-05-11 16:34   ` Martin Sebor
  2017-05-11 16:57     ` Jakub Jelinek
  2017-05-16 19:46     ` Jason Merrill
  2017-07-05 20:58   ` Andrew Pinski
  2 siblings, 2 replies; 46+ messages in thread
From: Martin Sebor @ 2017-05-11 16:34 UTC (permalink / raw)
  To: Pedro Alves, Gcc Patch List, Jason Merrill

On 04/30/2017 02:02 PM, Pedro Alves wrote:
> Hi Martin,
>
> Thanks much for doing this.  A few comments below, in light of my
> experience doing the equivalent checks in the gdb patch linked below,
> using standard C++11.

Thanks for the feedback!  It gave me quite a bit to think about.

The challenge with using memcpy or memset with class types is
figuring out if it's being called to copy-construct a new object
or assign a new value to an existing one.  In general it's not
possible to tell so the conservative assumption is that it's
the latter.

Because of that, relying on the trivially copyable property to
determine whether it's safe to assign a new value to an object
is not sufficient (or even necessary).  The class must be at
a minimum trivially assignable.  But it turns out that even
trivial assignability as defined by C++ isn't enough.  A class
with a const data member or a member of a reference type is
considered "trivially assignable" but its copy assignment is
deleted, and so it can't be assigned to.   Calling memset or
memcpy to write over an object of such a class can violate
the const invariant or corrupt the reference, respectively.

On the other hand, relying on the standard layout property
to decide whether or not calling memset on an object is safe,
while on the surface reasonable, is at the same time too strict
and overly permissive.  It's too strict because it warns for
calls where the destination is an object of a trivial derived
class that declares data members in one of its bases as well as
in the derived class (GCC has code like that).  It's not strict
enough because it doesn't catch cases where the class contains
only private or only protected data members (GCC is guilty of
abusing memset to violate encapsulation like that as well).

That said, I'm testing a solution that overcomes these problems.
I adjusted it so it doesn't warn on the GDB code in your example
(or any GDB code on trunk), even though in my opinion "tricks"
like that would best be avoided in favor of safer alternatives.

Unlike in C, the preferred way to initialize objects in C++
is to use some form of initialization (as opposed to memset).
The preferred way to copy objects is using the copy ctor or
assignment operator (as opposed to memcpy).  Using the special
member functions is clearer, less prone to mistakes and so
safer, and in some cases can also be more efficient.  Memcpy
and memset should be reserved for manipulating raw storage,
not typed objects.

Martin

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-11 16:34   ` Martin Sebor
@ 2017-05-11 16:57     ` Jakub Jelinek
  2017-05-11 17:17       ` Martin Sebor
  2017-05-16 19:46     ` Jason Merrill
  1 sibling, 1 reply; 46+ messages in thread
From: Jakub Jelinek @ 2017-05-11 16:57 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Pedro Alves, Gcc Patch List, Jason Merrill

On Thu, May 11, 2017 at 10:23:48AM -0600, Martin Sebor wrote:
> Unlike in C, the preferred way to initialize objects in C++
> is to use some form of initialization (as opposed to memset).
> The preferred way to copy objects is using the copy ctor or
> assignment operator (as opposed to memcpy).  Using the special
> member functions is clearer, less prone to mistakes and so
> safer, and in some cases can also be more efficient.  Memcpy
> and memset should be reserved for manipulating raw storage,
> not typed objects.

But optimizers should be able to turn the copy ctors/assignment operators
or default ctors into memcpy or memset where possible, making
it efficient again.  And that is something that doesn't work well yet.

	Jakub

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-11 16:57     ` Jakub Jelinek
@ 2017-05-11 17:17       ` Martin Sebor
  0 siblings, 0 replies; 46+ messages in thread
From: Martin Sebor @ 2017-05-11 17:17 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Pedro Alves, Gcc Patch List, Jason Merrill

On 05/11/2017 10:34 AM, Jakub Jelinek wrote:
> On Thu, May 11, 2017 at 10:23:48AM -0600, Martin Sebor wrote:
>> Unlike in C, the preferred way to initialize objects in C++
>> is to use some form of initialization (as opposed to memset).
>> The preferred way to copy objects is using the copy ctor or
>> assignment operator (as opposed to memcpy).  Using the special
>> member functions is clearer, less prone to mistakes and so
>> safer, and in some cases can also be more efficient.  Memcpy
>> and memset should be reserved for manipulating raw storage,
>> not typed objects.
>
> But optimizers should be able to turn the copy ctors/assignment operators
> or default ctors into memcpy or memset where possible, making
> it efficient again.  And that is something that doesn't work well yet.

I was referring to GCC not expanding inline calls to memcpy with
non-const size, as in function g below, whereas in f, the loop is
fully inlined for (likely) better performance.  This is meant to
be close to the GDB example of a vector-like class that uses
memcpy (or memmove) instead of ordinary assignment.

   struct S { char a[32]; };

   void f (S *p, const S *q, unsigned n)
   {
     while (n--)
       *p++ = *q++;
   }

   void g (S *p, const S *q, unsigned n)
   {
     __builtin_memcpy (p, q, sizeof *q * n);
   }

Martin

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-01 15:49   ` Jason Merrill
@ 2017-05-11 20:03     ` Martin Sebor
  2017-05-12  2:43       ` Martin Sebor
  2017-06-29 16:15       ` Jan Hubicka
  0 siblings, 2 replies; 46+ messages in thread
From: Martin Sebor @ 2017-05-11 20:03 UTC (permalink / raw)
  To: Jason Merrill, Pedro Alves; +Cc: Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 1787 bytes --]

Attached is revision 2 of the patch that addresses of the feedback
I got from Pedro (thanks again).  As I explained in my response
(https://gcc.gnu.org/ml/gcc-patches/2017-05/msg00925.html) it's not
entirely practical to rely on the strict interpretation of the C++
definition of type triviality or the standard layout property to
decide what to warn on.  Doing so results in too much noise for
safe code, and misses some interesting problems.

The warning instead implements its own slightly modified notion of
these concepts to detect common abuses of the functions without
flagging those that are safe in GCC (e.g, calling memset to clear
objects that would be standard layout if they didn't declare data
members in both derived and base classes).

The new warning detected a total of 74 instances (13 unique) in 8
GCC files.  I've gone through and resolved them by either replacing
memcpy/memset with invocations of the copy ctor or copy assignment
operator, or in a couple of instances by removing const quialifiers
from members of the flagged types.

-Wnon-trivial-memaccess Instances:
   /opt/notnfs/msebor/src/gcc/80560/gcc/gcc.c:2319
   /opt/notnfs/msebor/src/gcc/80560/gcc/gcc.c:7468
   /opt/notnfs/msebor/src/gcc/80560/gcc/genattrtab.c:4707
   /opt/notnfs/msebor/src/gcc/80560/gcc/hash-table.h:806
   /opt/notnfs/msebor/src/gcc/80560/gcc/ipa-cp.c:1491
   /opt/notnfs/msebor/src/gcc/80560/gcc/ipa-prop.c:3733
   /opt/notnfs/msebor/src/gcc/80560/gcc/omp-low.c:6295
   /opt/notnfs/msebor/src/gcc/80560/gcc/params.c:74
   /opt/notnfs/msebor/src/gcc/80560/gcc/vec.h:1098
   /opt/notnfs/msebor/src/gcc/80560/gcc/vec.h:1448
   /opt/notnfs/msebor/src/gcc/80560/gcc/vec.h:1613
   /opt/notnfs/msebor/src/gcc/80560/gcc/vec.h:618
   /opt/notnfs/msebor/src/gcc/80560/gcc/vec.h:841

Martin

[-- Attachment #2: gcc-80560.diff --]
[-- Type: text/x-patch, Size: 47899 bytes --]

PR c++/80560 - warn on undefined memory operations involving non-trivial types

gcc/c-family/ChangeLog:

	PR c++/80560
	* c.opt (-Wnon-trivial-memaccess): New option.

gcc/cp/ChangeLog:

	PR c++/80560
	* call.c (first_non_public_field): New function.
	(maybe_warn_nontrivial_memaccess): Same.
	(build_cxx_call): Call it.
	* cp-tree.h (has_trivial_assign, has_trivial_copy): Declare.
	* method.c (has_trivial_assign, has_trivial_copy): Define.	

gcc/ChangeLog:

	PR c++/80560
	* gcc.c (n_compilers, n_default_compilers): Make unsigned.
	(read_specs): Replace memset with assignment.
	(driver::set_up_specs): Replace memset with ctor.
	(driver::finalize): Use unsigned instead of int.
	* genattrtab.c (gen_insn_reserv): Replace memset with initialization.
	* hash-table.h: Ditto.
	* ipa-cp.c (allocate_and_init_ipcp_value): Replace memset with
	  assignment.
	* ipa-prop.c (ipa_free_edge_args_substructures): Ditto.
	* params.h (struct param_info): Make struct members non-const.
	* vec.h (vec_copy_construct): New helper function.
	(vec<T>::copy, vec<T>::splice, vec<T>::reserve): Replace memcpy
	with vec_copy_construct.
	* doc/invoke.texi (-Wnon-trivial-memaccess): Document.

libcpp/ChangeLog:

	PR c++/80560
	* line-map.c (line_maps::~line_maps): Avoid calling htab_delete
	with a null pointer.
	(linemap_init): Avoid calling memset on an object of a non-trivial
	type.

libitm/ChangeLog:

	PR c++/80560
	* beginend.cc (GTM::gtm_thread::rollback): Avoid calling memset
	on an object of a non-trivial type.
	(GTM::gtm_transaction_cp::commit): Use assignment instead of memcpy
	to copy an object.
	* method-ml.cc (orec_iterator::reinit): Avoid -Wnon-trivial-memaccess.

gcc/testsuite/ChangeLog:

	PR c++/80560
	* g++.dg/Wnon-trivial-memaccess.C: New test.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 9ad2f6e..dae5e80 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -792,6 +792,10 @@ Wnon-template-friend
 C++ ObjC++ Var(warn_nontemplate_friend) Init(1) Warning
 Warn when non-templatized friend functions are declared within a template.
 
+Wnon-trivial-memaccess
+C++ ObjC++ Var(warn_nontrival_memaccess) Warning LangEnabledBy(C++ ObjC++, Wall)
+Warn for raw memory writes to objects of non-trivial types.
+
 Wnon-virtual-dtor
 C++ ObjC++ Var(warn_nonvdtor) Warning LangEnabledBy(C++ ObjC++,Weffc++)
 Warn about non-virtual destructors.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index e348f29..44bc438 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8153,6 +8153,246 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
   return call;
 }
 
+/* Return the DECL of the first non-public data member of class TYPE
+   or null if none can be found.  */
+
+static tree
+first_non_public_field (tree type)
+{
+  if (!CLASS_TYPE_P (type))
+    return NULL_TREE;
+
+  for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+	continue;
+      if (TREE_STATIC (field))
+	continue;
+      if (TREE_PRIVATE (field) || TREE_PROTECTED (field))
+	return field;
+    }
+
+  return NULL_TREE;
+}
+
+/* Return true if class TYPE meets a relaxed definition of standard-layout.
+   In particular, the requirement that it "has all non-static data members
+   nd bit-fields in the class and its base classes first declared in the
+   same class" is not considered.  */
+
+static bool
+almost_std_layout_p (tree type, tree *field)
+{
+  if (!CLASS_TYPE_P (type))
+    return true;
+
+  if (!trivial_type_p (type))
+    return false;
+
+  if (tree fld = first_non_public_field (type))
+    if (!*field)
+      *field = fld;
+
+  tree binfo, base_binfo;
+
+  int i;
+
+  for (binfo = TYPE_BINFO (type), i = 0;
+       BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+    {
+      tree base = TREE_TYPE (base_binfo);
+
+      if (!almost_std_layout_p (base, field))
+	return false;
+    }
+
+  return true;
+}
+
+/* Issue a warning on a call to the built-in function FNDECL if it is
+   a memory write whose destination is not an object of (something like)
+   trivial or standard layout type.  Detects const correctness violations,
+   corrupting references, virtual table pointers, and bypassing non-
+   trivial assignments.  */
+
+static void
+maybe_warn_nontrivial_memaccess (location_t loc, tree fndecl, tree *args)
+{
+  /* The destination pointer is always first argument for all functions
+     handled here.  */
+  tree dest = args[0];
+  if (!dest || !TREE_TYPE (dest) || !POINTER_TYPE_P (TREE_TYPE (dest)))
+    return;
+
+  STRIP_NOPS (dest);
+
+  tree srctype = NULL_TREE;
+
+  /* Determine the type of the pointed-to object and whether it's
+     of class type.  If not, there's nothing to check.  */
+  tree desttype = TREE_TYPE (TREE_TYPE (dest));
+
+  if (!desttype || !COMPLETE_TYPE_P (desttype) || !CLASS_TYPE_P (desttype))
+    return;
+
+  /* Check to see if the memcpy/memset call is made by a ctor or dtor
+     with this as the destination argument for the destination type.
+     If so, be more permissive.  */
+  if (current_function_decl
+      && (DECL_CONSTRUCTOR_P (current_function_decl)
+	  || DECL_DESTRUCTOR_P (current_function_decl))
+      && is_this_parameter (dest))
+    {
+      tree ctx = DECL_CONTEXT (current_function_decl);
+      bool special = same_type_ignoring_top_level_qualifiers_p (ctx, desttype);
+
+      tree binfo = TYPE_BINFO (ctx);
+
+      /* A ctor and dtor for a class with no bases and no virtual functions
+	 can do whatever they want.  Bail early with no further checking.  */
+      if (special && !BINFO_VTABLE (binfo) && !BINFO_N_BASE_BINFOS (binfo))
+	return;
+    }
+
+  tree fld = NULL_TREE;
+  bool stdlayout = almost_std_layout_p (desttype, &fld);
+  bool trivial = trivial_type_p (desttype);
+  bool trivassign = has_trivial_assign (desttype);
+  bool trivcopy = has_trivial_copy (desttype);
+
+  /* Warn about raw memory operations whose destination is an object
+     of a non-trivial type because they are undefined.  */
+
+  const char *warnfmt = NULL;
+  bool warned = false;
+
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    case BUILT_IN_MEMSET:
+      if (!integer_zerop (args[1]))
+	{
+	  /* Diagnose setting non-copy-assignable, non-trivial or non-
+	     standard layout objects (in that order, since the latter
+	     two are subsets of one another), to (potentially) non-zero
+	     bytes.  Since the value of the bytes being written is unknown
+	     suggest using assignment instead (if one exists).  */
+	  if (!trivassign)
+	    warnfmt = G_("%qD writing to an object of type %#qT without "
+			 "trivial copy assignment");
+	  else if (!trivial)
+	    warnfmt = G_("%qD writing to an object of non-trivial "
+			 "type %#qT; use assignment instead");
+	  else if (!stdlayout)
+	    warnfmt = G_("%qD writing to an object of non-standard-layout "
+			 "type %#qT; use assignment instead");
+	  else if (fld)
+	    {
+	      const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	      warned = warning_at (loc, OPT_Wnon_trivial_memaccess,
+				   "%qD writing to an object of type %#qT with "
+				   "%qs member %qD; use assignment instead",
+				   fndecl, desttype, access, fld);
+	    }
+	  break;
+	}
+      /* Fall through.  */
+
+    case BUILT_IN_BZERO:
+      /* Similarly to the above, diagnose clearing non-trivial or non-
+	 standard layout objects, or objects of types with no assignmenmt.  */
+      if (!trivassign)
+	warnfmt = (trivcopy
+		   ? G_("%qD clearing an object of type %#qT without trivial "
+			"copy assignment; use copy constructor instead")
+		   : G_("%qD clearing an object of type %#qT without trivial "
+			"copy assignment"));
+      else if (!trivial)
+	warnfmt = G_("%qD clearing an object of non-trivial type "
+		     "%#qT; use assignment or default initialization instead");
+      else if (!stdlayout)
+	warnfmt = G_("%qD clearing an object of non-standard-layout type "
+		     "%#qT; use assignment or default initialization instead");
+      else if (fld)
+	{
+	  const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	  warned = warning_at (loc, OPT_Wnon_trivial_memaccess,
+			       "%qD clearing an object of type %#qT with "
+			       "%qs member %qD; use assignment or default "
+			       "initialization instead",
+			       fndecl, desttype, access, fld);
+	}
+      break;
+
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMPCPY:
+      /* Determine the type of the source object.  */
+      srctype = (TREE_CODE (args[1]) == NOP_EXPR
+		 ? TREE_OPERAND (args[1], 0) : args[1]);
+
+      srctype = TREE_TYPE (TREE_TYPE (srctype));
+
+      if (!trivassign)
+	warnfmt = (trivcopy
+		   ? G_("%qD copying to an object of type %#qT without trivial "
+			"copy assignment; use copy constructor instead")
+		   :  G_("%qD copying to an object of type %#qT without trivial "
+			 "copy assignment"));
+      else if (!trivially_copyable_p (desttype))
+	warnfmt = G_("%qD copying to an object of non-trivially-copyable type "
+		     "%#qT; use assignment or copy initialization instead");
+      else if (!trivial
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  /* Warn when copying into a non-trivial object from an object
+	     of a different type other than void or char.  */
+	  warned = warning_at (loc, OPT_Wnon_trivial_memaccess,
+			       "%qD copying an object of non-trivial type "
+			       "%#qT from an array of %#qT",
+			       fndecl, desttype, srctype);
+	}
+      else if (fld)
+	{
+	  const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	  warned = warning_at (loc, OPT_Wnon_trivial_memaccess,
+			       "%qD clearing an object of type %#qT with "
+			       "%qs member %qD; use assignment or default "
+			       "initialization instead",
+			       fndecl, desttype, access, fld);
+	}
+      else if (!trivial && TREE_CODE (args[2]) == INTEGER_CST)
+	{
+	  /* Finally, warn on partial copies.  */
+	  unsigned HOST_WIDE_INT typesize
+	    = tree_to_uhwi (TYPE_SIZE_UNIT (desttype));
+	  if (unsigned HOST_WIDE_INT nbytes = tree_to_uhwi (args[2]) % typesize)
+	    warned = warning_at (loc, OPT_Wnon_trivial_memaccess,
+				 (nbytes > 1
+				  ? G_("%qD copying to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "byte unchanged")
+				  : G_("%qD copying to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "bytes unchanged")),
+				 fndecl, desttype, nbytes);
+	}
+      break;
+
+    default:
+      return;
+    }
+
+  if (!warned && !warnfmt)
+    return;
+
+  if (warned
+      || warning_at (loc, OPT_Wnon_trivial_memaccess, warnfmt, fndecl, desttype))
+    inform (location_of (desttype), "%#qT declared here", desttype);
+}
+
 /* Build and return a call to FN, using NARGS arguments in ARGARRAY.
    This function performs no overload resolution, conversion, or other
    high-level operations.  */
@@ -8185,6 +8425,9 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
       if (!check_builtin_function_arguments (EXPR_LOCATION (fn), vNULL, fndecl,
 					     nargs, argarray))
 	return error_mark_node;
+
+      /* Warn if the built-in writes to an object of a non-trivial type.  */
+      maybe_warn_nontrivial_memaccess (loc, fndecl, argarray);
     }
 
     /* If it is a built-in array notation function, then the return type of
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8120b93..80508e3 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6131,6 +6131,9 @@ extern bool ctor_omit_inherited_parms		(tree);
 extern tree locate_ctor				(tree);
 extern tree implicitly_declare_fn               (special_function_kind, tree,
 						 bool, tree, tree);
+extern bool has_trivial_assign                  (tree);
+extern bool has_trivial_copy                    (tree);
+
 
 /* In optimize.c */
 extern bool maybe_clone_body			(tree);
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index b4c1f60..810119e 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -1716,6 +1716,44 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
   --c_inhibit_evaluation_warnings;
 }
 
+/* Return true if the class type CTYPE has non-deleted trivial copy
+   assignment.  */
+
+bool
+has_trivial_assign (tree ctype)
+{
+  if (!CLASS_TYPE_P (ctype))
+    return true;
+
+  bool trivial_p = false;
+  bool deleted_p = false;
+
+  synthesized_method_walk (ctype, sfk_copy_assignment, false, NULL,
+			   &trivial_p, &deleted_p,
+			   NULL, false, NULL_TREE, NULL_TREE);
+
+  return trivial_p && !deleted_p;
+}
+
+/* Return true if the class type CTYPE has non-deleted trivial copy
+   constructor.  */
+
+bool
+has_trivial_copy (tree ctype)
+{
+  if (!CLASS_TYPE_P (ctype))
+    return true;
+
+  bool trivial_p = false;
+  bool deleted_p = false;
+
+  synthesized_method_walk (ctype, sfk_copy_constructor, false, NULL,
+			   &trivial_p, &deleted_p,
+			   NULL, false, NULL_TREE, NULL_TREE);
+
+  return trivial_p && !deleted_p;
+}
+
 /* DECL is a defaulted function whose exception specification is now
    needed.  Return what it should be.  */
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 57c9678..3aa850c 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -215,7 +215,8 @@ in the following sections.
 -Wabi=@var{n}  -Wabi-tag  -Wconversion-null  -Wctor-dtor-privacy @gol
 -Wdelete-non-virtual-dtor  -Wliteral-suffix  -Wmultiple-inheritance @gol
 -Wnamespaces  -Wnarrowing @gol
--Wnoexcept  -Wnoexcept-type  -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
+-Wnoexcept  -Wnoexcept-type  -Wnon-trivial-memaccess @gol
+-Wnon-virtual-dtor  -Wreorder  -Wregister @gol
 -Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
 -Wno-non-template-friend  -Wold-style-cast @gol
 -Woverloaded-virtual  -Wno-pmf-conversions @gol
@@ -2913,6 +2914,24 @@ void g() noexcept;
 void h() @{ f(g); @} // in C++14 calls f<void(*)()>, in C++1z calls f<void(*)()noexcept>
 @end smallexample
 
+@item -Wnon-trivial-memaccess @r{(C++ and Objective-C++ only)}
+@opindex Wnon-trivial-memaccess
+Warn when the destination of a call to a raw memory function such as
+@code{memset} or @code{memcpy} is an object of class type where doing
+so might bypass the class non-trivial constructor or copy assignment,
+violate const-correctness or encapsulation, or corrupt the virtual
+table.  Modifying the representation of such objects may violate
+invariants maintained by member functions of the class.
+For example, the call to @code{memset} below is undefined becase it
+modifies a non-trivial class object and is, therefore, diagnosed.
+The safe way to either initialize or clear the storage of objects of
+non-trivial types is by using the appropriate constructor.
+@smallexample
+std::string str = "abc";
+memset (&str, 0, 3);
+@end smallexample
+The option does not warn on safe calls to such functions 
+The @option{-Wnon-trivial-memaccess} option is enabled by @option{-Wall}.
 
 @item -Wnon-virtual-dtor @r{(C++ and Objective-C++ only)}
 @opindex Wnon-virtual-dtor
diff --git a/gcc/testsuite/g++.dg/Wnon-trivial-memaccess.C b/gcc/testsuite/g++.dg/Wnon-trivial-memaccess.C
new file mode 100644
index 0000000..dc93c9b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wnon-trivial-memaccess.C
@@ -0,0 +1,571 @@
+/* PR c++/80560 - warn on undefined memory operations involving non-trivial
+   types
+   { dg-do compile }
+   { dg-options "-Wnon-trivial-memaccess -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern "C"
+{
+void bzero (void*, size_t);
+void* memcpy (void*, const void*, size_t);
+void* memmove (void*, const void*, size_t);
+void* mempcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+}
+
+/* Trivial can be manipulated by raw memory functions.  */
+struct Trivial { int i; char *s; char a[4]; };
+
+/* HasDefaultCtor is trivially copyable but should be initialized by
+   the ctor, not bzero or memset.  */
+struct HasDefaultCtor { HasDefaultCtor (); };
+
+/* HasCopyCtor should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  Since it's non-trivial, i should not be zeroed
+   out by bzero/memset either and should instead use assignment and/or
+   default initialization.  */
+struct HasCopyCtor { HasCopyCtor (const HasCopyCtor&); };
+
+/* HasDtor should be initialized using aggregate or memberwise intialization,
+   not bzero or memset.  */
+struct HasDtor { ~HasDtor (); };
+
+/* HasAssign should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  */
+struct HasAssign { void operator= (HasAssign&); };
+
+/* HasVirtuals should only be manipulated by the special member functions
+   and not by bzero, memcpy, or any other raw memory function. Doing
+   otherwse might corrupt the the vtable pointer.  */
+struct HasVirtuals { virtual void foo (); };
+
+/* HasConstData should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable allowing, raw memory functions to write into it would defeat
+   const-correctness.  */
+struct HasConstData { const int ci; };
+
+/* HasReference should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable, allowing raw memory functions to write into it could
+   corrupt the reference.  */
+struct HasReference { int &ci; };
+
+/* HasSomePrivateDate and HasSomeProtectedData should only be initialized
+   using aggregate initializatoon and not cleared by bzero, or copied into
+   using memcpy.  Doing otherwise would break encapsulation.  */
+struct HasSomePrivateData { int i; private: int j; };
+struct HasSomeProtectedData { int i; protected: int j; };
+
+/* Similarly to the above, HasAllPrivateDate and HasAllProtectedData should
+   only be initialized using aggregate initializatoon and not cleared by
+   bzero, or copied into using memcpy.  They are tested separately because
+   unlike the former classes, these are standard layout.  */
+struct HasAllPrivateData { private: int i; };
+struct HasAllProtectedData { protected: int i; };
+
+void sink (void*);
+
+#define T(fn, arglist) (fn arglist, sink (p))
+
+#if !defined TEST || TEST == TEST_TRIVIAL
+
+void test (Trivial *p, void *q, int x)
+{
+  T (bzero, (p, sizeof *p));
+  T (bzero, (q, sizeof *p));
+
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (q, p, sizeof *p));
+
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (q, 0, sizeof *p));
+
+  T (memset, (p, x, sizeof *p));
+  T (memset, (q, x, sizeof *p));
+
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (q, p, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DEFAULT_CTOR
+
+void test (HasDefaultCtor *p, const HasDefaultCtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // HasDefaultCtor is neither trivial nor standard-layout.  The warning
+  // should mention the former since it's more permissive than the latter
+  // and so more informative.
+  T (bzero, (p, sizeof *p));   // { dg-warning ".void bzero(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefaultCtor.; use assignment or default initialization instead" }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefaultCtor.; use assignment or default initialization instead" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefaultCtor.; use assignment instead" }
+
+
+  // Copying from another object of the same type is fine.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, sizeof *p * i));
+
+  // Copying from a void* or character buffer is also fine.
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, sizeof *p * i));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, sizeof *p * i));
+
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, q, sizeof *p * i));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, s, sizeof *p * i));
+
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p * i));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p * i));
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefaultCtor. from an array of .const int." }
+
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning ".void\\* memmove(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefaultCtor. from an array of .const int." }
+
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning ".void\\* mempcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefaultCtor. from an array of .const int." }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_COPY_CTOR
+
+void test (HasCopyCtor *p, const HasCopyCtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed because default initialization is
+  // invalid (the copy ctor makes no default ctor unavailable).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DTOR
+
+void test (HasDtor *p, const HasDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed only because it's difficult not to.
+  // Otherwise, a class that's non-trivial only because it has
+  // a non-trivial dtor can be safely zeroed out (that's what
+  // default-initializing it does).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed simply because
+  // a class with a user-defined dtor is not trivially copyable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ASSIGN
+
+void test (HasAssign *p, const HasAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_VIRTUALS
+
+void test (HasVirtuals *p, const HasVirtuals &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed because it corrupts the vtable.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is diagnosed because when used to initialize an object
+  // could incorrectly initialize the vtable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_CONST_DATA
+
+void test (HasConstData *p, const HasConstData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // The following is ill-formed because HasConstData's cannot
+  // be assigned (the assignment is implicitly deleted).  For
+  // that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object could break const correctness.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasConstData. without trivial copy assignment" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "copying to an object of type .struct HasConstData. without trivial copy assignment" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_REFERENCE
+
+void test (HasReference *p, const HasReference &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Similarly to HasConstData, the following is ill-formed because
+  // Hasreference cannot be assigned (the assignment is implicitly
+  // deleted).  For that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object would invalidate the reference.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasReference. without trivial copy assignment" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "copying to an object of type .struct HasReference. without trivial copy assignment" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PRIVATE_DATA
+
+void test (HasSomePrivateData *p, const HasSomePrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // All operations are diagnosed becauce they break encapsulation.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PROTECTED_DATA
+
+void test (HasSomeProtectedData *p, const HasSomeProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // All operations are diagnosed becauce they break encapsulation.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PRIVATE_DATA
+
+void test (HasAllPrivateData *p, const HasAllPrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // All operations are diagnosed becauce they break encapsulation.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PROTECTED_DATA
+
+void test (HasAllProtectedData *p, const HasAllProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // All operations are diagnosed becauce they break encapsulation.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_EXPRESSION
+
+void test_expr (int i)
+{
+  struct TestClass: HasDefaultCtor { };
+  TestClass a, b;
+
+  static void *p;
+
+  T (bzero, (i < 0 ? &a : &b, 1));  // { dg-warning ".void bzero" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_CTOR
+
+void test_ctor ()
+{
+#undef T
+#define T(fn, arglist) (fn arglist, sink (this))
+
+  static void *p;
+
+  struct TestBase
+  {
+    TestBase ()
+    {
+      /* A ctor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+
+    ~TestBase ()
+    {
+      /* A dtor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestBaseVtable
+  {
+    TestBaseVtable ()
+    {
+      /* A ctor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    ~TestBaseVtable ()
+    {
+      /* A dtor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    virtual void foo ();
+  };
+
+  struct TestDerived: HasDefaultCtor
+  {
+    TestDerived ()
+    {
+      /* A derived class ctor is treated as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestDerivedDtor: HasDefaultCtor
+  {
+    ~TestDerivedDtor ()
+    {
+      /* A derived class dtor is treated as an ordinary function though
+	 it probably shouldn't be unless the base dtor is trivial.  But
+	 it doesn't seem worth the trouble.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+  };
+}
+
+#endif
diff --git a/libcpp/line-map.c b/libcpp/line-map.c
index c4b7cb2..56b7913 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -62,7 +62,8 @@ extern unsigned num_macro_tokens_counter;
 
 line_maps::~line_maps ()
 {
-  htab_delete (location_adhoc_data_map.htab);
+  if (location_adhoc_data_map.htab)
+    htab_delete (location_adhoc_data_map.htab);
 }
 
 /* Hash function for location_adhoc_data hashtable.  */
@@ -347,7 +348,7 @@ void
 linemap_init (struct line_maps *set,
 	      source_location builtin_location)
 {
-  memset (set, 0, sizeof (struct line_maps));
+  *set = line_maps ();
   set->highest_location = RESERVED_LOCATION_COUNT - 1;
   set->highest_line = RESERVED_LOCATION_COUNT - 1;
   set->location_adhoc_data_map.htab =
diff --git a/libitm/beginend.cc b/libitm/beginend.cc
index d04f3e9..c6550a3 100644
--- a/libitm/beginend.cc
+++ b/libitm/beginend.cc
@@ -431,7 +431,7 @@ GTM::gtm_transaction_cp::save(gtm_thread* tx)
   // Save everything that we might have to restore on restarts or aborts.
   jb = tx->jb;
   undolog_size = tx->undolog.size();
-  memcpy(&alloc_actions, &tx->alloc_actions, sizeof(alloc_actions));
+  alloc_actions = tx->alloc_actions;
   user_actions_size = tx->user_actions.size();
   id = tx->id;
   prop = tx->prop;
@@ -449,7 +449,7 @@ GTM::gtm_transaction_cp::commit(gtm_thread* tx)
   // commits of nested transactions. Allocation actions must be committed
   // before committing the snapshot.
   tx->jb = jb;
-  memcpy(&tx->alloc_actions, &alloc_actions, sizeof(alloc_actions));
+  tx->alloc_actions = alloc_actions;
   tx->id = id;
   tx->prop = prop;
 }
@@ -485,7 +485,7 @@ GTM::gtm_thread::rollback (gtm_transaction_cp *cp, bool aborting)
       prop = cp->prop;
       if (cp->disp != abi_disp())
 	set_abi_disp(cp->disp);
-      memcpy(&alloc_actions, &cp->alloc_actions, sizeof(alloc_actions));
+      alloc_actions = cp->alloc_actions;
       nesting = cp->nesting;
     }
   else
diff --git a/libitm/method-ml.cc b/libitm/method-ml.cc
index fcae334..b857bff 100644
--- a/libitm/method-ml.cc
+++ b/libitm/method-ml.cc
@@ -138,7 +138,11 @@ struct ml_mg : public method_group
     // This store is only executed while holding the serial lock, so relaxed
     // memory order is sufficient here.  Same holds for the memset.
     time.store(0, memory_order_relaxed);
-    memset(orecs, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
+    // The memset below isn't strictly kosher because it bypasses
+    // the non-trivial assignment operator defined by std::atomic.  Using
+    // a local void* is enough to prevent GCC from warning for this.
+    void *p = orecs;
+    memset(p, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
   }
 };
 
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 826b012..acaa7cb 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -1253,9 +1253,9 @@ struct compiler
   const char *cpp_spec;         /* If non-NULL, substitute this spec
 				   for `%C', rather than the usual
 				   cpp_spec.  */
-  const int combinable;          /* If nonzero, compiler can deal with
+  int combinable;               /* If nonzero, compiler can deal with
 				    multiple source files at once (IMA).  */
-  const int needs_preprocessing; /* If nonzero, source files need to
+  int needs_preprocessing;      /* If nonzero, source files need to
 				    be run through a preprocessor.  */
 };
 
@@ -1272,7 +1272,7 @@ static struct compiler *compilers;
 
 /* Number of entries in `compilers', not counting the null terminator.  */
 
-static int n_compilers;
+static unsigned n_compilers;
 
 /* The default list of file name suffixes and their compilation specs.  */
 
@@ -1369,7 +1369,7 @@ static const struct compiler default_compilers[] =
 
 /* Number of elements in default_compilers, not counting the terminator.  */
 
-static const int n_default_compilers = ARRAY_SIZE (default_compilers) - 1;
+static const unsigned n_default_compilers = ARRAY_SIZE (default_compilers) - 1;
 
 typedef char *char_p; /* For DEF_VEC_P.  */
 
@@ -2310,7 +2310,7 @@ read_specs (const char *filename, bool main_p, bool user_p)
 	  compilers[n_compilers].suffix = suffix;
 	  compilers[n_compilers].spec = spec;
 	  n_compilers++;
-	  memset (&compilers[n_compilers], 0, sizeof compilers[n_compilers]);
+	  compilers[n_compilers] = compiler ();
 	}
 
       if (*suffix == 0)
@@ -7419,7 +7419,8 @@ driver::set_up_specs () const
      This means one element containing 0s, as a terminator.  */
 
   compilers = XNEWVAR (struct compiler, sizeof default_compilers);
-  memcpy (compilers, default_compilers, sizeof default_compilers);
+  for (i = 0; i != n_default_compilers; ++i)
+    new (compilers + i) compiler (default_compilers[i]);
   n_compilers = n_default_compilers;
 
   /* Read specs from a file if there is one.  */
@@ -10014,7 +10015,7 @@ driver::finalize ()
   /* Within the "compilers" vec, the fields "suffix" and "spec" were
      statically allocated for the default compilers, but dynamically
      allocated for additional compilers.  Delete them for the latter. */
-  for (int i = n_default_compilers; i < n_compilers; i++)
+  for (unsigned i = n_default_compilers; i < n_compilers; i++)
     {
       free (const_cast <char *> (compilers[i].suffix));
       free (const_cast <char *> (compilers[i].spec));
diff --git a/gcc/genattrtab.c b/gcc/genattrtab.c
index 3629b5f..51dfe77 100644
--- a/gcc/genattrtab.c
+++ b/gcc/genattrtab.c
@@ -4703,8 +4703,8 @@ gen_insn_reserv (md_rtx_info *info)
   struct insn_reserv *decl = oballoc (struct insn_reserv);
   rtx def = info->def;
 
-  struct attr_desc attr;
-  memset (&attr, 0, sizeof (attr));
+  struct attr_desc attr = { };
+
   attr.name = DEF_ATTR_STRING (XSTR (def, 0));
   attr.loc = info->loc;
 
diff --git a/gcc/hash-table.h b/gcc/hash-table.h
index 0f7e21a..443d16c 100644
--- a/gcc/hash-table.h
+++ b/gcc/hash-table.h
@@ -803,7 +803,10 @@ hash_table<Descriptor, Allocator>::empty_slow ()
       m_size_prime_index = nindex;
     }
   else
-    memset (entries, 0, size * sizeof (value_type));
+    {
+      for ( ; size; ++entries, --size)
+	*entries = value_type ();
+    }
   m_n_deleted = 0;
   m_n_elements = 0;
 }
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 26ae8fc..752ed99 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1473,7 +1473,7 @@ allocate_and_init_ipcp_value (tree source)
   ipcp_value<tree> *val;
 
   val = ipcp_cst_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  *val = ipcp_value<tree>();
   val->value = source;
   return val;
 }
@@ -1488,7 +1488,7 @@ allocate_and_init_ipcp_value (ipa_polymorphic_call_context source)
 
   // TODO
   val = ipcp_poly_ctx_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  *val = ipcp_value<ipa_polymorphic_call_context>();
   val->value = source;
   return val;
 }
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index b3d5159..8c912b4 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -3731,7 +3731,7 @@ void
 ipa_free_edge_args_substructures (struct ipa_edge_args *args)
 {
   vec_free (args->jump_functions);
-  memset (args, 0, sizeof (*args));
+  *args = ipa_edge_args ();
 }
 
 /* Free all ipa_edge structures.  */
diff --git a/gcc/params.h b/gcc/params.h
index b61cff9..8b91660 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -42,7 +42,7 @@ struct param_info
 {
   /* The name used with the `--param <name>=<value>' switch to set this
      value.  */
-  const char *const option;
+  const char *option;
 
   /* The default value.  */
   int default_value;
@@ -54,7 +54,7 @@ struct param_info
   int max_value;
 
   /* A short description of the option.  */
-  const char *const help;
+  const char *help;
 
   /* The optional names corresponding to the values.  */
   const char **value_names;
diff --git a/gcc/vec.h b/gcc/vec.h
index fee4616..ef9a56f 100644
--- a/gcc/vec.h
+++ b/gcc/vec.h
@@ -407,6 +407,16 @@ struct GTY((user)) vec
 {
 };
 
+/* Copy-construct N elements in DST from *SRC.  */
+
+template <typename T>
+inline void
+vec_copy_construct (T *dst, const T *src, unsigned n)
+{
+  for ( ; n; ++dst, ++src, --n)
+    ::new (static_cast<void*>(dst)) T (*src);
+}
+
 /* Type to provide NULL values for vec<T, A, L>.  This is used to
    provide nil initializers for vec instances.  Since vec must be
    a POD, we cannot have proper ctor/dtor for it.  To initialize
@@ -821,7 +831,7 @@ vec<T, A, vl_embed>::copy (ALONE_MEM_STAT_DECL) const
     {
       vec_alloc (new_vec, len PASS_MEM_STAT);
       new_vec->embedded_init (len, len);
-      memcpy (new_vec->address (), m_vecdata, sizeof (T) * len);
+      vec_copy_construct (new_vec->address (), m_vecdata, len);
     }
   return new_vec;
 }
@@ -838,7 +848,7 @@ vec<T, A, vl_embed>::splice (const vec<T, A, vl_embed> &src)
   if (len)
     {
       gcc_checking_assert (space (len));
-      memcpy (address () + length (), src.address (), len * sizeof (T));
+      vec_copy_construct (address () + length (), src.address (), len);
       m_vecpfx.m_num += len;
     }
 }
@@ -1098,7 +1108,6 @@ vec<T, A, vl_embed>::quick_grow_cleared (unsigned len)
     memset (&(address ()[oldlen]), 0, sz);
 }
 
-
 /* Garbage collection support for vec<T, A, vl_embed>.  */
 
 template<typename T>
@@ -1445,7 +1454,7 @@ vec<T, va_heap, vl_ptr>::reserve (unsigned nelems, bool exact MEM_STAT_DECL)
   va_heap::reserve (m_vec, nelems, exact PASS_MEM_STAT);
   if (handle_auto_vec)
     {
-      memcpy (m_vec->address (), oldvec->address (), sizeof (T) * oldsize);
+      vec_copy_construct (m_vec->address (), oldvec->address (), oldsize);
       m_vec->m_vecpfx.m_num = oldsize;
     }
 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-11 20:03     ` Martin Sebor
@ 2017-05-12  2:43       ` Martin Sebor
  2017-05-17 11:53         ` Pedro Alves
  2017-06-29 16:15       ` Jan Hubicka
  1 sibling, 1 reply; 46+ messages in thread
From: Martin Sebor @ 2017-05-12  2:43 UTC (permalink / raw)
  To: Jason Merrill, Pedro Alves; +Cc: Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 2019 bytes --]

I found a few more instances of the warning and a couple of bugs
in the fixes for it.  The attached update corrects those as well.

On 05/11/2017 02:01 PM, Martin Sebor wrote:
> Attached is revision 2 of the patch that addresses of the feedback
> I got from Pedro (thanks again).  As I explained in my response
> (https://gcc.gnu.org/ml/gcc-patches/2017-05/msg00925.html) it's not
> entirely practical to rely on the strict interpretation of the C++
> definition of type triviality or the standard layout property to
> decide what to warn on.  Doing so results in too much noise for
> safe code, and misses some interesting problems.
>
> The warning instead implements its own slightly modified notion of
> these concepts to detect common abuses of the functions without
> flagging those that are safe in GCC (e.g, calling memset to clear
> objects that would be standard layout if they didn't declare data
> members in both derived and base classes).
>
> The new warning detected a total of 74 instances (13 unique) in 8
> GCC files.  I've gone through and resolved them by either replacing
> memcpy/memset with invocations of the copy ctor or copy assignment
> operator, or in a couple of instances by removing const quialifiers
> from members of the flagged types.
>
> -Wnon-trivial-memaccess Instances:
>   /opt/notnfs/msebor/src/gcc/80560/gcc/gcc.c:2319
>   /opt/notnfs/msebor/src/gcc/80560/gcc/gcc.c:7468
>   /opt/notnfs/msebor/src/gcc/80560/gcc/genattrtab.c:4707
>   /opt/notnfs/msebor/src/gcc/80560/gcc/hash-table.h:806
>   /opt/notnfs/msebor/src/gcc/80560/gcc/ipa-cp.c:1491
>   /opt/notnfs/msebor/src/gcc/80560/gcc/ipa-prop.c:3733
>   /opt/notnfs/msebor/src/gcc/80560/gcc/omp-low.c:6295
>   /opt/notnfs/msebor/src/gcc/80560/gcc/params.c:74
>   /opt/notnfs/msebor/src/gcc/80560/gcc/vec.h:1098
>   /opt/notnfs/msebor/src/gcc/80560/gcc/vec.h:1448
>   /opt/notnfs/msebor/src/gcc/80560/gcc/vec.h:1613
>   /opt/notnfs/msebor/src/gcc/80560/gcc/vec.h:618
>   /opt/notnfs/msebor/src/gcc/80560/gcc/vec.h:841
>
> Martin


[-- Attachment #2: gcc-80560.diff --]
[-- Type: text/x-patch, Size: 48972 bytes --]

PR c++/80560 - warn on undefined memory operations involving non-trivial types

gcc/c-family/ChangeLog:

	PR c++/80560
	* c.opt (-Wnon-trivial-memaccess): New option.

gcc/cp/ChangeLog:

	PR c++/80560
	* call.c (first_non_public_field): New function.
	(maybe_warn_nontrivial_memaccess): Same.
	(build_cxx_call): Call it.
	* cp-tree.h (has_trivial_assign, has_trivial_copy): Declare.
	* method.c (has_trivial_assign, has_trivial_copy): Define.	

gcc/ChangeLog:

	PR c++/80560
	* gcc.c (struct compiler): Remove const qualification.
	* genattrtab.c (gen_insn_reserv): Replace memset with initialization.
	* hash-table.h: Ditto.
	* ipa-cp.c (allocate_and_init_ipcp_value): Replace memset with
	  assignment.
	* ipa-prop.c (ipa_free_edge_args_substructures): Ditto.
	* omp-low.c (lower_omp_ordered_clauses): Replace memset with
	default ctor.
	* params.h (struct param_info): Make struct members non-const.
	* tree-switch-conversion.c (emit_case_bit_tests): Replace memset
	with default initialization.
	* vec.h (vec_copy_construct, vec_default_construct): New helper
	functions.
	(vec<T>::copy, vec<T>::splice, vec<T>::reserve): Replace memcpy
	with vec_copy_construct.
	(vect<T>::quick_grow_cleared): Replace memset with default ctor.
	(vect<T>::vec_safe_grow_cleared, vec_safe_grow_cleared): Same.
	* doc/invoke.texi (-Wnon-trivial-memaccess): Document.

libcpp/ChangeLog:

	PR c++/80560
	* line-map.c (line_maps::~line_maps): Avoid calling htab_delete
	with a null pointer.
	(linemap_init): Avoid calling memset on an object of a non-trivial
	type.

libitm/ChangeLog:

	PR c++/80560
	* beginend.cc (GTM::gtm_thread::rollback): Avoid calling memset
	on an object of a non-trivial type.
	(GTM::gtm_transaction_cp::commit): Use assignment instead of memcpy
	to copy an object.
	* method-ml.cc (orec_iterator::reinit): Avoid -Wnon-trivial-memaccess.

gcc/testsuite/ChangeLog:

	PR c++/80560
	* g++.dg/Wnon-trivial-memaccess.C: New test.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 6ecbfca..c32f8a9 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -792,6 +792,10 @@ Wnon-template-friend
 C++ ObjC++ Var(warn_nontemplate_friend) Init(1) Warning
 Warn when non-templatized friend functions are declared within a template.
 
+Wnon-trivial-memaccess
+C++ ObjC++ Var(warn_nontrival_memaccess) Warning LangEnabledBy(C++ ObjC++, Wall)
+Warn for raw memory writes to objects of non-trivial types.
+
 Wnon-virtual-dtor
 C++ ObjC++ Var(warn_nonvdtor) Warning LangEnabledBy(C++ ObjC++,Weffc++)
 Warn about non-virtual destructors.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index c15b8e4..4bf984d 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8152,6 +8152,245 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
   return call;
 }
 
+/* Return the DECL of the first non-public data member of class TYPE
+   or null if none can be found.  */
+
+static tree
+first_non_public_field (tree type)
+{
+  if (!CLASS_TYPE_P (type))
+    return NULL_TREE;
+
+  for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+	continue;
+      if (TREE_STATIC (field))
+	continue;
+      if (TREE_PRIVATE (field) || TREE_PROTECTED (field))
+	return field;
+    }
+
+  return NULL_TREE;
+}
+
+/* Return true if class TYPE meets a relaxed definition of standard-layout.
+   In particular, the requirement that it "has all non-static data members
+   nd bit-fields in the class and its base classes first declared in the
+   same class" is not considered.  */
+
+static bool
+almost_std_layout_p (tree type, tree *field)
+{
+  if (!CLASS_TYPE_P (type))
+    return true;
+
+  if (!trivial_type_p (type))
+    return false;
+
+  if (tree fld = first_non_public_field (type))
+    if (!*field)
+      *field = fld;
+
+  tree binfo, base_binfo;
+
+  int i;
+
+  for (binfo = TYPE_BINFO (type), i = 0;
+       BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+    {
+      tree base = TREE_TYPE (base_binfo);
+
+      if (!almost_std_layout_p (base, field))
+	return false;
+    }
+
+  return true;
+}
+
+/* Issue a warning on a call to the built-in function FNDECL if it is
+   a memory write whose destination is not an object of (something like)
+   trivial or standard layout type.  Detects const correctness violations,
+   corrupting references, virtual table pointers, and bypassing non-
+   trivial assignments.  */
+
+static void
+maybe_warn_nontrivial_memaccess (location_t loc, tree fndecl, tree *args)
+{
+  /* The destination pointer is always first argument for all functions
+     handled here.  */
+  tree dest = args[0];
+  if (!dest || !TREE_TYPE (dest) || !POINTER_TYPE_P (TREE_TYPE (dest)))
+    return;
+
+  STRIP_NOPS (dest);
+
+  tree srctype = NULL_TREE;
+
+  /* Determine the type of the pointed-to object and whether it's a class.  */
+  tree desttype = TREE_TYPE (TREE_TYPE (dest));
+
+  if (!desttype || !COMPLETE_TYPE_P (desttype))
+    return;
+
+  /* Check to see if the memcpy/memset call is made by a ctor or dtor
+     with this as the destination argument for the destination type.
+     If so, be more permissive.  */
+  if (current_function_decl
+      && (DECL_CONSTRUCTOR_P (current_function_decl)
+	  || DECL_DESTRUCTOR_P (current_function_decl))
+      && is_this_parameter (dest))
+    {
+      tree ctx = DECL_CONTEXT (current_function_decl);
+      bool special = same_type_ignoring_top_level_qualifiers_p (ctx, desttype);
+
+      tree binfo = TYPE_BINFO (ctx);
+
+      /* A ctor and dtor for a class with no bases and no virtual functions
+	 can do whatever they want.  Bail early with no further checking.  */
+      if (special && !BINFO_VTABLE (binfo) && !BINFO_N_BASE_BINFOS (binfo))
+	return;
+    }
+
+  tree fld = NULL_TREE;
+  bool stdlayout = almost_std_layout_p (desttype, &fld);
+  bool trivial = trivial_type_p (desttype);
+  bool trivassign = has_trivial_assign (desttype);
+  bool trivcopy = has_trivial_copy (desttype);
+
+  /* Warn about raw memory operations whose destination is an object
+     of a non-trivial type because they are undefined.  */
+
+  const char *warnfmt = NULL;
+  bool warned = false;
+
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    case BUILT_IN_MEMSET:
+      if (!integer_zerop (args[1]))
+	{
+	  /* Diagnose setting non-copy-assignable, non-trivial or non-
+	     standard layout objects (in that order, since the latter
+	     two are subsets of one another), to (potentially) non-zero
+	     bytes.  Since the value of the bytes being written is unknown
+	     suggest using assignment instead (if one exists).  */
+	  if (!trivassign)
+	    warnfmt = G_("%qD writing to an object of type %#qT without "
+			 "trivial copy assignment");
+	  else if (!trivial)
+	    warnfmt = G_("%qD writing to an object of non-trivial "
+			 "type %#qT; use assignment instead");
+	  else if (!stdlayout)
+	    warnfmt = G_("%qD writing to an object of non-standard-layout "
+			 "type %#qT; use assignment instead");
+	  else if (fld)
+	    {
+	      const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	      warned = warning_at (loc, OPT_Wnon_trivial_memaccess,
+				   "%qD writing to an object of type %#qT with "
+				   "%qs member %qD; use assignment instead",
+				   fndecl, desttype, access, fld);
+	    }
+	  break;
+	}
+      /* Fall through.  */
+
+    case BUILT_IN_BZERO:
+      /* Similarly to the above, diagnose clearing non-trivial or non-
+	 standard layout objects, or objects of types with no assignmenmt.  */
+      if (!trivassign)
+	warnfmt = (trivcopy
+		   ? G_("%qD clearing an object of type %#qT without trivial "
+			"copy assignment; use copy constructor instead")
+		   : G_("%qD clearing an object of type %#qT without trivial "
+			"copy assignment"));
+      else if (!trivial)
+	warnfmt = G_("%qD clearing an object of non-trivial type "
+		     "%#qT; use assignment or default initialization instead");
+      else if (!stdlayout)
+	warnfmt = G_("%qD clearing an object of non-standard-layout type "
+		     "%#qT; use assignment or default initialization instead");
+      else if (fld)
+	{
+	  const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	  warned = warning_at (loc, OPT_Wnon_trivial_memaccess,
+			       "%qD clearing an object of type %#qT with "
+			       "%qs member %qD; use assignment or default "
+			       "initialization instead",
+			       fndecl, desttype, access, fld);
+	}
+      break;
+
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMPCPY:
+      /* Determine the type of the source object.  */
+      srctype = (TREE_CODE (args[1]) == NOP_EXPR
+		 ? TREE_OPERAND (args[1], 0) : args[1]);
+
+      srctype = TREE_TYPE (TREE_TYPE (srctype));
+
+      if (!trivassign)
+	warnfmt = (trivcopy
+		   ? G_("%qD copying to an object of type %#qT without trivial "
+			"copy assignment; use copy constructor instead")
+		   :  G_("%qD copying to an object of type %#qT without trivial "
+			 "copy assignment"));
+      else if (!trivially_copyable_p (desttype))
+	warnfmt = G_("%qD copying to an object of non-trivially-copyable type "
+		     "%#qT; use assignment or copy initialization instead");
+      else if (!trivial
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  /* Warn when copying into a non-trivial object from an object
+	     of a different type other than void or char.  */
+	  warned = warning_at (loc, OPT_Wnon_trivial_memaccess,
+			       "%qD copying an object of non-trivial type "
+			       "%#qT from an array of %#qT",
+			       fndecl, desttype, srctype);
+	}
+      else if (fld)
+	{
+	  const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	  warned = warning_at (loc, OPT_Wnon_trivial_memaccess,
+			       "%qD clearing an object of type %#qT with "
+			       "%qs member %qD; use assignment or default "
+			       "initialization instead",
+			       fndecl, desttype, access, fld);
+	}
+      else if (!trivial && TREE_CODE (args[2]) == INTEGER_CST)
+	{
+	  /* Finally, warn on partial copies.  */
+	  unsigned HOST_WIDE_INT typesize
+	    = tree_to_uhwi (TYPE_SIZE_UNIT (desttype));
+	  if (unsigned HOST_WIDE_INT nbytes = tree_to_uhwi (args[2]) % typesize)
+	    warned = warning_at (loc, OPT_Wnon_trivial_memaccess,
+				 (nbytes > 1
+				  ? G_("%qD copying to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "byte unchanged")
+				  : G_("%qD copying to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "bytes unchanged")),
+				 fndecl, desttype, nbytes);
+	}
+      break;
+
+    default:
+      return;
+    }
+
+  if (!warned && !warnfmt)
+    return;
+
+  if (warned
+      || warning_at (loc, OPT_Wnon_trivial_memaccess, warnfmt, fndecl, desttype))
+    inform (location_of (desttype), "%#qT declared here", desttype);
+}
+
 /* Build and return a call to FN, using NARGS arguments in ARGARRAY.
    This function performs no overload resolution, conversion, or other
    high-level operations.  */
@@ -8184,6 +8423,10 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
       if (!check_builtin_function_arguments (EXPR_LOCATION (fn), vNULL, fndecl,
 					     nargs, argarray))
 	return error_mark_node;
+
+      /* Warn if the built-in writes to an object of a non-trivial type.  */
+      if (nargs)
+	maybe_warn_nontrivial_memaccess (loc, fndecl, argarray);
     }
 
     /* If it is a built-in array notation function, then the return type of
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 3bbc0bf..b7ca930 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6119,6 +6119,9 @@ extern bool ctor_omit_inherited_parms		(tree);
 extern tree locate_ctor				(tree);
 extern tree implicitly_declare_fn               (special_function_kind, tree,
 						 bool, tree, tree);
+extern bool has_trivial_assign                  (tree);
+extern bool has_trivial_copy                    (tree);
+
 
 /* In optimize.c */
 extern bool maybe_clone_body			(tree);
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index b4c1f60..810119e 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -1716,6 +1716,44 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
   --c_inhibit_evaluation_warnings;
 }
 
+/* Return true if the class type CTYPE has non-deleted trivial copy
+   assignment.  */
+
+bool
+has_trivial_assign (tree ctype)
+{
+  if (!CLASS_TYPE_P (ctype))
+    return true;
+
+  bool trivial_p = false;
+  bool deleted_p = false;
+
+  synthesized_method_walk (ctype, sfk_copy_assignment, false, NULL,
+			   &trivial_p, &deleted_p,
+			   NULL, false, NULL_TREE, NULL_TREE);
+
+  return trivial_p && !deleted_p;
+}
+
+/* Return true if the class type CTYPE has non-deleted trivial copy
+   constructor.  */
+
+bool
+has_trivial_copy (tree ctype)
+{
+  if (!CLASS_TYPE_P (ctype))
+    return true;
+
+  bool trivial_p = false;
+  bool deleted_p = false;
+
+  synthesized_method_walk (ctype, sfk_copy_constructor, false, NULL,
+			   &trivial_p, &deleted_p,
+			   NULL, false, NULL_TREE, NULL_TREE);
+
+  return trivial_p && !deleted_p;
+}
+
 /* DECL is a defaulted function whose exception specification is now
    needed.  Return what it should be.  */
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 0eeea7b..9ec53a3 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -215,7 +215,8 @@ in the following sections.
 -Wabi=@var{n}  -Wabi-tag  -Wconversion-null  -Wctor-dtor-privacy @gol
 -Wdelete-non-virtual-dtor  -Wliteral-suffix  -Wmultiple-inheritance @gol
 -Wnamespaces  -Wnarrowing @gol
--Wnoexcept  -Wnoexcept-type  -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
+-Wnoexcept  -Wnoexcept-type  -Wnon-trivial-memaccess @gol
+-Wnon-virtual-dtor  -Wreorder  -Wregister @gol
 -Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
 -Wno-non-template-friend  -Wold-style-cast @gol
 -Woverloaded-virtual  -Wno-pmf-conversions @gol
@@ -2911,6 +2912,24 @@ void g() noexcept;
 void h() @{ f(g); @} // in C++14 calls f<void(*)()>, in C++1z calls f<void(*)()noexcept>
 @end smallexample
 
+@item -Wnon-trivial-memaccess @r{(C++ and Objective-C++ only)}
+@opindex Wnon-trivial-memaccess
+Warn when the destination of a call to a raw memory function such as
+@code{memset} or @code{memcpy} is an object of class type where doing
+so might bypass the class non-trivial constructor or copy assignment,
+violate const-correctness or encapsulation, or corrupt the virtual
+table.  Modifying the representation of such objects may violate
+invariants maintained by member functions of the class.
+For example, the call to @code{memset} below is undefined becase it
+modifies a non-trivial class object and is, therefore, diagnosed.
+The safe way to either initialize or clear the storage of objects of
+non-trivial types is by using the appropriate constructor.
+@smallexample
+std::string str = "abc";
+memset (&str, 0, 3);
+@end smallexample
+The option does not warn on safe calls to such functions 
+The @option{-Wnon-trivial-memaccess} option is enabled by @option{-Wall}.
 
 @item -Wnon-virtual-dtor @r{(C++ and Objective-C++ only)}
 @opindex Wnon-virtual-dtor
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 120c5c0..2872b2a 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -1259,9 +1259,9 @@ struct compiler
   const char *cpp_spec;         /* If non-NULL, substitute this spec
                                   for `%C', rather than the usual
                                   cpp_spec.  */
-  const int combinable;          /* If nonzero, compiler can deal with
+  int combinable;               /* If nonzero, compiler can deal with
                                    multiple source files at once (IMA).  */
-  const int needs_preprocessing; /* If nonzero, source files need to
+  int needs_preprocessing;      /* If nonzero, source files need to
                                    be run through a preprocessor.  */
 };
 
diff --git a/gcc/genattrtab.c b/gcc/genattrtab.c
index 3629b5f..51dfe77 100644
--- a/gcc/genattrtab.c
+++ b/gcc/genattrtab.c
@@ -4703,8 +4703,8 @@ gen_insn_reserv (md_rtx_info *info)
   struct insn_reserv *decl = oballoc (struct insn_reserv);
   rtx def = info->def;
 
-  struct attr_desc attr;
-  memset (&attr, 0, sizeof (attr));
+  struct attr_desc attr = { };
+
   attr.name = DEF_ATTR_STRING (XSTR (def, 0));
   attr.loc = info->loc;
 
diff --git a/gcc/hash-table.h b/gcc/hash-table.h
index 0f7e21a..443d16c 100644
--- a/gcc/hash-table.h
+++ b/gcc/hash-table.h
@@ -803,7 +803,10 @@ hash_table<Descriptor, Allocator>::empty_slow ()
       m_size_prime_index = nindex;
     }
   else
-    memset (entries, 0, size * sizeof (value_type));
+    {
+      for ( ; size; ++entries, --size)
+	*entries = value_type ();
+    }
   m_n_deleted = 0;
   m_n_elements = 0;
 }
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 26ae8fc..f232d57 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1472,8 +1472,7 @@ allocate_and_init_ipcp_value (tree source)
 {
   ipcp_value<tree> *val;
 
-  val = ipcp_cst_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_cst_values_pool.allocate ()) ipcp_value<tree>();
   val->value = source;
   return val;
 }
@@ -1487,8 +1486,8 @@ allocate_and_init_ipcp_value (ipa_polymorphic_call_context source)
   ipcp_value<ipa_polymorphic_call_context> *val;
 
   // TODO
-  val = ipcp_poly_ctx_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_poly_ctx_values_pool.allocate ())
+    ipcp_value<ipa_polymorphic_call_context>();
   val->value = source;
   return val;
 }
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index b3d5159..8c912b4 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -3731,7 +3731,7 @@ void
 ipa_free_edge_args_substructures (struct ipa_edge_args *args)
 {
   vec_free (args->jump_functions);
-  memset (args, 0, sizeof (*args));
+  *args = ipa_edge_args ();
 }
 
 /* Free all ipa_edge structures.  */
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 9cc2996..dc93dce 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -6292,7 +6292,11 @@ lower_omp_ordered_clauses (gimple_stmt_iterator *gsi_p, gomp_ordered *ord_stmt,
     return;
 
   wide_int *folded_deps = XALLOCAVEC (wide_int, 2 * len - 1);
-  memset (folded_deps, 0, sizeof (*folded_deps) * (2 * len - 1));
+
+  /* wide_int is not a POD so it must be default-constructed.  */
+  for (unsigned i = 0; i != 2 * len - 1; ++i)
+    new (static_cast<void*>(folded_deps + i)) wide_int ();
+
   tree folded_dep = NULL_TREE;
   /* TRUE if the first dimension's offset is negative.  */
   bool neg_offset_p = false;
diff --git a/gcc/params.h b/gcc/params.h
index b61cff9..8b91660 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -42,7 +42,7 @@ struct param_info
 {
   /* The name used with the `--param <name>=<value>' switch to set this
      value.  */
-  const char *const option;
+  const char *option;
 
   /* The default value.  */
   int default_value;
@@ -54,7 +54,7 @@ struct param_info
   int max_value;
 
   /* A short description of the option.  */
-  const char *const help;
+  const char *help;
 
   /* The optional names corresponding to the values.  */
   const char **value_names;
diff --git a/gcc/testsuite/g++.dg/Wnon-trivial-memaccess.C b/gcc/testsuite/g++.dg/Wnon-trivial-memaccess.C
new file mode 100644
index 0000000..dc93c9b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wnon-trivial-memaccess.C
@@ -0,0 +1,571 @@
+/* PR c++/80560 - warn on undefined memory operations involving non-trivial
+   types
+   { dg-do compile }
+   { dg-options "-Wnon-trivial-memaccess -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern "C"
+{
+void bzero (void*, size_t);
+void* memcpy (void*, const void*, size_t);
+void* memmove (void*, const void*, size_t);
+void* mempcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+}
+
+/* Trivial can be manipulated by raw memory functions.  */
+struct Trivial { int i; char *s; char a[4]; };
+
+/* HasDefaultCtor is trivially copyable but should be initialized by
+   the ctor, not bzero or memset.  */
+struct HasDefaultCtor { HasDefaultCtor (); };
+
+/* HasCopyCtor should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  Since it's non-trivial, i should not be zeroed
+   out by bzero/memset either and should instead use assignment and/or
+   default initialization.  */
+struct HasCopyCtor { HasCopyCtor (const HasCopyCtor&); };
+
+/* HasDtor should be initialized using aggregate or memberwise intialization,
+   not bzero or memset.  */
+struct HasDtor { ~HasDtor (); };
+
+/* HasAssign should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  */
+struct HasAssign { void operator= (HasAssign&); };
+
+/* HasVirtuals should only be manipulated by the special member functions
+   and not by bzero, memcpy, or any other raw memory function. Doing
+   otherwse might corrupt the the vtable pointer.  */
+struct HasVirtuals { virtual void foo (); };
+
+/* HasConstData should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable allowing, raw memory functions to write into it would defeat
+   const-correctness.  */
+struct HasConstData { const int ci; };
+
+/* HasReference should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable, allowing raw memory functions to write into it could
+   corrupt the reference.  */
+struct HasReference { int &ci; };
+
+/* HasSomePrivateDate and HasSomeProtectedData should only be initialized
+   using aggregate initializatoon and not cleared by bzero, or copied into
+   using memcpy.  Doing otherwise would break encapsulation.  */
+struct HasSomePrivateData { int i; private: int j; };
+struct HasSomeProtectedData { int i; protected: int j; };
+
+/* Similarly to the above, HasAllPrivateDate and HasAllProtectedData should
+   only be initialized using aggregate initializatoon and not cleared by
+   bzero, or copied into using memcpy.  They are tested separately because
+   unlike the former classes, these are standard layout.  */
+struct HasAllPrivateData { private: int i; };
+struct HasAllProtectedData { protected: int i; };
+
+void sink (void*);
+
+#define T(fn, arglist) (fn arglist, sink (p))
+
+#if !defined TEST || TEST == TEST_TRIVIAL
+
+void test (Trivial *p, void *q, int x)
+{
+  T (bzero, (p, sizeof *p));
+  T (bzero, (q, sizeof *p));
+
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (q, p, sizeof *p));
+
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (q, 0, sizeof *p));
+
+  T (memset, (p, x, sizeof *p));
+  T (memset, (q, x, sizeof *p));
+
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (q, p, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DEFAULT_CTOR
+
+void test (HasDefaultCtor *p, const HasDefaultCtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // HasDefaultCtor is neither trivial nor standard-layout.  The warning
+  // should mention the former since it's more permissive than the latter
+  // and so more informative.
+  T (bzero, (p, sizeof *p));   // { dg-warning ".void bzero(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefaultCtor.; use assignment or default initialization instead" }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefaultCtor.; use assignment or default initialization instead" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefaultCtor.; use assignment instead" }
+
+
+  // Copying from another object of the same type is fine.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, sizeof *p * i));
+
+  // Copying from a void* or character buffer is also fine.
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, sizeof *p * i));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, sizeof *p * i));
+
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, q, sizeof *p * i));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, s, sizeof *p * i));
+
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p * i));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p * i));
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefaultCtor. from an array of .const int." }
+
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning ".void\\* memmove(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefaultCtor. from an array of .const int." }
+
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning ".void\\* mempcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefaultCtor. from an array of .const int." }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_COPY_CTOR
+
+void test (HasCopyCtor *p, const HasCopyCtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed because default initialization is
+  // invalid (the copy ctor makes no default ctor unavailable).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DTOR
+
+void test (HasDtor *p, const HasDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed only because it's difficult not to.
+  // Otherwise, a class that's non-trivial only because it has
+  // a non-trivial dtor can be safely zeroed out (that's what
+  // default-initializing it does).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed simply because
+  // a class with a user-defined dtor is not trivially copyable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ASSIGN
+
+void test (HasAssign *p, const HasAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_VIRTUALS
+
+void test (HasVirtuals *p, const HasVirtuals &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed because it corrupts the vtable.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is diagnosed because when used to initialize an object
+  // could incorrectly initialize the vtable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_CONST_DATA
+
+void test (HasConstData *p, const HasConstData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // The following is ill-formed because HasConstData's cannot
+  // be assigned (the assignment is implicitly deleted).  For
+  // that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object could break const correctness.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasConstData. without trivial copy assignment" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "copying to an object of type .struct HasConstData. without trivial copy assignment" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_REFERENCE
+
+void test (HasReference *p, const HasReference &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Similarly to HasConstData, the following is ill-formed because
+  // Hasreference cannot be assigned (the assignment is implicitly
+  // deleted).  For that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object would invalidate the reference.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasReference. without trivial copy assignment" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "copying to an object of type .struct HasReference. without trivial copy assignment" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PRIVATE_DATA
+
+void test (HasSomePrivateData *p, const HasSomePrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // All operations are diagnosed becauce they break encapsulation.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PROTECTED_DATA
+
+void test (HasSomeProtectedData *p, const HasSomeProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // All operations are diagnosed becauce they break encapsulation.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PRIVATE_DATA
+
+void test (HasAllPrivateData *p, const HasAllPrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // All operations are diagnosed becauce they break encapsulation.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PROTECTED_DATA
+
+void test (HasAllProtectedData *p, const HasAllProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // All operations are diagnosed becauce they break encapsulation.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_EXPRESSION
+
+void test_expr (int i)
+{
+  struct TestClass: HasDefaultCtor { };
+  TestClass a, b;
+
+  static void *p;
+
+  T (bzero, (i < 0 ? &a : &b, 1));  // { dg-warning ".void bzero" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_CTOR
+
+void test_ctor ()
+{
+#undef T
+#define T(fn, arglist) (fn arglist, sink (this))
+
+  static void *p;
+
+  struct TestBase
+  {
+    TestBase ()
+    {
+      /* A ctor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+
+    ~TestBase ()
+    {
+      /* A dtor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestBaseVtable
+  {
+    TestBaseVtable ()
+    {
+      /* A ctor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    ~TestBaseVtable ()
+    {
+      /* A dtor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    virtual void foo ();
+  };
+
+  struct TestDerived: HasDefaultCtor
+  {
+    TestDerived ()
+    {
+      /* A derived class ctor is treated as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestDerivedDtor: HasDefaultCtor
+  {
+    ~TestDerivedDtor ()
+    {
+      /* A derived class dtor is treated as an ordinary function though
+	 it probably shouldn't be unless the base dtor is trivial.  But
+	 it doesn't seem worth the trouble.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+  };
+}
+
+#endif
diff --git a/gcc/tree-switch-conversion.c b/gcc/tree-switch-conversion.c
index 1ccb4bd..d8b1c67 100644
--- a/gcc/tree-switch-conversion.c
+++ b/gcc/tree-switch-conversion.c
@@ -266,7 +266,7 @@ static void
 emit_case_bit_tests (gswitch *swtch, tree index_expr,
 		     tree minval, tree range, tree maxval)
 {
-  struct case_bit_test test[MAX_CASE_BIT_TESTS];
+  struct case_bit_test test[MAX_CASE_BIT_TESTS] = { };
   unsigned int i, j, k;
   unsigned int count;
 
@@ -291,8 +291,6 @@ emit_case_bit_tests (gswitch *swtch, tree index_expr,
   int prec = TYPE_PRECISION (word_type_node);
   wide_int wone = wi::one (prec);
 
-  memset (&test, 0, sizeof (test));
-
   /* Get the edge for the default case.  */
   tmp = gimple_switch_default_label (swtch);
   default_bb = label_to_block (CASE_LABEL (tmp));
diff --git a/gcc/vec.h b/gcc/vec.h
index fee4616..0e9a13b 100644
--- a/gcc/vec.h
+++ b/gcc/vec.h
@@ -407,6 +407,26 @@ struct GTY((user)) vec
 {
 };
 
+/* Default-construct N elements in DST.  */
+
+template <typename T>
+inline void
+vec_default_construct (T *dst, unsigned n)
+{
+  for ( ; n; ++dst, --n)
+    ::new (static_cast<void*>(dst)) T ();
+}
+
+/* Copy-construct N elements in DST from *SRC.  */
+
+template <typename T>
+inline void
+vec_copy_construct (T *dst, const T *src, unsigned n)
+{
+  for ( ; n; ++dst, ++src, --n)
+    ::new (static_cast<void*>(dst)) T (*src);
+}
+
 /* Type to provide NULL values for vec<T, A, L>.  This is used to
    provide nil initializers for vec instances.  Since vec must be
    a POD, we cannot have proper ctor/dtor for it.  To initialize
@@ -615,7 +635,7 @@ vec_safe_grow_cleared (vec<T, A, vl_embed> *&v, unsigned len CXX_MEM_STAT_INFO)
 {
   unsigned oldlen = vec_safe_length (v);
   vec_safe_grow (v, len PASS_MEM_STAT);
-  memset (&(v->address ()[oldlen]), 0, sizeof (T) * (len - oldlen));
+  vec_default_construct (v->address () + oldlen, len - oldlen);
 }
 
 
@@ -821,7 +841,7 @@ vec<T, A, vl_embed>::copy (ALONE_MEM_STAT_DECL) const
     {
       vec_alloc (new_vec, len PASS_MEM_STAT);
       new_vec->embedded_init (len, len);
-      memcpy (new_vec->address (), m_vecdata, sizeof (T) * len);
+      vec_copy_construct (new_vec->address (), m_vecdata, len);
     }
   return new_vec;
 }
@@ -838,7 +858,7 @@ vec<T, A, vl_embed>::splice (const vec<T, A, vl_embed> &src)
   if (len)
     {
       gcc_checking_assert (space (len));
-      memcpy (address () + length (), src.address (), len * sizeof (T));
+      vec_copy_construct (end (), src.address (), len);
       m_vecpfx.m_num += len;
     }
 }
@@ -1092,13 +1112,12 @@ inline void
 vec<T, A, vl_embed>::quick_grow_cleared (unsigned len)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   quick_grow (len);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
-
 /* Garbage collection support for vec<T, A, vl_embed>.  */
 
 template<typename T>
@@ -1445,7 +1464,7 @@ vec<T, va_heap, vl_ptr>::reserve (unsigned nelems, bool exact MEM_STAT_DECL)
   va_heap::reserve (m_vec, nelems, exact PASS_MEM_STAT);
   if (handle_auto_vec)
     {
-      memcpy (m_vec->address (), oldvec->address (), sizeof (T) * oldsize);
+      vec_copy_construct (m_vec->address (), oldvec->address (), oldsize);
       m_vec->m_vecpfx.m_num = oldsize;
     }
 
@@ -1607,10 +1626,10 @@ inline void
 vec<T, va_heap, vl_ptr>::safe_grow_cleared (unsigned len MEM_STAT_DECL)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   safe_grow (len PASS_MEM_STAT);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
 
diff --git a/libcpp/line-map.c b/libcpp/line-map.c
index 949489e..4e36e38 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -62,7 +62,8 @@ extern unsigned num_macro_tokens_counter;
 
 line_maps::~line_maps ()
 {
-  htab_delete (location_adhoc_data_map.htab);
+  if (location_adhoc_data_map.htab)
+    htab_delete (location_adhoc_data_map.htab);
 }
 
 /* Hash function for location_adhoc_data hashtable.  */
@@ -347,7 +348,7 @@ void
 linemap_init (struct line_maps *set,
 	      source_location builtin_location)
 {
-  memset (set, 0, sizeof (struct line_maps));
+  *set = line_maps ();
   set->highest_location = RESERVED_LOCATION_COUNT - 1;
   set->highest_line = RESERVED_LOCATION_COUNT - 1;
   set->location_adhoc_data_map.htab =
diff --git a/libitm/beginend.cc b/libitm/beginend.cc
index d04f3e9..c6550a3 100644
--- a/libitm/beginend.cc
+++ b/libitm/beginend.cc
@@ -431,7 +431,7 @@ GTM::gtm_transaction_cp::save(gtm_thread* tx)
   // Save everything that we might have to restore on restarts or aborts.
   jb = tx->jb;
   undolog_size = tx->undolog.size();
-  memcpy(&alloc_actions, &tx->alloc_actions, sizeof(alloc_actions));
+  alloc_actions = tx->alloc_actions;
   user_actions_size = tx->user_actions.size();
   id = tx->id;
   prop = tx->prop;
@@ -449,7 +449,7 @@ GTM::gtm_transaction_cp::commit(gtm_thread* tx)
   // commits of nested transactions. Allocation actions must be committed
   // before committing the snapshot.
   tx->jb = jb;
-  memcpy(&tx->alloc_actions, &alloc_actions, sizeof(alloc_actions));
+  tx->alloc_actions = alloc_actions;
   tx->id = id;
   tx->prop = prop;
 }
@@ -485,7 +485,7 @@ GTM::gtm_thread::rollback (gtm_transaction_cp *cp, bool aborting)
       prop = cp->prop;
       if (cp->disp != abi_disp())
 	set_abi_disp(cp->disp);
-      memcpy(&alloc_actions, &cp->alloc_actions, sizeof(alloc_actions));
+      alloc_actions = cp->alloc_actions;
       nesting = cp->nesting;
     }
   else
diff --git a/libitm/method-ml.cc b/libitm/method-ml.cc
index fcae334..b857bff 100644
--- a/libitm/method-ml.cc
+++ b/libitm/method-ml.cc
@@ -138,7 +138,11 @@ struct ml_mg : public method_group
     // This store is only executed while holding the serial lock, so relaxed
     // memory order is sufficient here.  Same holds for the memset.
     time.store(0, memory_order_relaxed);
-    memset(orecs, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
+    // The memset below isn't strictly kosher because it bypasses
+    // the non-trivial assignment operator defined by std::atomic.  Using
+    // a local void* is enough to prevent GCC from warning for this.
+    void *p = orecs;
+    memset(p, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
   }
 };
 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-11 16:34   ` Martin Sebor
  2017-05-11 16:57     ` Jakub Jelinek
@ 2017-05-16 19:46     ` Jason Merrill
  2017-05-16 22:28       ` Martin Sebor
  2017-05-17  1:01       ` Pedro Alves
  1 sibling, 2 replies; 46+ messages in thread
From: Jason Merrill @ 2017-05-16 19:46 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Pedro Alves, Gcc Patch List

On Thu, May 11, 2017 at 12:23 PM, Martin Sebor <msebor@gmail.com> wrote:
> The challenge with using memcpy or memset with class types is
> figuring out if it's being called to copy-construct a new object
> or assign a new value to an existing one.  In general it's not
> possible to tell so the conservative assumption is that it's
> the latter.
>
> Because of that, relying on the trivially copyable property to
> determine whether it's safe to assign a new value to an object
> is not sufficient (or even necessary).  The class must be at
> a minimum trivially assignable.  But it turns out that even
> trivial assignability as defined by C++ isn't enough.  A class
> with a const data member or a member of a reference type is
> considered "trivially assignable" but its copy assignment is
> deleted, and so it can't be assigned to.   Calling memset or
> memcpy to write over an object of such a class can violate
> the const invariant or corrupt the reference, respectively.

Yes, "trivially copyable" elides the differences between
initialization and assignment context.  I agree that it makes sense to
check for a trivial assignment operator specifically.  I guess we want
a slightly stronger "trivially copyable" that also requires a
non-deleted assignment operator.

It seems to me that the relevant tests are:

bcopy/memcpy/memmove want trivally copyable + non-deleted assignment.
bzero/memset want trivial + non-deleted assignment.

I'm still not convinced we need to consider standard-layout at all.

Jason

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-16 19:46     ` Jason Merrill
@ 2017-05-16 22:28       ` Martin Sebor
  2017-05-19 19:14         ` Jason Merrill
  2017-05-17  1:01       ` Pedro Alves
  1 sibling, 1 reply; 46+ messages in thread
From: Martin Sebor @ 2017-05-16 22:28 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Pedro Alves, Gcc Patch List

On 05/16/2017 01:41 PM, Jason Merrill wrote:
> On Thu, May 11, 2017 at 12:23 PM, Martin Sebor <msebor@gmail.com> wrote:
>> The challenge with using memcpy or memset with class types is
>> figuring out if it's being called to copy-construct a new object
>> or assign a new value to an existing one.  In general it's not
>> possible to tell so the conservative assumption is that it's
>> the latter.
>>
>> Because of that, relying on the trivially copyable property to
>> determine whether it's safe to assign a new value to an object
>> is not sufficient (or even necessary).  The class must be at
>> a minimum trivially assignable.  But it turns out that even
>> trivial assignability as defined by C++ isn't enough.  A class
>> with a const data member or a member of a reference type is
>> considered "trivially assignable" but its copy assignment is
>> deleted, and so it can't be assigned to.   Calling memset or
>> memcpy to write over an object of such a class can violate
>> the const invariant or corrupt the reference, respectively.
>
> Yes, "trivially copyable" elides the differences between
> initialization and assignment context.  I agree that it makes sense to
> check for a trivial assignment operator specifically.  I guess we want
> a slightly stronger "trivially copyable" that also requires a
> non-deleted assignment operator.
>
> It seems to me that the relevant tests are:
>
> bcopy/memcpy/memmove want trivally copyable + non-deleted assignment.
> bzero/memset want trivial + non-deleted assignment.

I think that's very close to what the patch does.

bzero/memset: expects a trivial class with no private
members and with a non-deleted trivial assignment.  That looks
the same as what you have above.

bcopy/memcpy/memmove: expects the same plus trivially copyable,
except that the class may be non-trivial if the source of the
copy is any of void*, [signed or unsigned] char*, or a pointer
to the class itself.  In that case, the byte size must either
be non-constant or a multiple of the size of the object, to
help detect inadvertent partial copies.

The test for class triviality is to detect misusing the functions
to initialize (construct) an object of a class with a user-defined
default ctor by copying bytes into it from an object of unrelated
type (void* and char* are allowed for serialization).

I did forget about bcopy.  Let me add it.

>
> I'm still not convinced we need to consider standard-layout at all.

I agree.  The patch doesn't make use of is_standard_layout_p().
It defines its own helper called almost_std_layout_p() that
combines trivial_type_p() with tests for non-public members,
ignoring the requirement that all members of a derived standard
layout class must be defined in the same base (or derived) class.

Is there something you'd like to see done differently in the
latest revision of the patch?

https://gcc.gnu.org/ml/gcc-patches/2017-05/msg00976.html

Martin

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-16 19:46     ` Jason Merrill
  2017-05-16 22:28       ` Martin Sebor
@ 2017-05-17  1:01       ` Pedro Alves
  2017-05-17  1:57         ` Martin Sebor
  1 sibling, 1 reply; 46+ messages in thread
From: Pedro Alves @ 2017-05-17  1:01 UTC (permalink / raw)
  To: Jason Merrill, Martin Sebor; +Cc: Gcc Patch List

On 05/16/2017 08:41 PM, Jason Merrill wrote:

> I agree that it makes sense to
> check for a trivial assignment operator specifically.  I guess we want
> a slightly stronger "trivially copyable" that also requires a
> non-deleted assignment operator.
> 
> It seems to me that the relevant tests are:
> 
> bcopy/memcpy/memmove want trivally copyable + non-deleted assignment.
> bzero/memset want trivial + non-deleted assignment.
> 
> I'm still not convinced we need to consider standard-layout at all.

What do you think of warning for memset of types with references?  Since NULL
references are undefined behavior (N4659, [dcl.ref]/5), IMHO such code is quite
smelly and most likely a sign of incomplete refactoring, not design.

(My original intention for poisoning memset of standard-layout type in gdb was
mainly as proxy/approximation for "type with references".  Other properties
that make a type non-standard-layout are not as interesting to me.)

While at it, maybe the same reasoning would justify warn of memcpy/memset
of types with const data members?  memcpy of such types kind of sounds like a
recipe for subtle breakage that could only be salvaged with std::launder.
And if you know about that, you'll probably be copying objects of type T
to/from raw byte/char storage, not to/from another T.

Actually, now that I look at Martin's new patch, I see he's already warning
about const data members and references, both memcpy and memset.  I'm not
super convinced on warning about memcpy of references (unlike memset), but
I don't feel strongly against it either.  I'd be fine (FWIW) with giving it a
try and see what breaks out there.

> +Wnon-trivial-memaccess
> +C++ ObjC++ Var(warn_nontrival_memaccess) Warning LangEnabledBy(C++ ObjC++, Wall)
> +Warn for raw memory writes to objects of non-trivial types.

May I suggest renaming the warning (and the description above) from
-Wnon-trivial-memaccess to something else that avoids "trivial" in
its name?  Because it's no longer strictly about "trivial" in the
standard C++ sense.  The documentation talks about "The safe way",
and "does not warn on safe calls", so maybe call it -Wunsafe-memaccess?

(I spotted a couple typos in the new patch: "otherwse", "becase", btw.)

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-17  1:01       ` Pedro Alves
@ 2017-05-17  1:57         ` Martin Sebor
  2017-05-17 11:23           ` Pedro Alves
  0 siblings, 1 reply; 46+ messages in thread
From: Martin Sebor @ 2017-05-17  1:57 UTC (permalink / raw)
  To: Pedro Alves, Jason Merrill; +Cc: Gcc Patch List

On 05/16/2017 04:48 PM, Pedro Alves wrote:
> On 05/16/2017 08:41 PM, Jason Merrill wrote:
>
>> I agree that it makes sense to
>> check for a trivial assignment operator specifically.  I guess we want
>> a slightly stronger "trivially copyable" that also requires a
>> non-deleted assignment operator.
>>
>> It seems to me that the relevant tests are:
>>
>> bcopy/memcpy/memmove want trivally copyable + non-deleted assignment.
>> bzero/memset want trivial + non-deleted assignment.
>>
>> I'm still not convinced we need to consider standard-layout at all.
>
> What do you think of warning for memset of types with references?  Since NULL
> references are undefined behavior (N4659, [dcl.ref]/5), IMHO such code is quite
> smelly and most likely a sign of incomplete refactoring, not design.
>
> (My original intention for poisoning memset of standard-layout type in gdb was
> mainly as proxy/approximation for "type with references".  Other properties
> that make a type non-standard-layout are not as interesting to me.)
>
> While at it, maybe the same reasoning would justify warn of memcpy/memset
> of types with const data members?  memcpy of such types kind of sounds like a
> recipe for subtle breakage that could only be salvaged with std::launder.
> And if you know about that, you'll probably be copying objects of type T
> to/from raw byte/char storage, not to/from another T.
>
> Actually, now that I look at Martin's new patch, I see he's already warning
> about const data members and references, both memcpy and memset.  I'm not
> super convinced on warning about memcpy of references (unlike memset), but
> I don't feel strongly against it either.  I'd be fine (FWIW) with giving it a
> try and see what breaks out there.

I did this because objects with references cannot be assigned
to (the default copy assignment is deleted).  So as a baseline
rule, I made the warning trigger whenever a native assignment
or copy isn't valid.  In the IMO unlikely event that a memcpy
over a reference is intended, the warning is easy to suppress.

I expect calling memset on an object with a reference to almost
certainly be a bug since there's no way to make such a reference
usable.  The only time it might be intentional is when someone
tries to wipe out the storage before deleting the object in
the storage (e.g., in a dtor).  But that's a misuse as well
because such calls are typically eliminated, much to many
a security analyst's shock and horror.  I'd like to eventually
diagnose that too (though possibly under a different warning).

I used a similar (though not exactly the same) rationale for
warning for const members.  They too cannot be assigned to,
and letting memset or memcpy silently change them violates
const-correctnes.  It's also undefined and the immutability
of such members an optimization opportunity waiting to be
exploited.

>> +Wnon-trivial-memaccess
>> +C++ ObjC++ Var(warn_nontrival_memaccess) Warning LangEnabledBy(C++ ObjC++, Wall)
>> +Warn for raw memory writes to objects of non-trivial types.
>
> May I suggest renaming the warning (and the description above) from
> -Wnon-trivial-memaccess to something else that avoids "trivial" in
> its name?  Because it's no longer strictly about "trivial" in the
> standard C++ sense.  The documentation talks about "The safe way",
> and "does not warn on safe calls", so maybe call it -Wunsafe-memaccess?

I debated whether to rename things (not just the warning but
also the function that implements it in GCC).  Ultimately I
decided it wasn't necessary because the rules seem close enough
to be based on some notion of triviality and because no better
name came to mind. -Wunsafe-memaccess might work.  The one mild
concern I have with it is that it could suggest it might do more
than simple type checking (e.g., buffer overflow and what not).
Let's see what Jason thinks.

> (I spotted a couple typos in the new patch: "otherwse", "becase", btw.)

I'm a horrible typist.  I'll proofread the patch again and fix
them up before committing it.

Martin

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-17  1:57         ` Martin Sebor
@ 2017-05-17 11:23           ` Pedro Alves
  0 siblings, 0 replies; 46+ messages in thread
From: Pedro Alves @ 2017-05-17 11:23 UTC (permalink / raw)
  To: Martin Sebor, Jason Merrill; +Cc: Gcc Patch List

On 05/17/2017 02:55 AM, Martin Sebor wrote:
> On 05/16/2017 04:48 PM, Pedro Alves wrote:
>> On 05/16/2017 08:41 PM, Jason Merrill wrote:
>>
>>> I agree that it makes sense to
>>> check for a trivial assignment operator specifically.  I guess we want
>>> a slightly stronger "trivially copyable" that also requires a
>>> non-deleted assignment operator.
>>>
>>> It seems to me that the relevant tests are:
>>>
>>> bcopy/memcpy/memmove want trivally copyable + non-deleted assignment.
>>> bzero/memset want trivial + non-deleted assignment.
>>>
>>> I'm still not convinced we need to consider standard-layout at all.
>>
>> What do you think of warning for memset of types with references? 

Having slept, I now realize you had that covered already by the
"non-deleted assignment" requirement...  A reference data member
makes the assignment operator be implicitly deleted.  Sorry for the noise.

>> While at it, maybe the same reasoning would justify warn of memcpy/memset
>> of types with const data members?  

Ditto.

> I did this because objects with references cannot be assigned
> to (the default copy assignment is deleted).  So as a baseline
> rule, I made the warning trigger whenever a native assignment
> or copy isn't valid.  In the IMO unlikely event that a memcpy
> over a reference is intended, the warning is easy to suppress.

Agreed.

I wondered whether we'll end up wanting to distinguish these cases:

#1    memcpy (T *, const T *src, size_t n);
#2.1  memcpy (T *, const char *src, size_t n);  // char, void, std::byte...
#2.2  memcpy (char *, const T *src, size_t n);  // char, void, std::byte...
#3    memcpy (T *, const U *src, size_t n);

Where:
- T is a trivially copyable type that still triggers the new warning.
- U is a type unrelated to T, and is not (unsigned) char, void or std::byte.

#1 is the case that looks like copy.

#2.1 and 2.2 may well appear in type-erasing code.

#3 would look like a way to work around aliasing issues and (even though
ISTR that it's OK as GNU extension if T and U are trivial) worthy of a
warning even if T is trivial under the definition of the warning.
Reading your updated patch, I see that you warn already when T is trivial
and U is not trivial, but IIUC, not if U is also trivial, even if
unrelated to T.  Anyway, I don't really want to argue about this -- I
started writing this paragraph before actually reading the patch, and
then actually read the patch and was pleasantly surprised with what
I saw.   I think it's looking great.

> I used a similar (though not exactly the same) rationale for
> warning for const members.  They too cannot be assigned to,
> and letting memset or memcpy silently change them violates
> const-correctnes. 

*Nod*

> It's also undefined 

I'm not sure, I think there may be nuances, as usual.  AFAICS, it's generally
valid to memcpy into trivially copyable types that have const members to/from
untyped/char storage, AFAICS.  Might need to apply std::launder afterwards.  But it
isn't clear to me, since whether memcpy starts a (trivial) object's lifetime is
up in the air, AFAIK.  But I'm not suggesting to really consider that.  The rare
specialized code will be able to work around the spurious warning.

> and the immutability
> of such members an optimization opportunity waiting to be
> exploited.
> 

*nod*

>>> +Wnon-trivial-memaccess
>>> +C++ ObjC++ Var(warn_nontrival_memaccess) Warning LangEnabledBy(C++
>>> ObjC++, Wall)
>>> +Warn for raw memory writes to objects of non-trivial types.
>>
>> May I suggest renaming the warning (and the description above) from
>> -Wnon-trivial-memaccess to something else that avoids "trivial" in
>> its name?  Because it's no longer strictly about "trivial" in the
>> standard C++ sense.  The documentation talks about "The safe way",
>> and "does not warn on safe calls", so maybe call it -Wunsafe-memaccess?
> 
> I debated whether to rename things (not just the warning but
> also the function that implements it in GCC).  Ultimately I
> decided it wasn't necessary because the rules seem close enough
> to be based on some notion of triviality and because no better
> name came to mind. -Wunsafe-memaccess might work.  The one mild
> concern I have with it is that it could suggest it might do more
> than simple type checking (e.g., buffer overflow and what not).
> Let's see what Jason thinks.

Yet another motivation of avoiding "trivial" that crossed my mind is that
you may want to enable the warning in C too, e.g., for warning about
the memcpy of types with const members.  But C as no concept
of "trivial", everything is "trivial".

>> (I spotted a couple typos in the new patch: "otherwse", "becase", btw.)
> 
> I'm a horrible typist.  I'll proofread the patch again and fix
> them up before committing it.

Thanks much for working on this.  I think this will uncover lots of
latent bugs in many codebases.  Looking forward to have this in.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-12  2:43       ` Martin Sebor
@ 2017-05-17 11:53         ` Pedro Alves
  0 siblings, 0 replies; 46+ messages in thread
From: Pedro Alves @ 2017-05-17 11:53 UTC (permalink / raw)
  To: Martin Sebor, Jason Merrill; +Cc: Gcc Patch List

[Meant to add this to the other email, but forgot to copy it back.]

On 05/12/2017 03:31 AM, Martin Sebor wrote:
> +  // Zeroing out is diagnosed only because it's difficult not to.
> +  // Otherwise, a class that's non-trivial only because it has
> +  // a non-trivial dtor can be safely zeroed out (that's what
> +  // default-initializing it does).

FWIW, I found references like these to "default initialization" confusing,
until I realized you're likely talking about default initialization in
pre-C++11 terms.  I.e., "new T()" with parens.   I first assumed you're
talking about default initialization like "new T;" or "T t;", but in
that case the object would not the zeroed out, hence the confusion.
It may be beneficial to go over the comments in the patch (and
particularly documentation) and talk in C++11 (and later)
terms -- i.e., say "value-initializing it" instead.

> +  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
> +  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
> +  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-16 22:28       ` Martin Sebor
@ 2017-05-19 19:14         ` Jason Merrill
  2017-05-19 21:11           ` Martin Sebor
  0 siblings, 1 reply; 46+ messages in thread
From: Jason Merrill @ 2017-05-19 19:14 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Pedro Alves, Gcc Patch List

On Tue, May 16, 2017 at 5:39 PM, Martin Sebor <msebor@gmail.com> wrote:
> On 05/16/2017 01:41 PM, Jason Merrill wrote:
>
>> I'm still not convinced we need to consider standard-layout at all.
>
> I agree.  The patch doesn't make use of is_standard_layout_p().
> It defines its own helper called almost_std_layout_p() that
> combines trivial_type_p() with tests for non-public members,

That's the part that seems unnecessary.  Why do we care about
non-public members?

Jason

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-19 19:14         ` Jason Merrill
@ 2017-05-19 21:11           ` Martin Sebor
  2017-05-19 21:56             ` Jason Merrill
  0 siblings, 1 reply; 46+ messages in thread
From: Martin Sebor @ 2017-05-19 21:11 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Pedro Alves, Gcc Patch List

On 05/19/2017 01:07 PM, Jason Merrill wrote:
> On Tue, May 16, 2017 at 5:39 PM, Martin Sebor <msebor@gmail.com> wrote:
>> On 05/16/2017 01:41 PM, Jason Merrill wrote:
>>
>>> I'm still not convinced we need to consider standard-layout at all.
>>
>> I agree.  The patch doesn't make use of is_standard_layout_p().
>> It defines its own helper called almost_std_layout_p() that
>> combines trivial_type_p() with tests for non-public members,
>
> That's the part that seems unnecessary.  Why do we care about
> non-public members?

Because modifying them breaks encapsulation.

If I take a legacy struct, make some of its members private,
and define accessors and modifiers to manipulate those members
and maintain invariants between them, I will want to check and
adjust all code that changes objects of the struct in ways that
might violate the invariants.

What common use case are you concerned about that isn't more
appropriately expressed using the generated default or copy
constructor or assignment operator?

Martin

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-19 21:11           ` Martin Sebor
@ 2017-05-19 21:56             ` Jason Merrill
  2017-05-22  2:07               ` Martin Sebor
  0 siblings, 1 reply; 46+ messages in thread
From: Jason Merrill @ 2017-05-19 21:56 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Pedro Alves, Gcc Patch List

On Fri, May 19, 2017 at 4:07 PM, Martin Sebor <msebor@gmail.com> wrote:
> On 05/19/2017 01:07 PM, Jason Merrill wrote:
>>
>> On Tue, May 16, 2017 at 5:39 PM, Martin Sebor <msebor@gmail.com> wrote:
>>>
>>> On 05/16/2017 01:41 PM, Jason Merrill wrote:
>>>
>>>> I'm still not convinced we need to consider standard-layout at all.
>>>
>>>
>>> I agree.  The patch doesn't make use of is_standard_layout_p().
>>> It defines its own helper called almost_std_layout_p() that
>>> combines trivial_type_p() with tests for non-public members,
>>
>>
>> That's the part that seems unnecessary.  Why do we care about
>> non-public members?
>
> Because modifying them breaks encapsulation.

Not if you're clearing/copying the object as a whole.

> If I take a legacy struct, make some of its members private,
> and define accessors and modifiers to manipulate those members
> and maintain invariants between them, I will want to check and
> adjust all code that changes objects of the struct in ways that
> might violate the invariants.

For a trivial type, worrying about invariants doesn't make sense to
me, since default-initialization won't establish any invariants.  And
bzero or memset(0) will have the same effect as value-initialization
(if zero_init_p (type); we probably want to check that).  If you're
going to establish invariants, I would expect you to write a default
constructor, which would make the class non-trivial.

> What common use case are you concerned about that isn't more
> appropriately expressed using the generated default or copy
> constructor or assignment operator?

None; I am concerned about focusing the warning on code that is
actually likely to be problematic.

Jason

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-19 21:56             ` Jason Merrill
@ 2017-05-22  2:07               ` Martin Sebor
  2017-05-22  6:07                 ` Jason Merrill
  0 siblings, 1 reply; 46+ messages in thread
From: Martin Sebor @ 2017-05-22  2:07 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Pedro Alves, Gcc Patch List

On 05/19/2017 03:42 PM, Jason Merrill wrote:
> On Fri, May 19, 2017 at 4:07 PM, Martin Sebor <msebor@gmail.com> wrote:
>> On 05/19/2017 01:07 PM, Jason Merrill wrote:
>>>
>>> On Tue, May 16, 2017 at 5:39 PM, Martin Sebor <msebor@gmail.com> wrote:
>>>>
>>>> On 05/16/2017 01:41 PM, Jason Merrill wrote:
>>>>
>>>>> I'm still not convinced we need to consider standard-layout at all.
>>>>
>>>>
>>>> I agree.  The patch doesn't make use of is_standard_layout_p().
>>>> It defines its own helper called almost_std_layout_p() that
>>>> combines trivial_type_p() with tests for non-public members,
>>>
>>>
>>> That's the part that seems unnecessary.  Why do we care about
>>> non-public members?
>>
>> Because modifying them breaks encapsulation.
>
> Not if you're clearing/copying the object as a whole.
>
>> If I take a legacy struct, make some of its members private,
>> and define accessors and modifiers to manipulate those members
>> and maintain invariants between them, I will want to check and
>> adjust all code that changes objects of the struct in ways that
>> might violate the invariants.
>
> For a trivial type, worrying about invariants doesn't make sense to
> me, since default-initialization won't establish any invariants.  And
> bzero or memset(0) will have the same effect as value-initialization
> (if zero_init_p (type); we probably want to check that).  If you're
> going to establish invariants, I would expect you to write a default
> constructor, which would make the class non-trivial.

Thanks for the zero_init_p pointer!  Let me add that to the patch
along with Pedro's suggestion to use the current C++ terminology,
retest and resubmit.

In most cases you're right that defining the default constructor
is the way to go.  There is are a couple of use cases where a ctor
tends to be avoided: when objects the class need to be initialized
statically, and where they need to be PODs.  GCC itself relies on
the latter (e.g., some of the vec templates), apparently because
it stores them in unions.  It doesn't look tome like these vec
templates maintain much of an invariant of any kind, but they
easily could.

An example of the static initialization case is an atomic class
(not necessarily std::atomic though I think this would apply to
it as well).  Its objects usually need to be statically default-
initializable (without running any ctors) but then they must be
explicitly initialized by some sort of an init call to be made
valid, and their state can only be accessed and modified via
member functions (and so their state is private).  Modifying
them by any other means, including by memset or memcpy, is
undefined.

>
>> What common use case are you concerned about that isn't more
>> appropriately expressed using the generated default or copy
>> constructor or assignment operator?
>
> None; I am concerned about focusing the warning on code that is
> actually likely to be problematic.

Hopefully the above explanation resolves that concern.  If not,
please let me know.

Martin

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-22  2:07               ` Martin Sebor
@ 2017-05-22  6:07                 ` Jason Merrill
  2017-05-24 20:28                   ` Martin Sebor
  0 siblings, 1 reply; 46+ messages in thread
From: Jason Merrill @ 2017-05-22  6:07 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Pedro Alves, Gcc Patch List

On Sun, May 21, 2017 at 7:59 PM, Martin Sebor <msebor@gmail.com> wrote:
> On 05/19/2017 03:42 PM, Jason Merrill wrote:
>> On Fri, May 19, 2017 at 4:07 PM, Martin Sebor <msebor@gmail.com> wrote:
>>> On 05/19/2017 01:07 PM, Jason Merrill wrote:
>>>> On Tue, May 16, 2017 at 5:39 PM, Martin Sebor <msebor@gmail.com> wrote:
>>>>> On 05/16/2017 01:41 PM, Jason Merrill wrote:
>>>>>
>>>>>> I'm still not convinced we need to consider standard-layout at all.
>>>>>
>>>>> I agree.  The patch doesn't make use of is_standard_layout_p().
>>>>> It defines its own helper called almost_std_layout_p() that
>>>>> combines trivial_type_p() with tests for non-public members,
>>>>
>>>>
>>>>
>>>> That's the part that seems unnecessary.  Why do we care about
>>>> non-public members?
>>>
>>>
>>> Because modifying them breaks encapsulation.
>>
>>
>> Not if you're clearing/copying the object as a whole.
>>
>>> If I take a legacy struct, make some of its members private,
>>> and define accessors and modifiers to manipulate those members
>>> and maintain invariants between them, I will want to check and
>>> adjust all code that changes objects of the struct in ways that
>>> might violate the invariants.
>>
>> For a trivial type, worrying about invariants doesn't make sense to
>> me, since default-initialization won't establish any invariants.  And
>> bzero or memset(0) will have the same effect as value-initialization
>> (if zero_init_p (type); we probably want to check that).  If you're
>> going to establish invariants, I would expect you to write a default
>> constructor, which would make the class non-trivial.
>
> Thanks for the zero_init_p pointer!  Let me add that to the patch
> along with Pedro's suggestion to use the current C++ terminology,
> retest and resubmit.
>
> In most cases you're right that defining the default constructor
> is the way to go.  There is are a couple of use cases where a ctor
> tends to be avoided: when objects the class need to be initialized
> statically, and where they need to be PODs.  GCC itself relies on
> the latter (e.g., some of the vec templates), apparently because
> it stores them in unions.  It doesn't look tome like these vec
> templates maintain much of an invariant of any kind, but they
> easily could.
>
> An example of the static initialization case is an atomic class
> (not necessarily std::atomic though I think this would apply to
> it as well).  Its objects usually need to be statically default-
> initializable (without running any ctors) but then they must be
> explicitly initialized by some sort of an init call to be made
> valid, and their state can only be accessed and modified via
> member functions (and so their state is private).  Modifying
> them by any other means, including by memset or memcpy, is
> undefined.
>
>>
>>> What common use case are you concerned about that isn't more
>>> appropriately expressed using the generated default or copy
>>> constructor or assignment operator?
>>
>>
>> None; I am concerned about focusing the warning on code that is
>> actually likely to be problematic.
>
>
> Hopefully the above explanation resolves that concern.  If not,
> please let me know.

I still think that we shouldn't warn about zeroing out a
non-standard-layout object when value-initialization would produce the
exact same result.  If you want to keep the almost_std_layout_p check
for the non-zero memset case, that's fine.

Jason

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-22  6:07                 ` Jason Merrill
@ 2017-05-24 20:28                   ` Martin Sebor
  2017-05-24 20:48                     ` Martin Sebor
  0 siblings, 1 reply; 46+ messages in thread
From: Martin Sebor @ 2017-05-24 20:28 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Pedro Alves, Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 3973 bytes --]

On 05/21/2017 10:50 PM, Jason Merrill wrote:
> On Sun, May 21, 2017 at 7:59 PM, Martin Sebor <msebor@gmail.com> wrote:
>> On 05/19/2017 03:42 PM, Jason Merrill wrote:
>>> On Fri, May 19, 2017 at 4:07 PM, Martin Sebor <msebor@gmail.com> wrote:
>>>> On 05/19/2017 01:07 PM, Jason Merrill wrote:
>>>>> On Tue, May 16, 2017 at 5:39 PM, Martin Sebor <msebor@gmail.com> wrote:
>>>>>> On 05/16/2017 01:41 PM, Jason Merrill wrote:
>>>>>>
>>>>>>> I'm still not convinced we need to consider standard-layout at all.
>>>>>>
>>>>>> I agree.  The patch doesn't make use of is_standard_layout_p().
>>>>>> It defines its own helper called almost_std_layout_p() that
>>>>>> combines trivial_type_p() with tests for non-public members,
>>>>>
>>>>>
>>>>>
>>>>> That's the part that seems unnecessary.  Why do we care about
>>>>> non-public members?
>>>>
>>>>
>>>> Because modifying them breaks encapsulation.
>>>
>>>
>>> Not if you're clearing/copying the object as a whole.
>>>
>>>> If I take a legacy struct, make some of its members private,
>>>> and define accessors and modifiers to manipulate those members
>>>> and maintain invariants between them, I will want to check and
>>>> adjust all code that changes objects of the struct in ways that
>>>> might violate the invariants.
>>>
>>> For a trivial type, worrying about invariants doesn't make sense to
>>> me, since default-initialization won't establish any invariants.  And
>>> bzero or memset(0) will have the same effect as value-initialization
>>> (if zero_init_p (type); we probably want to check that).  If you're
>>> going to establish invariants, I would expect you to write a default
>>> constructor, which would make the class non-trivial.
>>
>> Thanks for the zero_init_p pointer!  Let me add that to the patch
>> along with Pedro's suggestion to use the current C++ terminology,
>> retest and resubmit.
>>
>> In most cases you're right that defining the default constructor
>> is the way to go.  There is are a couple of use cases where a ctor
>> tends to be avoided: when objects the class need to be initialized
>> statically, and where they need to be PODs.  GCC itself relies on
>> the latter (e.g., some of the vec templates), apparently because
>> it stores them in unions.  It doesn't look tome like these vec
>> templates maintain much of an invariant of any kind, but they
>> easily could.
>>
>> An example of the static initialization case is an atomic class
>> (not necessarily std::atomic though I think this would apply to
>> it as well).  Its objects usually need to be statically default-
>> initializable (without running any ctors) but then they must be
>> explicitly initialized by some sort of an init call to be made
>> valid, and their state can only be accessed and modified via
>> member functions (and so their state is private).  Modifying
>> them by any other means, including by memset or memcpy, is
>> undefined.
>>
>>>
>>>> What common use case are you concerned about that isn't more
>>>> appropriately expressed using the generated default or copy
>>>> constructor or assignment operator?
>>>
>>>
>>> None; I am concerned about focusing the warning on code that is
>>> actually likely to be problematic.
>>
>>
>> Hopefully the above explanation resolves that concern.  If not,
>> please let me know.
>
> I still think that we shouldn't warn about zeroing out a
> non-standard-layout object when value-initialization would produce the
> exact same result.  If you want to keep the almost_std_layout_p check
> for the non-zero memset case, that's fine.

Attached is an updated patch with the requested changes.  I've
also renamed the option -Wclass-memaccess to avoid suggesting
that the warning focuses solely on non-trivial types, and
updated its wording and comments throughout to refer to value
initialization rather than default initialization.  Retesting
exposed another problem recently introduced into dumplfile.c
so this revision of the patch also contains a fix for that.

Martin

[-- Attachment #2: gcc-80560.diff --]
[-- Type: text/x-patch, Size: 55371 bytes --]

PR c++/80560 - warn on undefined memory operations involving non-trivial types

gcc/c-family/ChangeLog:

	PR c++/80560
	* c.opt (-Wclass-memaccess): New option.

gcc/cp/ChangeLog:

	PR c++/80560
	* call.c (first_non_public_field): New function.
	(maybe_warn_class_memaccess): Same.
	(build_cxx_call): Call it.
	* cp-tree.h (has_trivial_assign, has_trivial_copy): Declare.
	* method.c (has_trivial_assign, has_trivial_copy): Define.	

gcc/ChangeLog:

	PR c++/80560
	* gcc.c (struct compiler): Remove const qualification.
	* genattrtab.c (gen_insn_reserv): Replace memset with initialization.
	* hash-table.h: Ditto.
	* ipa-cp.c (allocate_and_init_ipcp_value): Replace memset with
	  assignment.
	* ipa-prop.c (ipa_free_edge_args_substructures): Ditto.
	* omp-low.c (lower_omp_ordered_clauses): Replace memset with
	default ctor.
	* params.h (struct param_info): Make struct members non-const.
	* tree-switch-conversion.c (emit_case_bit_tests): Replace memset
	with default initialization.
	* vec.h (vec_copy_construct, vec_default_construct): New helper
	functions.
	(vec<T>::copy, vec<T>::splice, vec<T>::reserve): Replace memcpy
	with vec_copy_construct.
	(vect<T>::quick_grow_cleared): Replace memset with default ctor.
	(vect<T>::vec_safe_grow_cleared, vec_safe_grow_cleared): Same.
	* doc/invoke.texi (-Wclass-memaccess): Document.

libcpp/ChangeLog:

	PR c++/80560
	* line-map.c (line_maps::~line_maps): Avoid calling htab_delete
	with a null pointer.
	(linemap_init): Avoid calling memset on an object of a non-trivial
	type.

libitm/ChangeLog:

	PR c++/80560
	* beginend.cc (GTM::gtm_thread::rollback): Avoid calling memset
	on an object of a non-trivial type.
	(GTM::gtm_transaction_cp::commit): Use assignment instead of memcpy
	to copy an object.
	* method-ml.cc (orec_iterator::reinit): Avoid -Wclass-memaccess.

gcc/testsuite/ChangeLog:

	PR c++/80560
	* g++.dg/Wclass-memaccess.C: New test.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 9ad2f6e..f777b8f 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -792,6 +792,10 @@ Wnon-template-friend
 C++ ObjC++ Var(warn_nontemplate_friend) Init(1) Warning
 Warn when non-templatized friend functions are declared within a template.
 
+Wclass-memaccess
+C++ ObjC++ Var(warn_class_memaccess) Warning LangEnabledBy(C++ ObjC++, Wall)
+Warn for unsafe raw memory writes to objects of class types.
+
 Wnon-virtual-dtor
 C++ ObjC++ Var(warn_nonvdtor) Warning LangEnabledBy(C++ ObjC++,Weffc++)
 Warn about non-virtual destructors.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index e348f29..f2e6bcc 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8153,6 +8153,259 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
   return call;
 }
 
+/* Return the DECL of the first non-public data member of class TYPE
+   or null if none can be found.  */
+
+static tree
+first_non_public_field (tree type)
+{
+  if (!CLASS_TYPE_P (type))
+    return NULL_TREE;
+
+  for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+	continue;
+      if (TREE_STATIC (field))
+	continue;
+      if (TREE_PRIVATE (field) || TREE_PROTECTED (field))
+	return field;
+    }
+
+  return NULL_TREE;
+}
+
+/* Return true if class TYPE meets a relaxed definition of standard-layout.
+   In particular, the requirement that it "has all non-static data members
+   nd bit-fields in the class and its base classes first declared in the
+   same class" is not considered.  */
+
+static bool
+almost_std_layout_p (tree type, tree *field)
+{
+  if (!CLASS_TYPE_P (type))
+    return true;
+
+  if (!trivial_type_p (type))
+    return false;
+
+  if (tree fld = first_non_public_field (type))
+    if (!*field)
+      *field = fld;
+
+  tree binfo, base_binfo;
+
+  int i;
+
+  for (binfo = TYPE_BINFO (type), i = 0;
+       BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+    {
+      tree base = TREE_TYPE (base_binfo);
+
+      if (!almost_std_layout_p (base, field))
+	return false;
+    }
+
+  return true;
+}
+
+/* Issue a warning on a call to the built-in function FNDECL if it is
+   a memory write whose destination is not an object of (something like)
+   trivial or standard layout type with a non-deleted assignment and copy
+   ctor.  Detects const correctness violations, corrupting references,
+   virtual table pointers, and bypassing non-trivial assignments.  */
+
+static void
+maybe_warn_class_memaccess (location_t loc, tree fndecl, tree *args)
+{
+  /* The destination pointer is always the first argument for all functions
+     handled here.  */
+  tree dest = args[0];
+  if (!dest || !TREE_TYPE (dest) || !POINTER_TYPE_P (TREE_TYPE (dest)))
+    return;
+
+  STRIP_NOPS (dest);
+
+  tree srctype = NULL_TREE;
+
+  /* Determine the type of the pointed-to object and whether it's
+     a complete class.  */
+  tree desttype = TREE_TYPE (TREE_TYPE (dest));
+
+  if (!desttype || !COMPLETE_TYPE_P (desttype))
+    return;
+
+  /* Check to see if the memcpy/memset call is made by a ctor or dtor
+     with this as the destination argument for the destination type.
+     If so, be more permissive.  */
+  if (current_function_decl
+      && (DECL_CONSTRUCTOR_P (current_function_decl)
+	  || DECL_DESTRUCTOR_P (current_function_decl))
+      && is_this_parameter (dest))
+    {
+      tree ctx = DECL_CONTEXT (current_function_decl);
+      bool special = same_type_ignoring_top_level_qualifiers_p (ctx, desttype);
+
+      tree binfo = TYPE_BINFO (ctx);
+
+      /* A ctor and dtor for a class with no bases and no virtual functions
+	 can do whatever they want.  Bail early with no further checking.  */
+      if (special && !BINFO_VTABLE (binfo) && !BINFO_N_BASE_BINFOS (binfo))
+	return;
+    }
+
+  /* FLD will be set to the first private/protected member of a non
+     standard layout class (a looser interpretation of the term than
+     in the C++ standard is used here).  */
+  tree fld = NULL_TREE;
+  bool stdlayout = almost_std_layout_p (desttype, &fld);
+  bool trivial = trivial_type_p (desttype);
+  bool trivassign = has_trivial_assign (desttype);
+  bool trivcopy = has_trivial_copy (desttype);
+
+  const char *warnfmt = NULL;
+  bool warned = false;
+
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    case BUILT_IN_MEMSET:
+      if (!integer_zerop (args[1]))
+	{
+	  /* Diagnose setting non-copy-assignable, non-trivial or non-
+	     standard layout objects (in that order, since the latter
+	     two are subsets of one another), to (potentially) non-zero
+	     bytes.  Since the value of the bytes being written is unknown,
+	     suggest using assignment instead (if one exists).  */
+	  if (!trivassign)
+	    warnfmt = G_("%qD writing to an object of type %#qT without "
+			 "trivial copy assignment");
+	  else if (!trivial)
+	    warnfmt = G_("%qD writing to an object of non-trivial "
+			 "type %#qT; use assignment instead");
+	  else if (!stdlayout)
+	    warnfmt = G_("%qD writing to an object of non-standard-layout "
+			 "type %#qT; use assignment instead");
+	  else if (fld)
+	    {
+	      const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	      warned = warning_at (loc, OPT_Wclass_memaccess,
+				   "%qD writing to an object of type %#qT with "
+				   "%qs member %qD; use assignment instead",
+				   fndecl, desttype, access, fld);
+	    }
+	  else if (!zero_init_p (desttype))
+	    warnfmt = G_("%qD writing to an object of type %#qT containing "
+			 "a pointer to data member; use assignment instead");
+
+	  break;
+	}
+      /* Fall through.  */
+
+    case BUILT_IN_BZERO:
+      /* Similarly to the above, diagnose clearing non-trivial or non-
+	 standard layout objects, or objects of types with no assignmenmt.  */
+      if (!trivassign)
+	warnfmt = (trivcopy
+		   ? G_("%qD clearing an object of type %#qT without trivial "
+			"copy assignment; use copy constructor instead")
+		   : G_("%qD clearing an object of type %#qT without trivial "
+			"copy assignment"));
+      else if (!trivial)
+	warnfmt = G_("%qD clearing an object of non-trivial type "
+		     "%#qT; use assignment or value-initialization instead");
+      else if (!stdlayout)
+	warnfmt = G_("%qD clearing an object of non-standard-layout type "
+		     "%#qT; use assignment or value-initialization instead");
+      else if (!zero_init_p (desttype))
+	warnfmt = G_("%qD clearing an object of type %#qT containing "
+		     "a pointer-to-member; use assignment or value-"
+		     "initialization instead");
+      break;
+
+    case BUILT_IN_BCOPY:
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMPCPY:
+      /* Determine the type of the source object.  */
+      srctype = (TREE_CODE (args[1]) == NOP_EXPR
+		 ? TREE_OPERAND (args[1], 0) : args[1]);
+
+      srctype = TREE_TYPE (TREE_TYPE (srctype));
+
+      if (!trivassign)
+	warnfmt = (trivcopy
+		   ? G_("%qD writing to an object of type %#qT without trivial "
+			"copy assignment; use copy constructor instead")
+		   :  G_("%qD writing to an object of type %#qT without trivial "
+			 "copy assignment"));
+      else if (!trivially_copyable_p (desttype))
+	warnfmt = G_("%qD writing to an object of non-trivially-copyable type "
+		     "%#qT; use assignment or copy-initialization instead");
+      else if (!trivial
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  /* Warn when copying into a non-trivial object from an object
+	     of a different type other than void or char.  */
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD copying an object of non-trivial type "
+			       "%#qT from an array of %#qT",
+			       fndecl, desttype, srctype);
+	}
+      else if (fld
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD copying an object of type %#qT with "
+			       "%qs member %qD from an arrayt of %#qT; use "
+			       "assignment or copy-initialization instead",
+			       fndecl, desttype, access, fld);
+	}
+      else if (!zero_init_p (desttype)
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	warnfmt = G_("%qD writing to an object of type %#qT containing "
+		     "a pointer-to-member; use assignment or copy-"
+		     "initialization instead");
+      else if (!trivial && TREE_CODE (args[2]) == INTEGER_CST)
+	{
+	  /* Finally, warn on partial copies.  */
+	  unsigned HOST_WIDE_INT typesize
+	    = tree_to_uhwi (TYPE_SIZE_UNIT (desttype));
+	  if (unsigned HOST_WIDE_INT partial
+	      = tree_to_uhwi (args[2]) % typesize)
+	    warned = warning_at (loc, OPT_Wclass_memaccess,
+				 (typesize - partial > 1
+				  ? G_("%qD writing to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "bytes unchanged")
+				  : G_("%qD writing to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "byte unchanged")),
+				 fndecl, desttype, typesize - partial);
+	}
+      break;
+
+    default:
+      return;
+    }
+
+  if (!warned && !warnfmt)
+    return;
+
+  if (warned
+      || warning_at (loc, OPT_Wclass_memaccess, warnfmt, fndecl, desttype))
+    inform (location_of (desttype), "%#qT declared here", desttype);
+}
+
 /* Build and return a call to FN, using NARGS arguments in ARGARRAY.
    This function performs no overload resolution, conversion, or other
    high-level operations.  */
@@ -8185,6 +8438,10 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
       if (!check_builtin_function_arguments (EXPR_LOCATION (fn), vNULL, fndecl,
 					     nargs, argarray))
 	return error_mark_node;
+
+      /* Warn if the built-in writes to an object of a non-trivial type.  */
+      if (nargs)
+	maybe_warn_class_memaccess (loc, fndecl, argarray);
     }
 
     /* If it is a built-in array notation function, then the return type of
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8120b93..80508e3 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6131,6 +6131,9 @@ extern bool ctor_omit_inherited_parms		(tree);
 extern tree locate_ctor				(tree);
 extern tree implicitly_declare_fn               (special_function_kind, tree,
 						 bool, tree, tree);
+extern bool has_trivial_assign                  (tree);
+extern bool has_trivial_copy                    (tree);
+
 
 /* In optimize.c */
 extern bool maybe_clone_body			(tree);
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index b4c1f60..810119e 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -1716,6 +1716,44 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
   --c_inhibit_evaluation_warnings;
 }
 
+/* Return true if the class type CTYPE has non-deleted trivial copy
+   assignment.  */
+
+bool
+has_trivial_assign (tree ctype)
+{
+  if (!CLASS_TYPE_P (ctype))
+    return true;
+
+  bool trivial_p = false;
+  bool deleted_p = false;
+
+  synthesized_method_walk (ctype, sfk_copy_assignment, false, NULL,
+			   &trivial_p, &deleted_p,
+			   NULL, false, NULL_TREE, NULL_TREE);
+
+  return trivial_p && !deleted_p;
+}
+
+/* Return true if the class type CTYPE has non-deleted trivial copy
+   constructor.  */
+
+bool
+has_trivial_copy (tree ctype)
+{
+  if (!CLASS_TYPE_P (ctype))
+    return true;
+
+  bool trivial_p = false;
+  bool deleted_p = false;
+
+  synthesized_method_walk (ctype, sfk_copy_constructor, false, NULL,
+			   &trivial_p, &deleted_p,
+			   NULL, false, NULL_TREE, NULL_TREE);
+
+  return trivial_p && !deleted_p;
+}
+
 /* DECL is a defaulted function whose exception specification is now
    needed.  Return what it should be.  */
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 57c9678..4301306 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -215,7 +215,8 @@ in the following sections.
 -Wabi=@var{n}  -Wabi-tag  -Wconversion-null  -Wctor-dtor-privacy @gol
 -Wdelete-non-virtual-dtor  -Wliteral-suffix  -Wmultiple-inheritance @gol
 -Wnamespaces  -Wnarrowing @gol
--Wnoexcept  -Wnoexcept-type  -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
+-Wnoexcept  -Wnoexcept-type  -Wclass-memaccess @gol
+-Wnon-virtual-dtor  -Wreorder  -Wregister @gol
 -Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
 -Wno-non-template-friend  -Wold-style-cast @gol
 -Woverloaded-virtual  -Wno-pmf-conversions @gol
@@ -2913,6 +2914,23 @@ void g() noexcept;
 void h() @{ f(g); @} // in C++14 calls f<void(*)()>, in C++1z calls f<void(*)()noexcept>
 @end smallexample
 
+@item -Wclass-memaccess @r{(C++ and Objective-C++ only)}
+@opindex Wclass-memaccess
+Warn when the destination of a call to a raw memory function such as
+@code{memset} or @code{memcpy} is an object of class type writing into which
+might bypass the class non-trivial or deleted constructor or copy assignment,
+violate const-correctness or encapsulation, or corrupt the virtual table.
+Modifying the representation of such objects may violate invariants maintained
+by member functions of the class.  For example, the call to @code{memset}
+below is undefined becase it modifies a non-trivial class object and is,
+therefore, diagnosed.  The safe way to either initialize or clear the storage
+of objects of such types is by using the appropriate constructor or assignment
+operator, if one is available.
+@smallexample
+std::string str = "abc";
+memset (&str, 0, 3);
+@end smallexample
+The @option{-Wclass-memaccess} option is enabled by @option{-Wall}.
 
 @item -Wnon-virtual-dtor @r{(C++ and Objective-C++ only)}
 @opindex Wnon-virtual-dtor
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 120c5c0..0ef4f10 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -1259,9 +1259,9 @@ struct compiler
   const char *cpp_spec;         /* If non-NULL, substitute this spec
 				   for `%C', rather than the usual
 				   cpp_spec.  */
-  const int combinable;          /* If nonzero, compiler can deal with
+  int combinable;               /* If nonzero, compiler can deal with
 				    multiple source files at once (IMA).  */
-  const int needs_preprocessing; /* If nonzero, source files need to
+  int needs_preprocessing;       /* If nonzero, source files need to
 				    be run through a preprocessor.  */
 };
 
diff --git a/gcc/genattrtab.c b/gcc/genattrtab.c
index 3629b5f..51dfe77 100644
--- a/gcc/genattrtab.c
+++ b/gcc/genattrtab.c
@@ -4703,8 +4703,8 @@ gen_insn_reserv (md_rtx_info *info)
   struct insn_reserv *decl = oballoc (struct insn_reserv);
   rtx def = info->def;
 
-  struct attr_desc attr;
-  memset (&attr, 0, sizeof (attr));
+  struct attr_desc attr = { };
+
   attr.name = DEF_ATTR_STRING (XSTR (def, 0));
   attr.loc = info->loc;
 
diff --git a/gcc/hash-table.h b/gcc/hash-table.h
index 0f7e21a..443d16c 100644
--- a/gcc/hash-table.h
+++ b/gcc/hash-table.h
@@ -803,7 +803,10 @@ hash_table<Descriptor, Allocator>::empty_slow ()
       m_size_prime_index = nindex;
     }
   else
-    memset (entries, 0, size * sizeof (value_type));
+    {
+      for ( ; size; ++entries, --size)
+	*entries = value_type ();
+    }
   m_n_deleted = 0;
   m_n_elements = 0;
 }
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 5e1d94c..2705cbc 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1472,8 +1472,7 @@ allocate_and_init_ipcp_value (tree source)
 {
   ipcp_value<tree> *val;
 
-  val = ipcp_cst_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_cst_values_pool.allocate ()) ipcp_value<tree>();
   val->value = source;
   return val;
 }
@@ -1487,8 +1486,8 @@ allocate_and_init_ipcp_value (ipa_polymorphic_call_context source)
   ipcp_value<ipa_polymorphic_call_context> *val;
 
   // TODO
-  val = ipcp_poly_ctx_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_poly_ctx_values_pool.allocate ())
+    ipcp_value<ipa_polymorphic_call_context>();
   val->value = source;
   return val;
 }
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index 5819f78..212f423 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -3730,7 +3730,7 @@ void
 ipa_free_edge_args_substructures (struct ipa_edge_args *args)
 {
   vec_free (args->jump_functions);
-  memset (args, 0, sizeof (*args));
+  *args = ipa_edge_args ();
 }
 
 /* Free all ipa_edge structures.  */
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 9cc2996..dc93dce 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -6292,7 +6292,11 @@ lower_omp_ordered_clauses (gimple_stmt_iterator *gsi_p, gomp_ordered *ord_stmt,
     return;
 
   wide_int *folded_deps = XALLOCAVEC (wide_int, 2 * len - 1);
-  memset (folded_deps, 0, sizeof (*folded_deps) * (2 * len - 1));
+
+  /* wide_int is not a POD so it must be default-constructed.  */
+  for (unsigned i = 0; i != 2 * len - 1; ++i)
+    new (static_cast<void*>(folded_deps + i)) wide_int ();
+
   tree folded_dep = NULL_TREE;
   /* TRUE if the first dimension's offset is negative.  */
   bool neg_offset_p = false;
diff --git a/gcc/params.h b/gcc/params.h
index b61cff9..8b91660 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -42,7 +42,7 @@ struct param_info
 {
   /* The name used with the `--param <name>=<value>' switch to set this
      value.  */
-  const char *const option;
+  const char *option;
 
   /* The default value.  */
   int default_value;
@@ -54,7 +54,7 @@ struct param_info
   int max_value;
 
   /* A short description of the option.  */
-  const char *const help;
+  const char *help;
 
   /* The optional names corresponding to the values.  */
   const char **value_names;
diff --git a/gcc/testsuite/g++.dg/Wclass-memaccess.C b/gcc/testsuite/g++.dg/Wclass-memaccess.C
new file mode 100644
index 0000000..3676947
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wclass-memaccess.C
@@ -0,0 +1,730 @@
+/* PR c++/80560 - warn on undefined memory operations involving non-trivial
+   types
+   { dg-do compile }
+   { dg-options "-Wclass-memaccess -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern "C"
+{
+void bzero (void*, size_t);
+void* memcpy (void*, const void*, size_t);
+void* memmove (void*, const void*, size_t);
+void* mempcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+}
+
+/* Trivial can be manipulated by raw memory functions.  */
+struct Trivial { int i; unsigned bf: 1; char *s; char a[4]; };
+
+/* HasDefaultCtor is trivially copyable but should be initialized by
+   the ctor, not bzero or memset.  */
+struct HasDefaultCtor { char a[4]; HasDefaultCtor (); };
+
+/* HasCopyCtor should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  Since it's non-trivial, i should not be zeroed
+   out by bzero/memset either and should instead use assignment and/or
+   value initialization.  */
+struct HasCopyCtor { int i; HasCopyCtor (const HasCopyCtor&); };
+
+/* HasDtor should be initialized using aggregate or memberwise intialization,
+   not bzero or memset.  */
+struct HasDtor { int i; ~HasDtor (); };
+
+/* HasAssign should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  */
+struct HasAssign { int i; void operator= (HasAssign&); };
+
+/* HasVirtuals should only be manipulated by the special member functions
+   and not by bzero, memcpy, or any other raw memory function. Doing
+   otherwse might corrupt the the vtable pointer.  */
+struct HasVirtuals { int i; virtual void foo (); };
+
+/* HasConstData should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable allowing, raw memory functions to write into it would defeat
+   const-correctness.  */
+struct HasConstData { const int ci; };
+
+/* HasReference should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable, allowing raw memory functions to write into it could
+   corrupt the reference.  */
+struct HasReference { int &ci; };
+
+/* HasMemFuncPtr should only be initialized using aggregate initializatoon
+   and not cleared by bzero or written into using memset because its
+   representation is different from ordinary scalars (a null member data
+   pointer is all ones).  It can be copied into using memcpy from an object
+   of the same type or from a character buffer.  */
+struct HasMemDataPtr { int HasMemDataPtr::*p; };
+
+/* HasSomePrivateDate and HasSomeProtectedData can be initialized using
+   value initialization and should not be written to using memset with
+   a non-zero argument.  Doing otherwise would break encapsulation.  */
+struct HasSomePrivateData { int i; private: int j; };
+struct HasSomeProtectedData { int i; protected: int j; };
+
+/* Similarly to the above, HasAllPrivateDate and HasAllProtectedData should
+   only be initialized using value initializatoon and should not be written
+   to using memset with non-zero argument.  They are tested separately
+   because unlike the former classes, these are standard layout.  */
+struct HasAllPrivateData { private: int i; };
+struct HasAllProtectedData { protected: int i; };
+
+void sink (void*);
+
+#define T(fn, arglist) (fn arglist, sink (p))
+
+#if !defined TEST || TEST == TEST_TRIVIAL
+
+void test (Trivial *p, void *q, int x)
+{
+  T (bzero, (p, 1));
+  T (bzero, (p, sizeof *p));
+  T (bzero, (q, 1));
+  T (bzero, (q, sizeof *p));
+
+  T (memcpy, (p, q, 1));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (q, p, 1));
+  T (memcpy, (q, p, sizeof *p));
+
+  T (memset, (p, 0, 1));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (q, 0, 1));
+  T (memset, (q, 0, sizeof *p));
+
+  T (memset, (p, 1, 1));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (q, 1, 1));
+  T (memset, (q, 1, sizeof *p));
+
+  T (memset, (p, x, 1));
+  T (memset, (p, x, sizeof *p));
+  T (memset, (q, x, 1));
+  T (memset, (q, x, sizeof *p));
+
+  T (memmove, (p, q, 1));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (q, p, 1));
+  T (memmove, (q, p, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DEFAULT_CTOR
+
+void test (HasDefaultCtor *p, const HasDefaultCtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefaultCtor is neither trivial nor standard-layout.  The warning
+  // should mention the former since it's more permissive than the latter
+  // and so more informative.
+  T (bzero, (p, sizeof *p));   // { dg-warning ".void bzero(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefaultCtor.; use assignment or value-initialization instead" }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefaultCtor.; use assignment or value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefaultCtor.; use assignment instead" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefaultCtor.; use assignment instead" }
+
+  // Copying from another object of the same type is fine.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+
+  // Copying from a void* or character buffer is also fine.
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, n));
+
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, q, n));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, s, n));
+
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+
+  // ...but partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefaultCtor. leaves 3 bytes unchanged" } */
+  T (memmove, (p, q, 2));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefaultCtor. leaves 2 bytes unchanged" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefaultCtor. leaves 1 byte unchanged" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefaultCtor. from an array of .const int." }
+
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning ".void\\* memmove(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefaultCtor. from an array of .const int." }
+
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning ".void\\* mempcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefaultCtor. from an array of .const int." }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_COPY_CTOR
+
+void test (HasCopyCtor *p, const HasCopyCtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed because value initialization is
+  // invalid (the copy ctor makes no default ctor unavailable).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DTOR
+
+void test (HasDtor *p, const HasDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed only because it's difficult not to.
+  // Otherwise, a class that's non-trivial only because it has
+  // a non-trivial dtor can be safely zeroed out (that's what
+  // value-initializing it does).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed simply because
+  // a class with a user-defined dtor is not trivially copyable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ASSIGN
+
+void test (HasAssign *p, const HasAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_VIRTUALS
+
+void test (HasVirtuals *p, const HasVirtuals &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed because it corrupts the vtable.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is diagnosed because when used to initialize an object
+  // could incorrectly initialize the vtable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_CONST_DATA
+
+void test (HasConstData *p, const HasConstData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // The following is ill-formed because HasConstData's cannot
+  // be assigned (the assignment is implicitly deleted).  For
+  // that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object could break const correctness.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasConstData. without trivial copy assignment" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of type .struct HasConstData. without trivial copy assignment" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_REFERENCE
+
+void test (HasReference *p, const HasReference &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Similarly to HasConstData, the following is ill-formed because
+  // Hasreference cannot be assigned (the assignment is implicitly
+  // deleted).  For that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object would invalidate the reference.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasReference. without trivial copy assignment" }
+  T (bzero, (p, n));                // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 0, n));            // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, n));            // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, n));            // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of type .struct HasReference. without trivial copy assignment" }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_MEM_DATA_PTR
+
+void test (HasMemDataPtr *p, const HasMemDataPtr &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because a null member data pointer has
+  // a representation that's all bits set.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasMemDataPtr. containing a pointer-to-member" }
+  T (bzero, (p, n));                // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 0, n));            // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, n));            // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, n));            // { dg-warning "memset" }
+
+  // Copying from an object of the same type of character buffer is
+  // not diagnosed.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, n));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PRIVATE_DATA
+
+void test (HasSomePrivateData *p, const HasSomePrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PROTECTED_DATA
+
+void test (HasSomeProtectedData *p, const HasSomeProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PRIVATE_DATA
+
+void test (HasAllPrivateData *p, const HasAllPrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PROTECTED_DATA
+
+void test (HasAllProtectedData *p, const HasAllProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_EXPRESSION
+
+void test_expr (int i)
+{
+  struct TestClass: HasDefaultCtor { };
+  TestClass a, b;
+
+  static void *p;
+
+  T (bzero, (i < 0 ? &a : &b, 1));  // { dg-warning ".void bzero" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_CTOR
+
+void test_ctor ()
+{
+#undef T
+#define T(fn, arglist) (fn arglist, sink (this))
+
+  static void *p;
+
+  struct TestBase
+  {
+    TestBase ()
+    {
+      /* A ctor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+
+    ~TestBase ()
+    {
+      /* A dtor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestBaseVtable
+  {
+    TestBaseVtable ()
+    {
+      /* A ctor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    ~TestBaseVtable ()
+    {
+      /* A dtor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    virtual void foo ();
+  };
+
+  struct TestDerived: HasDefaultCtor
+  {
+    TestDerived ()
+    {
+      /* A derived class ctor is treated as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestDerivedDtor: HasDefaultCtor
+  {
+    ~TestDerivedDtor ()
+    {
+      /* A derived class dtor is treated as an ordinary function though
+	 it probably shouldn't be unless the base dtor is trivial.  But
+	 it doesn't seem worth the trouble.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+  };
+}
+
+#endif
diff --git a/gcc/tree-switch-conversion.c b/gcc/tree-switch-conversion.c
index 0a2a840..7a58209 100644
--- a/gcc/tree-switch-conversion.c
+++ b/gcc/tree-switch-conversion.c
@@ -266,7 +266,7 @@ static void
 emit_case_bit_tests (gswitch *swtch, tree index_expr,
 		     tree minval, tree range, tree maxval)
 {
-  struct case_bit_test test[MAX_CASE_BIT_TESTS];
+  struct case_bit_test test[MAX_CASE_BIT_TESTS] = { };
   unsigned int i, j, k;
   unsigned int count;
 
@@ -291,8 +291,6 @@ emit_case_bit_tests (gswitch *swtch, tree index_expr,
   int prec = TYPE_PRECISION (word_type_node);
   wide_int wone = wi::one (prec);
 
-  memset (&test, 0, sizeof (test));
-
   /* Get the edge for the default case.  */
   tmp = gimple_switch_default_label (swtch);
   default_bb = label_to_block (CASE_LABEL (tmp));
diff --git a/gcc/vec.h b/gcc/vec.h
index fee4616..0e9a13b 100644
--- a/gcc/vec.h
+++ b/gcc/vec.h
@@ -407,6 +407,26 @@ struct GTY((user)) vec
 {
 };
 
+/* Default-construct N elements in DST.  */
+
+template <typename T>
+inline void
+vec_default_construct (T *dst, unsigned n)
+{
+  for ( ; n; ++dst, --n)
+    ::new (static_cast<void*>(dst)) T ();
+}
+
+/* Copy-construct N elements in DST from *SRC.  */
+
+template <typename T>
+inline void
+vec_copy_construct (T *dst, const T *src, unsigned n)
+{
+  for ( ; n; ++dst, ++src, --n)
+    ::new (static_cast<void*>(dst)) T (*src);
+}
+
 /* Type to provide NULL values for vec<T, A, L>.  This is used to
    provide nil initializers for vec instances.  Since vec must be
    a POD, we cannot have proper ctor/dtor for it.  To initialize
@@ -615,7 +635,7 @@ vec_safe_grow_cleared (vec<T, A, vl_embed> *&v, unsigned len CXX_MEM_STAT_INFO)
 {
   unsigned oldlen = vec_safe_length (v);
   vec_safe_grow (v, len PASS_MEM_STAT);
-  memset (&(v->address ()[oldlen]), 0, sizeof (T) * (len - oldlen));
+  vec_default_construct (v->address () + oldlen, len - oldlen);
 }
 
 
@@ -821,7 +841,7 @@ vec<T, A, vl_embed>::copy (ALONE_MEM_STAT_DECL) const
     {
       vec_alloc (new_vec, len PASS_MEM_STAT);
       new_vec->embedded_init (len, len);
-      memcpy (new_vec->address (), m_vecdata, sizeof (T) * len);
+      vec_copy_construct (new_vec->address (), m_vecdata, len);
     }
   return new_vec;
 }
@@ -838,7 +858,7 @@ vec<T, A, vl_embed>::splice (const vec<T, A, vl_embed> &src)
   if (len)
     {
       gcc_checking_assert (space (len));
-      memcpy (address () + length (), src.address (), len * sizeof (T));
+      vec_copy_construct (end (), src.address (), len);
       m_vecpfx.m_num += len;
     }
 }
@@ -1092,13 +1112,12 @@ inline void
 vec<T, A, vl_embed>::quick_grow_cleared (unsigned len)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   quick_grow (len);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
-
 /* Garbage collection support for vec<T, A, vl_embed>.  */
 
 template<typename T>
@@ -1445,7 +1464,7 @@ vec<T, va_heap, vl_ptr>::reserve (unsigned nelems, bool exact MEM_STAT_DECL)
   va_heap::reserve (m_vec, nelems, exact PASS_MEM_STAT);
   if (handle_auto_vec)
     {
-      memcpy (m_vec->address (), oldvec->address (), sizeof (T) * oldsize);
+      vec_copy_construct (m_vec->address (), oldvec->address (), oldsize);
       m_vec->m_vecpfx.m_num = oldsize;
     }
 
@@ -1607,10 +1626,10 @@ inline void
 vec<T, va_heap, vl_ptr>::safe_grow_cleared (unsigned len MEM_STAT_DECL)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   safe_grow (len PASS_MEM_STAT);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
 
diff --git a/libcpp/line-map.c b/libcpp/line-map.c
index c4b7cb2..56b7913 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -62,7 +62,8 @@ extern unsigned num_macro_tokens_counter;
 
 line_maps::~line_maps ()
 {
-  htab_delete (location_adhoc_data_map.htab);
+  if (location_adhoc_data_map.htab)
+    htab_delete (location_adhoc_data_map.htab);
 }
 
 /* Hash function for location_adhoc_data hashtable.  */
@@ -347,7 +348,7 @@ void
 linemap_init (struct line_maps *set,
 	      source_location builtin_location)
 {
-  memset (set, 0, sizeof (struct line_maps));
+  *set = line_maps ();
   set->highest_location = RESERVED_LOCATION_COUNT - 1;
   set->highest_line = RESERVED_LOCATION_COUNT - 1;
   set->location_adhoc_data_map.htab =
diff --git a/libitm/beginend.cc b/libitm/beginend.cc
index d04f3e9..c6550a3 100644
--- a/libitm/beginend.cc
+++ b/libitm/beginend.cc
@@ -431,7 +431,7 @@ GTM::gtm_transaction_cp::save(gtm_thread* tx)
   // Save everything that we might have to restore on restarts or aborts.
   jb = tx->jb;
   undolog_size = tx->undolog.size();
-  memcpy(&alloc_actions, &tx->alloc_actions, sizeof(alloc_actions));
+  alloc_actions = tx->alloc_actions;
   user_actions_size = tx->user_actions.size();
   id = tx->id;
   prop = tx->prop;
@@ -449,7 +449,7 @@ GTM::gtm_transaction_cp::commit(gtm_thread* tx)
   // commits of nested transactions. Allocation actions must be committed
   // before committing the snapshot.
   tx->jb = jb;
-  memcpy(&tx->alloc_actions, &alloc_actions, sizeof(alloc_actions));
+  tx->alloc_actions = alloc_actions;
   tx->id = id;
   tx->prop = prop;
 }
@@ -485,7 +485,7 @@ GTM::gtm_thread::rollback (gtm_transaction_cp *cp, bool aborting)
       prop = cp->prop;
       if (cp->disp != abi_disp())
 	set_abi_disp(cp->disp);
-      memcpy(&alloc_actions, &cp->alloc_actions, sizeof(alloc_actions));
+      alloc_actions = cp->alloc_actions;
       nesting = cp->nesting;
     }
   else
diff --git a/libitm/method-ml.cc b/libitm/method-ml.cc
index fcae334..b857bff 100644
--- a/libitm/method-ml.cc
+++ b/libitm/method-ml.cc
@@ -138,7 +138,11 @@ struct ml_mg : public method_group
     // This store is only executed while holding the serial lock, so relaxed
     // memory order is sufficient here.  Same holds for the memset.
     time.store(0, memory_order_relaxed);
-    memset(orecs, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
+    // The memset below isn't strictly kosher because it bypasses
+    // the non-trivial assignment operator defined by std::atomic.  Using
+    // a local void* is enough to prevent GCC from warning for this.
+    void *p = orecs;
+    memset(p, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
   }
 };
 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-24 20:28                   ` Martin Sebor
@ 2017-05-24 20:48                     ` Martin Sebor
  2017-05-24 21:36                       ` Jason Merrill
  0 siblings, 1 reply; 46+ messages in thread
From: Martin Sebor @ 2017-05-24 20:48 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Pedro Alves, Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 537 bytes --]

> Attached is an updated patch with the requested changes.  I've
> also renamed the option -Wclass-memaccess to avoid suggesting
> that the warning focuses solely on non-trivial types, and
> updated its wording and comments throughout to refer to value
> initialization rather than default initialization.  Retesting
> exposed another problem recently introduced into dumplfile.c
> so this revision of the patch also contains a fix for that.

Emacs wasn't done saving the patch so here's the latest one with
the dumpfile.c fix.

Martin


[-- Attachment #2: gcc-80560.diff --]
[-- Type: text/x-patch, Size: 56297 bytes --]

PR c++/80560 - warn on undefined memory operations involving non-trivial types

gcc/c-family/ChangeLog:

	PR c++/80560
	* c.opt (-Wclass-memaccess): New option.

gcc/cp/ChangeLog:

	PR c++/80560
	* call.c (first_non_public_field): New function.
	(maybe_warn_class_memaccess): Same.
	(build_cxx_call): Call it.
	* cp-tree.h (has_trivial_assign, has_trivial_copy): Declare.
	* method.c (has_trivial_assign, has_trivial_copy): Define.	

gcc/ChangeLog:

	PR c++/80560
	* dumpfile.c (dump_register): Avoid calling memset to initialize
	a class with a default ctor.
	* gcc.c (struct compiler): Remove const qualification.
	* genattrtab.c (gen_insn_reserv): Replace memset with initialization.
	* hash-table.h: Ditto.
	* ipa-cp.c (allocate_and_init_ipcp_value): Replace memset with
	  assignment.
	* ipa-prop.c (ipa_free_edge_args_substructures): Ditto.
	* omp-low.c (lower_omp_ordered_clauses): Replace memset with
	default ctor.
	* params.h (struct param_info): Make struct members non-const.
	* tree-switch-conversion.c (emit_case_bit_tests): Replace memset
	with default initialization.
	* vec.h (vec_copy_construct, vec_default_construct): New helper
	functions.
	(vec<T>::copy, vec<T>::splice, vec<T>::reserve): Replace memcpy
	with vec_copy_construct.
	(vect<T>::quick_grow_cleared): Replace memset with default ctor.
	(vect<T>::vec_safe_grow_cleared, vec_safe_grow_cleared): Same.
	* doc/invoke.texi (-Wclass-memaccess): Document.

libcpp/ChangeLog:

	PR c++/80560
	* line-map.c (line_maps::~line_maps): Avoid calling htab_delete
	with a null pointer.
	(linemap_init): Avoid calling memset on an object of a non-trivial
	type.

libitm/ChangeLog:

	PR c++/80560
	* beginend.cc (GTM::gtm_thread::rollback): Avoid calling memset
	on an object of a non-trivial type.
	(GTM::gtm_transaction_cp::commit): Use assignment instead of memcpy
	to copy an object.
	* method-ml.cc (orec_iterator::reinit): Avoid -Wclass-memaccess.

gcc/testsuite/ChangeLog:

	PR c++/80560
	* g++.dg/Wclass-memaccess.C: New test.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 9ad2f6e..f777b8f 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -792,6 +792,10 @@ Wnon-template-friend
 C++ ObjC++ Var(warn_nontemplate_friend) Init(1) Warning
 Warn when non-templatized friend functions are declared within a template.
 
+Wclass-memaccess
+C++ ObjC++ Var(warn_class_memaccess) Warning LangEnabledBy(C++ ObjC++, Wall)
+Warn for unsafe raw memory writes to objects of class types.
+
 Wnon-virtual-dtor
 C++ ObjC++ Var(warn_nonvdtor) Warning LangEnabledBy(C++ ObjC++,Weffc++)
 Warn about non-virtual destructors.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index f14c0fa..8e9c583 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8143,6 +8143,259 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
   return call;
 }
 
+/* Return the DECL of the first non-public data member of class TYPE
+   or null if none can be found.  */
+
+static tree
+first_non_public_field (tree type)
+{
+  if (!CLASS_TYPE_P (type))
+    return NULL_TREE;
+
+  for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+	continue;
+      if (TREE_STATIC (field))
+	continue;
+      if (TREE_PRIVATE (field) || TREE_PROTECTED (field))
+	return field;
+    }
+
+  return NULL_TREE;
+}
+
+/* Return true if class TYPE meets a relaxed definition of standard-layout.
+   In particular, the requirement that it "has all non-static data members
+   nd bit-fields in the class and its base classes first declared in the
+   same class" is not considered.  */
+
+static bool
+almost_std_layout_p (tree type, tree *field)
+{
+  if (!CLASS_TYPE_P (type))
+    return true;
+
+  if (!trivial_type_p (type))
+    return false;
+
+  if (tree fld = first_non_public_field (type))
+    if (!*field)
+      *field = fld;
+
+  tree binfo, base_binfo;
+
+  int i;
+
+  for (binfo = TYPE_BINFO (type), i = 0;
+       BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+    {
+      tree base = TREE_TYPE (base_binfo);
+
+      if (!almost_std_layout_p (base, field))
+	return false;
+    }
+
+  return true;
+}
+
+/* Issue a warning on a call to the built-in function FNDECL if it is
+   a memory write whose destination is not an object of (something like)
+   trivial or standard layout type with a non-deleted assignment and copy
+   ctor.  Detects const correctness violations, corrupting references,
+   virtual table pointers, and bypassing non-trivial assignments.  */
+
+static void
+maybe_warn_class_memaccess (location_t loc, tree fndecl, tree *args)
+{
+  /* The destination pointer is always the first argument for all functions
+     handled here.  */
+  tree dest = args[0];
+  if (!dest || !TREE_TYPE (dest) || !POINTER_TYPE_P (TREE_TYPE (dest)))
+    return;
+
+  STRIP_NOPS (dest);
+
+  tree srctype = NULL_TREE;
+
+  /* Determine the type of the pointed-to object and whether it's
+     a complete class.  */
+  tree desttype = TREE_TYPE (TREE_TYPE (dest));
+
+  if (!desttype || !COMPLETE_TYPE_P (desttype))
+    return;
+
+  /* Check to see if the memcpy/memset call is made by a ctor or dtor
+     with this as the destination argument for the destination type.
+     If so, be more permissive.  */
+  if (current_function_decl
+      && (DECL_CONSTRUCTOR_P (current_function_decl)
+	  || DECL_DESTRUCTOR_P (current_function_decl))
+      && is_this_parameter (dest))
+    {
+      tree ctx = DECL_CONTEXT (current_function_decl);
+      bool special = same_type_ignoring_top_level_qualifiers_p (ctx, desttype);
+
+      tree binfo = TYPE_BINFO (ctx);
+
+      /* A ctor and dtor for a class with no bases and no virtual functions
+	 can do whatever they want.  Bail early with no further checking.  */
+      if (special && !BINFO_VTABLE (binfo) && !BINFO_N_BASE_BINFOS (binfo))
+	return;
+    }
+
+  /* FLD will be set to the first private/protected member of a non
+     standard layout class (a looser interpretation of the term than
+     in the C++ standard is used here).  */
+  tree fld = NULL_TREE;
+  bool stdlayout = almost_std_layout_p (desttype, &fld);
+  bool trivial = trivial_type_p (desttype);
+  bool trivassign = has_trivial_assign (desttype);
+  bool trivcopy = has_trivial_copy (desttype);
+
+  const char *warnfmt = NULL;
+  bool warned = false;
+
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    case BUILT_IN_MEMSET:
+      if (!integer_zerop (args[1]))
+	{
+	  /* Diagnose setting non-copy-assignable, non-trivial or non-
+	     standard layout objects (in that order, since the latter
+	     two are subsets of one another), to (potentially) non-zero
+	     bytes.  Since the value of the bytes being written is unknown,
+	     suggest using assignment instead (if one exists).  */
+	  if (!trivassign)
+	    warnfmt = G_("%qD writing to an object of type %#qT without "
+			 "trivial copy assignment");
+	  else if (!trivial)
+	    warnfmt = G_("%qD writing to an object of non-trivial "
+			 "type %#qT; use assignment instead");
+	  else if (!stdlayout)
+	    warnfmt = G_("%qD writing to an object of non-standard-layout "
+			 "type %#qT; use assignment instead");
+	  else if (fld)
+	    {
+	      const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	      warned = warning_at (loc, OPT_Wclass_memaccess,
+				   "%qD writing to an object of type %#qT with "
+				   "%qs member %qD; use assignment instead",
+				   fndecl, desttype, access, fld);
+	    }
+	  else if (!zero_init_p (desttype))
+	    warnfmt = G_("%qD writing to an object of type %#qT containing "
+			 "a pointer to data member; use assignment instead");
+
+	  break;
+	}
+      /* Fall through.  */
+
+    case BUILT_IN_BZERO:
+      /* Similarly to the above, diagnose clearing non-trivial or non-
+	 standard layout objects, or objects of types with no assignmenmt.  */
+      if (!trivassign)
+	warnfmt = (trivcopy
+		   ? G_("%qD clearing an object of type %#qT without trivial "
+			"copy assignment; use copy constructor instead")
+		   : G_("%qD clearing an object of type %#qT without trivial "
+			"copy assignment"));
+      else if (!trivial)
+	warnfmt = G_("%qD clearing an object of non-trivial type "
+		     "%#qT; use assignment or value-initialization instead");
+      else if (!stdlayout)
+	warnfmt = G_("%qD clearing an object of non-standard-layout type "
+		     "%#qT; use assignment or value-initialization instead");
+      else if (!zero_init_p (desttype))
+	warnfmt = G_("%qD clearing an object of type %#qT containing "
+		     "a pointer-to-member; use assignment or value-"
+		     "initialization instead");
+      break;
+
+    case BUILT_IN_BCOPY:
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMPCPY:
+      /* Determine the type of the source object.  */
+      srctype = (TREE_CODE (args[1]) == NOP_EXPR
+		 ? TREE_OPERAND (args[1], 0) : args[1]);
+
+      srctype = TREE_TYPE (TREE_TYPE (srctype));
+
+      if (!trivassign)
+	warnfmt = (trivcopy
+		   ? G_("%qD writing to an object of type %#qT without trivial "
+			"copy assignment; use copy constructor instead")
+		   :  G_("%qD writing to an object of type %#qT without trivial "
+			 "copy assignment"));
+      else if (!trivially_copyable_p (desttype))
+	warnfmt = G_("%qD writing to an object of non-trivially-copyable type "
+		     "%#qT; use assignment or copy-initialization instead");
+      else if (!trivial
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  /* Warn when copying into a non-trivial object from an object
+	     of a different type other than void or char.  */
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD copying an object of non-trivial type "
+			       "%#qT from an array of %#qT",
+			       fndecl, desttype, srctype);
+	}
+      else if (fld
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD copying an object of type %#qT with "
+			       "%qs member %qD from an arrayt of %#qT; use "
+			       "assignment or copy-initialization instead",
+			       fndecl, desttype, access, fld, srctype);
+	}
+      else if (!zero_init_p (desttype)
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	warnfmt = G_("%qD writing to an object of type %#qT containing "
+		     "a pointer-to-member; use assignment or copy-"
+		     "initialization instead");
+      else if (!trivial && TREE_CODE (args[2]) == INTEGER_CST)
+	{
+	  /* Finally, warn on partial copies.  */
+	  unsigned HOST_WIDE_INT typesize
+	    = tree_to_uhwi (TYPE_SIZE_UNIT (desttype));
+	  if (unsigned HOST_WIDE_INT partial
+	      = tree_to_uhwi (args[2]) % typesize)
+	    warned = warning_at (loc, OPT_Wclass_memaccess,
+				 (typesize - partial > 1
+				  ? G_("%qD writing to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "bytes unchanged")
+				  : G_("%qD writing to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "byte unchanged")),
+				 fndecl, desttype, typesize - partial);
+	}
+      break;
+
+    default:
+      return;
+    }
+
+  if (!warned && !warnfmt)
+    return;
+
+  if (warned
+      || warning_at (loc, OPT_Wclass_memaccess, warnfmt, fndecl, desttype))
+    inform (location_of (desttype), "%#qT declared here", desttype);
+}
+
 /* Build and return a call to FN, using NARGS arguments in ARGARRAY.
    This function performs no overload resolution, conversion, or other
    high-level operations.  */
@@ -8175,6 +8428,10 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
       if (!check_builtin_function_arguments (EXPR_LOCATION (fn), vNULL, fndecl,
 					     nargs, argarray))
 	return error_mark_node;
+
+      /* Warn if the built-in writes to an object of a non-trivial type.  */
+      if (nargs)
+	maybe_warn_class_memaccess (loc, fndecl, argarray);
     }
 
     /* If it is a built-in array notation function, then the return type of
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d52a784..c166a68 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6314,6 +6314,9 @@ extern bool ctor_omit_inherited_parms		(tree);
 extern tree locate_ctor				(tree);
 extern tree implicitly_declare_fn               (special_function_kind, tree,
 						 bool, tree, tree);
+extern bool has_trivial_assign                  (tree);
+extern bool has_trivial_copy                    (tree);
+
 
 /* In optimize.c */
 extern bool maybe_clone_body			(tree);
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 8aa4f3e..b86d88b 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -1744,6 +1744,44 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
   --c_inhibit_evaluation_warnings;
 }
 
+/* Return true if the class type CTYPE has non-deleted trivial copy
+   assignment.  */
+
+bool
+has_trivial_assign (tree ctype)
+{
+  if (!CLASS_TYPE_P (ctype))
+    return true;
+
+  bool trivial_p = false;
+  bool deleted_p = false;
+
+  synthesized_method_walk (ctype, sfk_copy_assignment, false, NULL,
+			   &trivial_p, &deleted_p,
+			   NULL, false, NULL_TREE, NULL_TREE);
+
+  return trivial_p && !deleted_p;
+}
+
+/* Return true if the class type CTYPE has non-deleted trivial copy
+   constructor.  */
+
+bool
+has_trivial_copy (tree ctype)
+{
+  if (!CLASS_TYPE_P (ctype))
+    return true;
+
+  bool trivial_p = false;
+  bool deleted_p = false;
+
+  synthesized_method_walk (ctype, sfk_copy_constructor, false, NULL,
+			   &trivial_p, &deleted_p,
+			   NULL, false, NULL_TREE, NULL_TREE);
+
+  return trivial_p && !deleted_p;
+}
+
 /* DECL is a defaulted function whose exception specification is now
    needed.  Return what it should be.  */
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index a65e78f..67c0e64 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -215,7 +215,8 @@ in the following sections.
 -Wabi=@var{n}  -Wabi-tag  -Wconversion-null  -Wctor-dtor-privacy @gol
 -Wdelete-non-virtual-dtor  -Wliteral-suffix  -Wmultiple-inheritance @gol
 -Wnamespaces  -Wnarrowing @gol
--Wnoexcept  -Wnoexcept-type  -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
+-Wnoexcept  -Wnoexcept-type  -Wclass-memaccess @gol
+-Wnon-virtual-dtor  -Wreorder  -Wregister @gol
 -Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
 -Wno-non-template-friend  -Wold-style-cast @gol
 -Woverloaded-virtual  -Wno-pmf-conversions @gol
@@ -2918,6 +2919,23 @@ void g() noexcept;
 void h() @{ f(g); @} // in C++14 calls f<void(*)()>, in C++1z calls f<void(*)()noexcept>
 @end smallexample
 
+@item -Wclass-memaccess @r{(C++ and Objective-C++ only)}
+@opindex Wclass-memaccess
+Warn when the destination of a call to a raw memory function such as
+@code{memset} or @code{memcpy} is an object of class type writing into which
+might bypass the class non-trivial or deleted constructor or copy assignment,
+violate const-correctness or encapsulation, or corrupt the virtual table.
+Modifying the representation of such objects may violate invariants maintained
+by member functions of the class.  For example, the call to @code{memset}
+below is undefined becase it modifies a non-trivial class object and is,
+therefore, diagnosed.  The safe way to either initialize or clear the storage
+of objects of such types is by using the appropriate constructor or assignment
+operator, if one is available.
+@smallexample
+std::string str = "abc";
+memset (&str, 0, 3);
+@end smallexample
+The @option{-Wclass-memaccess} option is enabled by @option{-Wall}.
 
 @item -Wnon-virtual-dtor @r{(C++ and Objective-C++ only)}
 @opindex Wnon-virtual-dtor
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 13a7113..ae584c7 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -200,9 +200,16 @@ dump_register (const char *suffix, const char *swtch, const char *glob,
       m_extra_dump_files = XRESIZEVEC (struct dump_file_info,
 				       m_extra_dump_files,
 				       m_extra_dump_files_alloced);
+
+      /* Construct a new object in the space allocated above.  */
+      new (m_extra_dump_files + count) dump_file_info ();
+    }
+  else
+    {
+      /* Zero out the already constructed object.  */
+      m_extra_dump_files[count] = dump_file_info ();
     }
 
-  memset (&m_extra_dump_files[count], 0, sizeof (struct dump_file_info));
   m_extra_dump_files[count].suffix = suffix;
   m_extra_dump_files[count].swtch = swtch;
   m_extra_dump_files[count].glob = glob;
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 4724276..a65bdbf 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -1259,9 +1259,9 @@ struct compiler
   const char *cpp_spec;         /* If non-NULL, substitute this spec
 				   for `%C', rather than the usual
 				   cpp_spec.  */
-  const int combinable;          /* If nonzero, compiler can deal with
+  int combinable;               /* If nonzero, compiler can deal with
 				    multiple source files at once (IMA).  */
-  const int needs_preprocessing; /* If nonzero, source files need to
+  int needs_preprocessing;       /* If nonzero, source files need to
 				    be run through a preprocessor.  */
 };
 
diff --git a/gcc/genattrtab.c b/gcc/genattrtab.c
index 3629b5f..51dfe77 100644
--- a/gcc/genattrtab.c
+++ b/gcc/genattrtab.c
@@ -4703,8 +4703,8 @@ gen_insn_reserv (md_rtx_info *info)
   struct insn_reserv *decl = oballoc (struct insn_reserv);
   rtx def = info->def;
 
-  struct attr_desc attr;
-  memset (&attr, 0, sizeof (attr));
+  struct attr_desc attr = { };
+
   attr.name = DEF_ATTR_STRING (XSTR (def, 0));
   attr.loc = info->loc;
 
diff --git a/gcc/hash-table.h b/gcc/hash-table.h
index 0f7e21a..443d16c 100644
--- a/gcc/hash-table.h
+++ b/gcc/hash-table.h
@@ -803,7 +803,10 @@ hash_table<Descriptor, Allocator>::empty_slow ()
       m_size_prime_index = nindex;
     }
   else
-    memset (entries, 0, size * sizeof (value_type));
+    {
+      for ( ; size; ++entries, --size)
+	*entries = value_type ();
+    }
   m_n_deleted = 0;
   m_n_elements = 0;
 }
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index f5e023e..d392fcd 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1470,8 +1470,7 @@ allocate_and_init_ipcp_value (tree source)
 {
   ipcp_value<tree> *val;
 
-  val = ipcp_cst_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_cst_values_pool.allocate ()) ipcp_value<tree>();
   val->value = source;
   return val;
 }
@@ -1485,8 +1484,8 @@ allocate_and_init_ipcp_value (ipa_polymorphic_call_context source)
   ipcp_value<ipa_polymorphic_call_context> *val;
 
   // TODO
-  val = ipcp_poly_ctx_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_poly_ctx_values_pool.allocate ())
+    ipcp_value<ipa_polymorphic_call_context>();
   val->value = source;
   return val;
 }
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index 81fbb52..a96eeed 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -3711,7 +3711,7 @@ void
 ipa_free_edge_args_substructures (struct ipa_edge_args *args)
 {
   vec_free (args->jump_functions);
-  memset (args, 0, sizeof (*args));
+  *args = ipa_edge_args ();
 }
 
 /* Free all ipa_edge structures.  */
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 9a16248..dd4a092 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -6320,7 +6320,11 @@ lower_omp_ordered_clauses (gimple_stmt_iterator *gsi_p, gomp_ordered *ord_stmt,
     return;
 
   wide_int *folded_deps = XALLOCAVEC (wide_int, 2 * len - 1);
-  memset (folded_deps, 0, sizeof (*folded_deps) * (2 * len - 1));
+
+  /* wide_int is not a POD so it must be default-constructed.  */
+  for (unsigned i = 0; i != 2 * len - 1; ++i)
+    new (static_cast<void*>(folded_deps + i)) wide_int ();
+
   tree folded_dep = NULL_TREE;
   /* TRUE if the first dimension's offset is negative.  */
   bool neg_offset_p = false;
diff --git a/gcc/params.h b/gcc/params.h
index b61cff9..8b91660 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -42,7 +42,7 @@ struct param_info
 {
   /* The name used with the `--param <name>=<value>' switch to set this
      value.  */
-  const char *const option;
+  const char *option;
 
   /* The default value.  */
   int default_value;
@@ -54,7 +54,7 @@ struct param_info
   int max_value;
 
   /* A short description of the option.  */
-  const char *const help;
+  const char *help;
 
   /* The optional names corresponding to the values.  */
   const char **value_names;
diff --git a/gcc/testsuite/g++.dg/Wclass-memaccess.C b/gcc/testsuite/g++.dg/Wclass-memaccess.C
new file mode 100644
index 0000000..3676947
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wclass-memaccess.C
@@ -0,0 +1,730 @@
+/* PR c++/80560 - warn on undefined memory operations involving non-trivial
+   types
+   { dg-do compile }
+   { dg-options "-Wclass-memaccess -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern "C"
+{
+void bzero (void*, size_t);
+void* memcpy (void*, const void*, size_t);
+void* memmove (void*, const void*, size_t);
+void* mempcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+}
+
+/* Trivial can be manipulated by raw memory functions.  */
+struct Trivial { int i; unsigned bf: 1; char *s; char a[4]; };
+
+/* HasDefaultCtor is trivially copyable but should be initialized by
+   the ctor, not bzero or memset.  */
+struct HasDefaultCtor { char a[4]; HasDefaultCtor (); };
+
+/* HasCopyCtor should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  Since it's non-trivial, i should not be zeroed
+   out by bzero/memset either and should instead use assignment and/or
+   value initialization.  */
+struct HasCopyCtor { int i; HasCopyCtor (const HasCopyCtor&); };
+
+/* HasDtor should be initialized using aggregate or memberwise intialization,
+   not bzero or memset.  */
+struct HasDtor { int i; ~HasDtor (); };
+
+/* HasAssign should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  */
+struct HasAssign { int i; void operator= (HasAssign&); };
+
+/* HasVirtuals should only be manipulated by the special member functions
+   and not by bzero, memcpy, or any other raw memory function. Doing
+   otherwse might corrupt the the vtable pointer.  */
+struct HasVirtuals { int i; virtual void foo (); };
+
+/* HasConstData should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable allowing, raw memory functions to write into it would defeat
+   const-correctness.  */
+struct HasConstData { const int ci; };
+
+/* HasReference should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable, allowing raw memory functions to write into it could
+   corrupt the reference.  */
+struct HasReference { int &ci; };
+
+/* HasMemFuncPtr should only be initialized using aggregate initializatoon
+   and not cleared by bzero or written into using memset because its
+   representation is different from ordinary scalars (a null member data
+   pointer is all ones).  It can be copied into using memcpy from an object
+   of the same type or from a character buffer.  */
+struct HasMemDataPtr { int HasMemDataPtr::*p; };
+
+/* HasSomePrivateDate and HasSomeProtectedData can be initialized using
+   value initialization and should not be written to using memset with
+   a non-zero argument.  Doing otherwise would break encapsulation.  */
+struct HasSomePrivateData { int i; private: int j; };
+struct HasSomeProtectedData { int i; protected: int j; };
+
+/* Similarly to the above, HasAllPrivateDate and HasAllProtectedData should
+   only be initialized using value initializatoon and should not be written
+   to using memset with non-zero argument.  They are tested separately
+   because unlike the former classes, these are standard layout.  */
+struct HasAllPrivateData { private: int i; };
+struct HasAllProtectedData { protected: int i; };
+
+void sink (void*);
+
+#define T(fn, arglist) (fn arglist, sink (p))
+
+#if !defined TEST || TEST == TEST_TRIVIAL
+
+void test (Trivial *p, void *q, int x)
+{
+  T (bzero, (p, 1));
+  T (bzero, (p, sizeof *p));
+  T (bzero, (q, 1));
+  T (bzero, (q, sizeof *p));
+
+  T (memcpy, (p, q, 1));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (q, p, 1));
+  T (memcpy, (q, p, sizeof *p));
+
+  T (memset, (p, 0, 1));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (q, 0, 1));
+  T (memset, (q, 0, sizeof *p));
+
+  T (memset, (p, 1, 1));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (q, 1, 1));
+  T (memset, (q, 1, sizeof *p));
+
+  T (memset, (p, x, 1));
+  T (memset, (p, x, sizeof *p));
+  T (memset, (q, x, 1));
+  T (memset, (q, x, sizeof *p));
+
+  T (memmove, (p, q, 1));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (q, p, 1));
+  T (memmove, (q, p, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DEFAULT_CTOR
+
+void test (HasDefaultCtor *p, const HasDefaultCtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefaultCtor is neither trivial nor standard-layout.  The warning
+  // should mention the former since it's more permissive than the latter
+  // and so more informative.
+  T (bzero, (p, sizeof *p));   // { dg-warning ".void bzero(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefaultCtor.; use assignment or value-initialization instead" }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefaultCtor.; use assignment or value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefaultCtor.; use assignment instead" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefaultCtor.; use assignment instead" }
+
+  // Copying from another object of the same type is fine.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+
+  // Copying from a void* or character buffer is also fine.
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, n));
+
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, q, n));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, s, n));
+
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+
+  // ...but partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefaultCtor. leaves 3 bytes unchanged" } */
+  T (memmove, (p, q, 2));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefaultCtor. leaves 2 bytes unchanged" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefaultCtor. leaves 1 byte unchanged" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefaultCtor. from an array of .const int." }
+
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning ".void\\* memmove(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefaultCtor. from an array of .const int." }
+
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning ".void\\* mempcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefaultCtor. from an array of .const int." }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_COPY_CTOR
+
+void test (HasCopyCtor *p, const HasCopyCtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed because value initialization is
+  // invalid (the copy ctor makes no default ctor unavailable).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DTOR
+
+void test (HasDtor *p, const HasDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed only because it's difficult not to.
+  // Otherwise, a class that's non-trivial only because it has
+  // a non-trivial dtor can be safely zeroed out (that's what
+  // value-initializing it does).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed simply because
+  // a class with a user-defined dtor is not trivially copyable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ASSIGN
+
+void test (HasAssign *p, const HasAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_VIRTUALS
+
+void test (HasVirtuals *p, const HasVirtuals &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // Zeroing out is diagnosed because it corrupts the vtable.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is diagnosed because when used to initialize an object
+  // could incorrectly initialize the vtable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_CONST_DATA
+
+void test (HasConstData *p, const HasConstData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+
+  // The following is ill-formed because HasConstData's cannot
+  // be assigned (the assignment is implicitly deleted).  For
+  // that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object could break const correctness.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasConstData. without trivial copy assignment" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of type .struct HasConstData. without trivial copy assignment" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_REFERENCE
+
+void test (HasReference *p, const HasReference &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Similarly to HasConstData, the following is ill-formed because
+  // Hasreference cannot be assigned (the assignment is implicitly
+  // deleted).  For that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object would invalidate the reference.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasReference. without trivial copy assignment" }
+  T (bzero, (p, n));                // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 0, n));            // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, n));            // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, n));            // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of type .struct HasReference. without trivial copy assignment" }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_MEM_DATA_PTR
+
+void test (HasMemDataPtr *p, const HasMemDataPtr &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because a null member data pointer has
+  // a representation that's all bits set.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasMemDataPtr. containing a pointer-to-member" }
+  T (bzero, (p, n));                // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 0, n));            // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, n));            // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, n));            // { dg-warning "memset" }
+
+  // Copying from an object of the same type of character buffer is
+  // not diagnosed.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, n));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PRIVATE_DATA
+
+void test (HasSomePrivateData *p, const HasSomePrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PROTECTED_DATA
+
+void test (HasSomeProtectedData *p, const HasSomeProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PRIVATE_DATA
+
+void test (HasAllPrivateData *p, const HasAllPrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PROTECTED_DATA
+
+void test (HasAllProtectedData *p, const HasAllProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_EXPRESSION
+
+void test_expr (int i)
+{
+  struct TestClass: HasDefaultCtor { };
+  TestClass a, b;
+
+  static void *p;
+
+  T (bzero, (i < 0 ? &a : &b, 1));  // { dg-warning ".void bzero" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_CTOR
+
+void test_ctor ()
+{
+#undef T
+#define T(fn, arglist) (fn arglist, sink (this))
+
+  static void *p;
+
+  struct TestBase
+  {
+    TestBase ()
+    {
+      /* A ctor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+
+    ~TestBase ()
+    {
+      /* A dtor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestBaseVtable
+  {
+    TestBaseVtable ()
+    {
+      /* A ctor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    ~TestBaseVtable ()
+    {
+      /* A dtor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    virtual void foo ();
+  };
+
+  struct TestDerived: HasDefaultCtor
+  {
+    TestDerived ()
+    {
+      /* A derived class ctor is treated as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestDerivedDtor: HasDefaultCtor
+  {
+    ~TestDerivedDtor ()
+    {
+      /* A derived class dtor is treated as an ordinary function though
+	 it probably shouldn't be unless the base dtor is trivial.  But
+	 it doesn't seem worth the trouble.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+  };
+}
+
+#endif
diff --git a/gcc/tree-switch-conversion.c b/gcc/tree-switch-conversion.c
index 0a2a840..7a58209 100644
--- a/gcc/tree-switch-conversion.c
+++ b/gcc/tree-switch-conversion.c
@@ -266,7 +266,7 @@ static void
 emit_case_bit_tests (gswitch *swtch, tree index_expr,
 		     tree minval, tree range, tree maxval)
 {
-  struct case_bit_test test[MAX_CASE_BIT_TESTS];
+  struct case_bit_test test[MAX_CASE_BIT_TESTS] = { };
   unsigned int i, j, k;
   unsigned int count;
 
@@ -291,8 +291,6 @@ emit_case_bit_tests (gswitch *swtch, tree index_expr,
   int prec = TYPE_PRECISION (word_type_node);
   wide_int wone = wi::one (prec);
 
-  memset (&test, 0, sizeof (test));
-
   /* Get the edge for the default case.  */
   tmp = gimple_switch_default_label (swtch);
   default_bb = label_to_block (CASE_LABEL (tmp));
diff --git a/gcc/vec.h b/gcc/vec.h
index 755a1f8..cbdd439 100644
--- a/gcc/vec.h
+++ b/gcc/vec.h
@@ -407,6 +407,26 @@ struct GTY((user)) vec
 {
 };
 
+/* Default-construct N elements in DST.  */
+
+template <typename T>
+inline void
+vec_default_construct (T *dst, unsigned n)
+{
+  for ( ; n; ++dst, --n)
+    ::new (static_cast<void*>(dst)) T ();
+}
+
+/* Copy-construct N elements in DST from *SRC.  */
+
+template <typename T>
+inline void
+vec_copy_construct (T *dst, const T *src, unsigned n)
+{
+  for ( ; n; ++dst, ++src, --n)
+    ::new (static_cast<void*>(dst)) T (*src);
+}
+
 /* Type to provide NULL values for vec<T, A, L>.  This is used to
    provide nil initializers for vec instances.  Since vec must be
    a POD, we cannot have proper ctor/dtor for it.  To initialize
@@ -612,7 +632,7 @@ vec_safe_grow_cleared (vec<T, A, vl_embed> *&v, unsigned len CXX_MEM_STAT_INFO)
 {
   unsigned oldlen = vec_safe_length (v);
   vec_safe_grow (v, len PASS_MEM_STAT);
-  memset (&(v->address ()[oldlen]), 0, sizeof (T) * (len - oldlen));
+  vec_default_construct (v->address () + oldlen, len - oldlen);
 }
 
 
@@ -818,7 +838,7 @@ vec<T, A, vl_embed>::copy (ALONE_MEM_STAT_DECL) const
     {
       vec_alloc (new_vec, len PASS_MEM_STAT);
       new_vec->embedded_init (len, len);
-      memcpy (new_vec->address (), m_vecdata, sizeof (T) * len);
+      vec_copy_construct (new_vec->address (), m_vecdata, len);
     }
   return new_vec;
 }
@@ -835,7 +855,7 @@ vec<T, A, vl_embed>::splice (const vec<T, A, vl_embed> &src)
   if (len)
     {
       gcc_checking_assert (space (len));
-      memcpy (address () + length (), src.address (), len * sizeof (T));
+      vec_copy_construct (end (), src.address (), len);
       m_vecpfx.m_num += len;
     }
 }
@@ -1089,13 +1109,12 @@ inline void
 vec<T, A, vl_embed>::quick_grow_cleared (unsigned len)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   quick_grow (len);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
-
 /* Garbage collection support for vec<T, A, vl_embed>.  */
 
 template<typename T>
@@ -1454,7 +1473,7 @@ vec<T, va_heap, vl_ptr>::reserve (unsigned nelems, bool exact MEM_STAT_DECL)
   va_heap::reserve (m_vec, nelems, exact PASS_MEM_STAT);
   if (handle_auto_vec)
     {
-      memcpy (m_vec->address (), oldvec->address (), sizeof (T) * oldsize);
+      vec_copy_construct (m_vec->address (), oldvec->address (), oldsize);
       m_vec->m_vecpfx.m_num = oldsize;
     }
 
@@ -1616,10 +1635,10 @@ inline void
 vec<T, va_heap, vl_ptr>::safe_grow_cleared (unsigned len MEM_STAT_DECL)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   safe_grow (len PASS_MEM_STAT);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
 
diff --git a/libcpp/line-map.c b/libcpp/line-map.c
index c4b7cb2..56b7913 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -62,7 +62,8 @@ extern unsigned num_macro_tokens_counter;
 
 line_maps::~line_maps ()
 {
-  htab_delete (location_adhoc_data_map.htab);
+  if (location_adhoc_data_map.htab)
+    htab_delete (location_adhoc_data_map.htab);
 }
 
 /* Hash function for location_adhoc_data hashtable.  */
@@ -347,7 +348,7 @@ void
 linemap_init (struct line_maps *set,
 	      source_location builtin_location)
 {
-  memset (set, 0, sizeof (struct line_maps));
+  *set = line_maps ();
   set->highest_location = RESERVED_LOCATION_COUNT - 1;
   set->highest_line = RESERVED_LOCATION_COUNT - 1;
   set->location_adhoc_data_map.htab =
diff --git a/libitm/beginend.cc b/libitm/beginend.cc
index d04f3e9..c6550a3 100644
--- a/libitm/beginend.cc
+++ b/libitm/beginend.cc
@@ -431,7 +431,7 @@ GTM::gtm_transaction_cp::save(gtm_thread* tx)
   // Save everything that we might have to restore on restarts or aborts.
   jb = tx->jb;
   undolog_size = tx->undolog.size();
-  memcpy(&alloc_actions, &tx->alloc_actions, sizeof(alloc_actions));
+  alloc_actions = tx->alloc_actions;
   user_actions_size = tx->user_actions.size();
   id = tx->id;
   prop = tx->prop;
@@ -449,7 +449,7 @@ GTM::gtm_transaction_cp::commit(gtm_thread* tx)
   // commits of nested transactions. Allocation actions must be committed
   // before committing the snapshot.
   tx->jb = jb;
-  memcpy(&tx->alloc_actions, &alloc_actions, sizeof(alloc_actions));
+  tx->alloc_actions = alloc_actions;
   tx->id = id;
   tx->prop = prop;
 }
@@ -485,7 +485,7 @@ GTM::gtm_thread::rollback (gtm_transaction_cp *cp, bool aborting)
       prop = cp->prop;
       if (cp->disp != abi_disp())
 	set_abi_disp(cp->disp);
-      memcpy(&alloc_actions, &cp->alloc_actions, sizeof(alloc_actions));
+      alloc_actions = cp->alloc_actions;
       nesting = cp->nesting;
     }
   else
diff --git a/libitm/method-ml.cc b/libitm/method-ml.cc
index fcae334..b857bff 100644
--- a/libitm/method-ml.cc
+++ b/libitm/method-ml.cc
@@ -138,7 +138,11 @@ struct ml_mg : public method_group
     // This store is only executed while holding the serial lock, so relaxed
     // memory order is sufficient here.  Same holds for the memset.
     time.store(0, memory_order_relaxed);
-    memset(orecs, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
+    // The memset below isn't strictly kosher because it bypasses
+    // the non-trivial assignment operator defined by std::atomic.  Using
+    // a local void* is enough to prevent GCC from warning for this.
+    void *p = orecs;
+    memset(p, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
   }
 };
 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-24 20:48                     ` Martin Sebor
@ 2017-05-24 21:36                       ` Jason Merrill
  2017-05-28  5:02                         ` Martin Sebor
  0 siblings, 1 reply; 46+ messages in thread
From: Jason Merrill @ 2017-05-24 21:36 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Pedro Alves, Gcc Patch List

On 05/24/2017 04:28 PM, Martin Sebor wrote:
>> Attached is an updated patch with the requested changes.  I've
>> also renamed the option -Wclass-memaccess to avoid suggesting
>> that the warning focuses solely on non-trivial types, and
>> updated its wording and comments throughout to refer to value
>> initialization rather than default initialization.  Retesting
>> exposed another problem recently introduced into dumplfile.c
>> so this revision of the patch also contains a fix for that.
> 
> Emacs wasn't done saving the patch so here's the latest one with
> the dumpfile.c fix.

> +/* Return true if class TYPE meets a relaxed definition of standard-layout.
> +   In particular, the requirement that it "has all non-static data members
> +   nd bit-fields in the class and its base classes first declared in the
> +   same class" is not considered.  */
> +
> +static bool
> +almost_std_layout_p (tree type, tree *field)

It looks like this will only return false when trivial_type_p will also 
return false, so its only real function is setting *field.  Can we 
remove it and instead make first_non_public_field recursive?

> +    case BUILT_IN_MEMSET:
> +      if (!integer_zerop (args[1]))
> +	{
> +	  /* Diagnose setting non-copy-assignable, non-trivial or non-
> +	     standard layout objects (in that order, since the latter
> +	     two are subsets of one another), to (potentially) non-zero
> +	     bytes.  Since the value of the bytes being written is unknown,
> +	     suggest using assignment instead (if one exists).  */
> +	  if (!trivassign)
> +	    warnfmt = G_("%qD writing to an object of type %#qT without "
> +			 "trivial copy assignment");
> +	  else if (!trivial)
> +	    warnfmt = G_("%qD writing to an object of non-trivial "
> +			 "type %#qT; use assignment instead");

Let's put the !trivial check first.

> +	  else if (!stdlayout)
> +	    warnfmt = G_("%qD writing to an object of non-standard-layout "
> +			 "type %#qT; use assignment instead");
> +	  else if (fld)
> +	    {
> +	      const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
> +	      warned = warning_at (loc, OPT_Wclass_memaccess,
> +				   "%qD writing to an object of type %#qT with "
> +				   "%qs member %qD; use assignment instead",
> +				   fndecl, desttype, access, fld);
> +	    }
> +	  else if (!zero_init_p (desttype))
> +	    warnfmt = G_("%qD writing to an object of type %#qT containing "
> +			 "a pointer to data member; use assignment instead");
> +
> +	  break;
> +	}
> +      /* Fall through.  */
> +
> +    case BUILT_IN_BZERO:
> +      /* Similarly to the above, diagnose clearing non-trivial or non-
> +	 standard layout objects, or objects of types with no assignmenmt.  */
> +      if (!trivassign)
> +	warnfmt = (trivcopy
> +		   ? G_("%qD clearing an object of type %#qT without trivial "
> +			"copy assignment; use copy constructor instead")
> +		   : G_("%qD clearing an object of type %#qT without trivial "
> +			"copy assignment"));
> +      else if (!trivial)
> +	warnfmt = G_("%qD clearing an object of non-trivial type "
> +		     "%#qT; use assignment or value-initialization instead");

Again, let's put the !trivial check first; then we don't need to check 
trivcopy in the !trivassign branch.

> +      else if (!stdlayout)
> +	warnfmt = G_("%qD clearing an object of non-standard-layout type "
> +		     "%#qT; use assignment or value-initialization instead");
> +      else if (!zero_init_p (desttype))
> +	warnfmt = G_("%qD clearing an object of type %#qT containing "
> +		     "a pointer-to-member; use assignment or value-"
> +		     "initialization instead");
> +      break;
> +
> +    case BUILT_IN_BCOPY:
> +    case BUILT_IN_MEMCPY:
> +    case BUILT_IN_MEMMOVE:
> +    case BUILT_IN_MEMPCPY:
> +      /* Determine the type of the source object.  */
> +      srctype = (TREE_CODE (args[1]) == NOP_EXPR
> +		 ? TREE_OPERAND (args[1], 0) : args[1]);
> +
> +      srctype = TREE_TYPE (TREE_TYPE (srctype));
> +
> +      if (!trivassign)
> +	warnfmt = (trivcopy
> +		   ? G_("%qD writing to an object of type %#qT without trivial "
> +			"copy assignment; use copy constructor instead")
> +		   :  G_("%qD writing to an object of type %#qT without trivial "
> +			 "copy assignment"));
> +      else if (!trivially_copyable_p (desttype))
> +	warnfmt = G_("%qD writing to an object of non-trivially-copyable type "
> +		     "%#qT; use assignment or copy-initialization instead");

And here put the trivially-copyable test first, and drop checking 
trivcopy in the !trivassign branch.

> +      else if (!trivial
> +	       && !VOID_TYPE_P (srctype)
> +	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
> +	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
> +							      srctype))

How can this ever be true?  If !trivial, we would have already 
complained about the type being non-trivially-copyable.

> +	{
> +	  /* Warn when copying into a non-trivial object from an object
> +	     of a different type other than void or char.  */
> +	  warned = warning_at (loc, OPT_Wclass_memaccess,
> +			       "%qD copying an object of non-trivial type "
> +			       "%#qT from an array of %#qT",
> +			       fndecl, desttype, srctype);
> +	}
> +      else if (fld
> +	       && !VOID_TYPE_P (srctype)
> +	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
> +	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
> +							      srctype))
> +	{
> +	  const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
> +	  warned = warning_at (loc, OPT_Wclass_memaccess,
> +			       "%qD copying an object of type %#qT with "
> +			       "%qs member %qD from an arrayt of %#qT; use "

"array"

> +			       "assignment or copy-initialization instead",
> +			       fndecl, desttype, access, fld, srctype);
> +	}
> +      else if (!zero_init_p (desttype)
> +	       && !VOID_TYPE_P (srctype)
> +	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
> +	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
> +							      srctype))
> +	warnfmt = G_("%qD writing to an object of type %#qT containing "
> +		     "a pointer-to-member; use assignment or copy-"
> +		     "initialization instead");

This check seems unnecessary; copying from e.g. an array of 
pointer-to-members would be fine.  I think we only want to check 
zero_init_p for the bzero case above.

> +      else if (!trivial && TREE_CODE (args[2]) == INTEGER_CST)

As above, I don't see how we can ever hit this branch.

Jason

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-24 21:36                       ` Jason Merrill
@ 2017-05-28  5:02                         ` Martin Sebor
       [not found]                           ` <cc62e93c-3b49-8e2f-70b9-acdd013fe760@redhat.com>
  0 siblings, 1 reply; 46+ messages in thread
From: Martin Sebor @ 2017-05-28  5:02 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Pedro Alves, Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 2471 bytes --]

>> +/* Return true if class TYPE meets a relaxed definition of
>> standard-layout.
>> +   In particular, the requirement that it "has all non-static data
>> members
>> +   nd bit-fields in the class and its base classes first declared in the
>> +   same class" is not considered.  */
>> +
>> +static bool
>> +almost_std_layout_p (tree type, tree *field)
>
> It looks like this will only return false when trivial_type_p will also
> return false, so its only real function is setting *field.  Can we
> remove it and instead make first_non_public_field recursive?

Sure, that simplifies the diagnostic code as well.  Thanks!

>
> Let's put the !trivial check first.
>
...
> Again, let's put the !trivial check first; then we don't need to check
> trivcopy in the !trivassign branch.
>
...
> And here put the trivially-copyable test first, and drop checking
> trivcopy in the !trivassign branch.
>

I found some gaps so I've reworked the checking completely.  I've
also added a check for realloc.  Please see the new patch (sorry
for the churn).

>> +      else if (!trivial
>> +           && !VOID_TYPE_P (srctype)
>> +           && !char_type_p (TYPE_MAIN_VARIANT (srctype))
>> +           && !same_type_ignoring_top_level_qualifiers_p (desttype,
>> +                                  srctype))
>
> How can this ever be true?  If !trivial, we would have already
> complained about the type being non-trivially-copyable.

The code has changed but the warning here now fires for cases like
the following (see the test(HasDefault*, ...) overload in the new
test).

   struct S { S (); };

   void f (S *s, const int a[])
   {
     memcpy (s, a, sizeof *s);
   }

>
> "array"

Fixed, thanks.

>
>> +                   "assignment or copy-initialization instead",
>> +                   fndecl, desttype, access, fld, srctype);
>> +    }
>> +      else if (!zero_init_p (desttype)
>> +           && !VOID_TYPE_P (srctype)
>> +           && !char_type_p (TYPE_MAIN_VARIANT (srctype))
>> +           && !same_type_ignoring_top_level_qualifiers_p (desttype,
>> +                                  srctype))
>> +    warnfmt = G_("%qD writing to an object of type %#qT containing "
>> +             "a pointer-to-member; use assignment or copy-"
>> +             "initialization instead");
>
> This check seems unnecessary; copying from e.g. an array of
> pointer-to-members would be fine.  I think we only want to check
> zero_init_p for the bzero case above.

Makes sense.

Martin

[-- Attachment #2: gcc-80560.diff --]
[-- Type: text/x-patch, Size: 84530 bytes --]

PR c++/80560 - warn on undefined memory operations involving non-trivial types

gcc/c-family/ChangeLog:

	PR c++/80560
	* c.opt (-Wclass-memaccess): New option.

gcc/cp/ChangeLog:

	PR c++/80560
	* call.c (first_non_public_field, maybe_warn_class_memaccess): New
	functions.
	(build_cxx_call): Call maybe_warn_class_memaccess.
	* cp-tree.h (locate_ctor, locate_copy_ctor): Declare.
	(locate_copy_assign, has_trivial_assign, has_trivial_copy): Same.
	(has_trivial_dtor): Same.
	* method.c (locate_dtor, locate_copy_ctor): New functions.
	(locate_copy_assign, has_trivial_special_function): Same.
	(has_trivial_assign, has_trivial_copy, has_trivial_default): Same.
	(has_trivial_dtor): Same.

gcc/ChangeLog:

	PR c++/80560
	* dumpfile.c (dump_register): Avoid calling memset to initialize
	a class with a default ctor.
	* gcc.c (struct compiler): Remove const qualification.
	* genattrtab.c (gen_insn_reserv): Replace memset with initialization.
	* hash-table.h: Ditto.
	* ipa-cp.c (allocate_and_init_ipcp_value): Replace memset with
	  assignment.
	* ipa-prop.c (ipa_free_edge_args_substructures): Ditto.
	* omp-low.c (lower_omp_ordered_clauses): Replace memset with
	default ctor.
	* params.h (struct param_info): Make struct members non-const.
	* tree-switch-conversion.c (emit_case_bit_tests): Replace memset
	with default initialization.
	* vec.h (vec_copy_construct, vec_default_construct): New helper
	functions.
	(vec<T>::copy, vec<T>::splice, vec<T>::reserve): Replace memcpy
	with vec_copy_construct.
	(vect<T>::quick_grow_cleared): Replace memset with default ctor.
	(vect<T>::vec_safe_grow_cleared, vec_safe_grow_cleared): Same.
	* doc/invoke.texi (-Wclass-memaccess): Document.

libcpp/ChangeLog:

	PR c++/80560
	* line-map.c (line_maps::~line_maps): Avoid calling htab_delete
	with a null pointer.
	(linemap_init): Avoid calling memset on an object of a non-trivial
	type.

libitm/ChangeLog:

	PR c++/80560
	* beginend.cc (GTM::gtm_thread::rollback): Avoid calling memset
	on an object of a non-trivial type.
	(GTM::gtm_transaction_cp::commit): Use assignment instead of memcpy
	to copy an object.
	* method-ml.cc (orec_iterator::reinit): Avoid -Wclass-memaccess.

gcc/testsuite/ChangeLog:

	PR c++/80560
	* g++.dg/Wclass-memaccess.C: New test.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 9ad2f6e..f777b8f 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -792,6 +792,10 @@ Wnon-template-friend
 C++ ObjC++ Var(warn_nontemplate_friend) Init(1) Warning
 Warn when non-templatized friend functions are declared within a template.
 
+Wclass-memaccess
+C++ ObjC++ Var(warn_class_memaccess) Warning LangEnabledBy(C++ ObjC++, Wall)
+Warn for unsafe raw memory writes to objects of class types.
+
 Wnon-virtual-dtor
 C++ ObjC++ Var(warn_nonvdtor) Warning LangEnabledBy(C++ ObjC++,Weffc++)
 Warn about non-virtual destructors.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index e348f29..e98b37b 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8153,6 +8153,341 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
   return call;
 }
 
+/* Return the DECL of the first non-public data member of class TYPE
+   or null if none can be found.  */
+
+static tree
+first_non_public_field (tree type)
+{
+  if (!CLASS_TYPE_P (type))
+    return NULL_TREE;
+
+  for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+	continue;
+      if (TREE_STATIC (field))
+	continue;
+      if (TREE_PRIVATE (field) || TREE_PROTECTED (field))
+	return field;
+    }
+
+  int i = 0;
+
+  for (tree base_binfo, binfo = TYPE_BINFO (type);
+       BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+    {
+      tree base = TREE_TYPE (base_binfo);
+
+      if (tree field = first_non_public_field (base))
+	return field;
+    }
+
+  return NULL_TREE;
+}
+
+/* Issue a warning on a call to the built-in function FNDECL if it is
+   a raw memory write whose destination is not an object of (something
+   like) trivial or standard layout type with a non-deleted assignment
+   and copy ctor.  Detects const correctness violations, corrupting
+   references, virtual table pointers, and bypassing non-trivial
+   assignments.  */
+
+static void
+maybe_warn_class_memaccess (location_t loc, tree fndecl, tree *args)
+{
+  /* Except for bcopy where it's second, the destination pointer is
+     the first argument for all functions handled here.  Compute
+     the index of the destination and source arguments.  */
+  unsigned dstidx = DECL_FUNCTION_CODE (fndecl) == BUILT_IN_BCOPY;
+  unsigned srcidx = !dstidx;
+
+  tree dest = args[dstidx];
+  if (!dest || !TREE_TYPE (dest) || !POINTER_TYPE_P (TREE_TYPE (dest)))
+    return;
+
+  STRIP_NOPS (dest);
+
+  tree srctype = NULL_TREE;
+
+  /* Determine the type of the pointed-to object and whether it's
+     a complete class type.  */
+  tree desttype = TREE_TYPE (TREE_TYPE (dest));
+
+  if (!desttype || !COMPLETE_TYPE_P (desttype) || !CLASS_TYPE_P (desttype))
+    return;
+
+  /* Check to see if the raw memory call is made by a ctor or dtor
+     with this as the destination argument for the destination type.
+     If so, be more permissive.  */
+  if (current_function_decl
+      && (DECL_CONSTRUCTOR_P (current_function_decl)
+	  || DECL_DESTRUCTOR_P (current_function_decl))
+      && is_this_parameter (dest))
+    {
+      tree ctx = DECL_CONTEXT (current_function_decl);
+      bool special = same_type_ignoring_top_level_qualifiers_p (ctx, desttype);
+
+      tree binfo = TYPE_BINFO (ctx);
+
+      /* A ctor and dtor for a class with no bases and no virtual functions
+	 can do whatever they want.  Bail early with no further checking.  */
+      if (special && !BINFO_VTABLE (binfo) && !BINFO_N_BASE_BINFOS (binfo))
+	return;
+    }
+
+  /* True if the class is trivial and has a trivial non-deleted copy
+     assignment, copy ctor, and default ctor, respectively.  The last
+     one isn't used to issue warnings but only to decide what suitable
+     alternatives to offer as replacements for the raw memory operation.  */
+  bool trivial = trivial_type_p (desttype);
+
+  /* True if the copy assignment, copy ctor, default ctor, or destructor,
+     respectively, are deleted.  Except for DELASSIGN which is needed
+     regardless of the call, the others are set only when needed below
+     to avoid the overhead.  */
+  bool delassign = false, delcopy = false, deldflt = false, deldtor = false;
+
+  /* True if the class is has a non-deleted trivial assignment.  Set
+     below as needed to avoid overhead.  */
+  bool trivassign;
+
+  /* True if the class has a (possibly deleted) trivial copy ctor.  */
+  bool trivcopy = trivially_copyable_p (desttype);
+
+  /* Set FLD to the first private/protected member of the class.  */
+  tree fld = trivial ? first_non_public_field (desttype) : NULL_TREE;
+
+  const char *warnfmt = NULL;
+  bool warned = false;
+
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    case BUILT_IN_MEMSET:
+      if (!integer_zerop (args[1]))
+	{
+	  trivassign = has_trivial_assign (desttype, &delassign) && !delassign;
+
+	  /* Diagnose setting non-copy-assignable or non-trivial types,
+	     or types with a private member, to (potentially) non-zero
+	     bytes.  Since the value of the bytes being written is unknown,
+	     suggest using assignment instead (if one exists).  Also warn
+	     for writes into objects for which zero-initialization doesn't
+	     mean all bits clear (pointer-to-member data, where null is all
+	     bits set).  Since the value being written is (most likely)
+	     non-zero, simply suggest assignment (but not copy assignment).  */
+	  if (delassign)
+	    warnfmt = G_("%qD writing to an object of type %#qT with "
+			 "deleted copy assignment");
+	  else if (!trivassign)
+	    warnfmt = G_("%qD writing to an object of type %#qT with "
+			 "no trivial copy assignment");
+	  else if (!trivial)
+	    warnfmt = G_("%qD writing to an object of non-trivial "
+			 "type %#qT; use assignment instead");
+	  else if (fld)
+	    {
+	      const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	      warned = warning_at (loc, OPT_Wclass_memaccess,
+				   "%qD writing to an object of type %#qT with "
+				   "%qs member %qD; use assignment instead",
+				   fndecl, desttype, access, fld);
+	    }
+	  else if (!zero_init_p (desttype))
+	    warnfmt = G_("%qD writing to an object of type %#qT containing "
+			 "a pointer to data member; use assignment instead");
+
+	  break;
+	}
+      /* Fall through.  */
+
+    case BUILT_IN_BZERO:
+      /* Similarly to the above, diagnose clearing non-trivial or non-
+	 standard layout objects, or objects of types with no assignmenmt.
+	 Since the value being written is known to be zero, suggest either
+	 copy assignment, copy ctor, or default ctor as an alternative,
+	 depending on what's available.  */
+
+      trivassign = has_trivial_assign (desttype, &delassign) && !delassign;
+
+      if (delassign || !trivassign)
+	{
+	  /* To issue a better diagnostic when the type isn't trivially
+	     assignable, figure out if that's because the copy assignment
+	     is deleted.  */
+	  has_trivial_default (desttype, &deldflt);
+	}
+
+      if (delassign)
+	{
+	  if (!deldflt)
+	    warnfmt =  G_("%qD clearing an object of type %#qT with "
+			  "deleted copy assignment; use value-initialization "
+			  "instead");
+	  else
+	    warnfmt = G_("%qD clearing an object of type %#qT with "
+			 "deleted copy assignment");
+	}
+      else if (!trivassign)
+	{
+	  if (!deldflt)
+	    warnfmt =  G_("%qD clearing an object of type %#qT with no "
+			  "trivial copy assignment; use value-initialization "
+			  "instead");
+	  else
+	    warnfmt = G_("%qD clearing an object of type %#qT with "
+			 "no trivial copy assignment");
+	}
+      else if (!trivial)
+	warnfmt = G_("%qD clearing an object of non-trivial type "
+		     "%#qT; use assignment or value-initialization instead");
+      else if (!zero_init_p (desttype))
+	warnfmt = G_("%qD clearing an object of type %#qT containing "
+		     "a pointer-to-member; use assignment or value-"
+		     "initialization instead");
+      break;
+
+    case BUILT_IN_BCOPY:
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMPCPY:
+      trivassign = has_trivial_assign (desttype, &delassign) && !delassign;
+
+      /* Determine the type of the source object.  */
+      srctype = STRIP_NOPS (args[srcidx]);
+      srctype = TREE_TYPE (TREE_TYPE (srctype));
+
+      if (!trivcopy)
+	{
+	  /* To issue a better diagnostic when the type isn't trivially
+	     copyable, figure out if that's because the copy ctor is
+	     deleted.  */
+	  has_trivial_copy (desttype, &delcopy);
+	}
+
+      /* Since it's impossible to determine wheter the byte copy is
+	 being used in place of assignment to an existing object or
+	 as a substitute for initialization, assume it's the former.
+	 Determine the best alternative to use instead depending on
+	 what's not deleted.  */
+      if (delassign)
+	{
+	  if (!delcopy)
+	    warnfmt = G_("%qD writing to an object of type %#qT with "
+			 "deleted copy assignment; use copy-"
+			 "initialization instead");
+	  else
+	    warnfmt = G_("%qD writing to an object of type %#qT with "
+			 "deleted copy assignment");
+	}
+      else if (!trivassign)
+	{
+	  if (!delcopy)
+	    warnfmt = G_("%qD writing to an object of type %#qT with "
+			 "no trivial copy assignment; use copy-assignment "
+			 "or copy-initialization instead");
+	  else
+	    warnfmt = G_("%qD writing to an object of type %#qT with "
+			 "no trivial copy assignment");
+	}
+      else if (delcopy)
+	warnfmt = G_("%qD writing to an object of type %qT with "
+		     "deleted copy constructor; use copy-assignment instead");
+
+      else if (!trivcopy)
+	warnfmt = G_("%qD writing to an object of non-trivially copyable "
+		     "type %#qT; use copy-assignment or copy-initialization "
+		     "instead");
+      else if (!trivial
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  /* Warn when copying into a non-trivial object from an object
+	     of a different type other than void or char.  */
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD copying an object of non-trivial type "
+			       "%#qT from an array of %#qT",
+			       fndecl, desttype, srctype);
+	}
+      else if (fld
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD copying an object of type %#qT with "
+			       "%qs member %qD from an array of %#qT; use "
+			       "assignment or copy-initialization instead",
+			       fndecl, desttype, access, fld, srctype);
+	}
+      else if (!trivial && TREE_CODE (args[2]) == INTEGER_CST)
+	{
+	  /* Finally, warn on partial copies.  */
+	  unsigned HOST_WIDE_INT typesize
+	    = tree_to_uhwi (TYPE_SIZE_UNIT (desttype));
+	  if (unsigned HOST_WIDE_INT partial
+	      = tree_to_uhwi (args[2]) % typesize)
+	    warned = warning_at (loc, OPT_Wclass_memaccess,
+				 (typesize - partial > 1
+				  ? G_("%qD writing to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "bytes unchanged")
+				  : G_("%qD writing to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "byte unchanged")),
+				 fndecl, desttype, typesize - partial);
+	}
+      break;
+
+    case BUILT_IN_REALLOC:
+
+      if (!trivcopy)
+	{
+	  /* To issue a better diagnostic when the type isn't trivially
+	     copyable, figure out if that's because the copy ctor is
+	     deleted.  */
+	  has_trivial_copy (desttype, &delcopy);
+	}
+
+      if (delcopy)
+	warnfmt = G_("%qD moving an object of type %qT with "
+		     "deleted copy constructor");
+
+      else if (!trivcopy)
+	warnfmt = G_("%qD moving an object of non-trivially copyable type "
+		     "%#qT; use %<new%> and %<delete%> instead");
+      else if (has_trivial_dtor (desttype, &deldtor) && deldtor)
+	warnfmt = G_("%qD moving an object of type %#qT with deleted "
+		     "destructor");
+      else if (!trivial
+	       && TREE_CODE (args[1]) == INTEGER_CST
+	       && tree_int_cst_lt (args[1], TYPE_SIZE_UNIT (desttype)))
+	{
+	  /* Finally, warn on reallocation into insufficient space.  */
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD moving an object of non-trivial type "
+			       "%#qT and size %E into a region of size %E",
+			       fndecl, desttype, TYPE_SIZE_UNIT (desttype),
+			       args[1]);
+	}
+      break;
+
+    default:
+      return;
+    }
+
+  if (!warned && !warnfmt)
+    return;
+
+  if (warned
+      || warning_at (loc, OPT_Wclass_memaccess, warnfmt, fndecl, desttype))
+    inform (location_of (desttype), "%#qT declared here", desttype);
+}
+
 /* Build and return a call to FN, using NARGS arguments in ARGARRAY.
    This function performs no overload resolution, conversion, or other
    high-level operations.  */
@@ -8185,6 +8520,10 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
       if (!check_builtin_function_arguments (EXPR_LOCATION (fn), vNULL, fndecl,
 					     nargs, argarray))
 	return error_mark_node;
+
+      /* Warn if the built-in writes to an object of a non-trivial type.  */
+      if (nargs)
+	maybe_warn_class_memaccess (loc, fndecl, argarray);
     }
 
     /* If it is a built-in array notation function, then the return type of
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8120b93..6cd1f17 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6121,6 +6121,9 @@ extern tree lazily_declare_fn			(special_function_kind,
 extern tree skip_artificial_parms_for		(const_tree, tree);
 extern int num_artificial_parms_for		(const_tree);
 extern tree make_alias_for			(tree, tree);
+extern tree locate_ctor				(tree);
+extern tree locate_copy_ctor			(tree);
+extern tree locate_copy_assign			(tree);
 extern tree get_copy_ctor			(tree, tsubst_flags_t);
 extern tree get_copy_assign			(tree);
 extern tree get_default_ctor			(tree);
@@ -6128,9 +6131,13 @@ extern tree get_dtor				(tree, tsubst_flags_t);
 extern tree strip_inheriting_ctors		(tree);
 extern tree inherited_ctor_binfo		(tree);
 extern bool ctor_omit_inherited_parms		(tree);
-extern tree locate_ctor				(tree);
 extern tree implicitly_declare_fn               (special_function_kind, tree,
 						 bool, tree, tree);
+extern bool has_trivial_assign                  (tree, bool *);
+extern bool has_trivial_copy                    (tree, bool *);
+extern bool has_trivial_default                 (tree, bool *);
+extern bool has_trivial_dtor                    (tree, bool *);
+
 
 /* In optimize.c */
 extern bool maybe_clone_body			(tree);
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index b4c1f60..9917686 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -1046,6 +1046,21 @@ locate_fn_flags (tree type, tree name, tree argtype, int flags,
     return fn;
 }
 
+/* Locate the destructor of TYPE and return it irrespective of whether
+   it's accessible.  Return null when not found.  */
+
+tree
+locate_dtor (tree type)
+{
+  tree fn;
+
+  fn = locate_fn_flags (type, complete_dtor_identifier, NULL_TREE,
+			LOOKUP_SPECULATIVE, tf_none);
+  if (fn == error_mark_node)
+    return NULL_TREE;
+  return fn;
+}
+
 /* Locate the dtor of TYPE.  */
 
 tree
@@ -1058,7 +1073,8 @@ get_dtor (tree type, tsubst_flags_t complain)
   return fn;
 }
 
-/* Locate the default ctor of TYPE.  */
+/* Locate the default ctor of TYPE and return it irrespective of whether
+   it's accessible.  Return null when not found.  */
 
 tree
 locate_ctor (tree type)
@@ -1086,7 +1102,24 @@ get_default_ctor (tree type)
   return fn;
 }
 
-/* Locate the copy ctor of TYPE.  */
+/* Locate the copy ctor of TYPE and return it if it's accessible,
+   otherwise return null.  */
+
+tree
+locate_copy_ctor (tree type)
+{
+  int quals = (TYPE_HAS_CONST_COPY_CTOR (type)
+	       ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED);
+  tree argtype = build_stub_type (type, quals, false);
+
+  tree fn = locate_fn_flags (type, complete_ctor_identifier, argtype,
+			     LOOKUP_NORMAL, tf_none);
+  if (fn == error_mark_node)
+    return NULL_TREE;
+  return fn;
+}
+
+/* Likewise, but give any appropriate errors.  */
 
 tree
 get_copy_ctor (tree type, tsubst_flags_t complain)
@@ -1101,7 +1134,22 @@ get_copy_ctor (tree type, tsubst_flags_t complain)
   return fn;
 }
 
-/* Locate the copy assignment operator of TYPE.  */
+/* Locate the copy assignment operator of TYPE and return it if it's
+   accessible, otherwise return null.  */
+
+tree
+locate_copy_assign (tree type)
+{
+  int quals = (TYPE_HAS_CONST_COPY_ASSIGN (type)
+	       ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED);
+  tree argtype = build_stub_type (type, quals, false);
+
+  tree fn = locate_fn_flags (type, cp_assignment_operator_id (NOP_EXPR), argtype,
+			     LOOKUP_NORMAL, tf_none);
+  if (fn == error_mark_node)
+    return NULL_TREE;
+  return fn;
+}
 
 tree
 get_copy_assign (tree type)
@@ -1716,6 +1764,84 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
   --c_inhibit_evaluation_warnings;
 }
 
+/* Return true if the class type CTYPE has a trivial special function
+   SFK and set *DELETED_P to true if it's deleted, otherwise to false.  */
+
+static bool
+has_trivial_special_function (tree ctype, special_function_kind sfk,
+			      bool *deleted_p)
+{
+  if (!CLASS_TYPE_P (ctype))
+    return true;
+
+  bool trivial_p = false;
+
+  synthesized_method_walk (ctype, sfk, false, NULL, &trivial_p, deleted_p,
+			   NULL, false, NULL_TREE, NULL_TREE);
+
+  return trivial_p;
+}
+
+/* Return true if the class type CTYPE has a trivial copy assignment
+   and set *DELETED_P to true if it's deleted, otherwise to false.  */
+
+bool
+has_trivial_assign (tree ctype, bool *deleted_p)
+{
+  bool ret = has_trivial_special_function (ctype, sfk_copy_assignment,
+					   deleted_p);
+
+  /* In C++ 98 mode avoid trying to locate the copy assignment operator
+     because rather than quietly returning a result it can fail with
+     a hard error .  */
+  if (cxx_dialect >= cxx11 && !locate_copy_assign (ctype))
+    *deleted_p = true;
+
+  return ret;
+}
+
+/* Return true if the class type CTYPE has a trivial copy constructor
+   and set *DELETED_P to true if it's deleted, otherwise to false.  */
+
+bool
+has_trivial_copy (tree ctype, bool *deleted_p)
+{
+  bool ret = has_trivial_special_function (ctype, sfk_copy_constructor,
+					   deleted_p);
+  if (!locate_copy_ctor (ctype))
+    *deleted_p = true;
+
+  return ret;
+}
+
+/* Return true if the class type CTYPE has a trivial default constructor
+   and set *DELETED_P to true if it's deleted, otherwise to false.  */
+
+bool
+has_trivial_default (tree ctype, bool *deleted_p)
+{
+  bool ret = has_trivial_special_function (ctype, sfk_constructor,
+					   deleted_p);
+  if (!locate_ctor (ctype))
+    *deleted_p = true;
+
+  return ret;
+}
+
+/* Return true if the class type CTYPE has a trivial destructor
+   and set *DELETED_P to true if it's deleted, otherwise to false.  */
+
+bool
+has_trivial_dtor (tree ctype, bool *deleted_p)
+{
+  bool ret = has_trivial_special_function (ctype, sfk_destructor,
+					   deleted_p);
+  if (!locate_dtor (ctype))
+    *deleted_p = true;
+
+  return ret;
+}
+
 /* DECL is a defaulted function whose exception specification is now
    needed.  Return what it should be.  */
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 57c9678..daf1601 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -215,7 +215,8 @@ in the following sections.
 -Wabi=@var{n}  -Wabi-tag  -Wconversion-null  -Wctor-dtor-privacy @gol
 -Wdelete-non-virtual-dtor  -Wliteral-suffix  -Wmultiple-inheritance @gol
 -Wnamespaces  -Wnarrowing @gol
--Wnoexcept  -Wnoexcept-type  -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
+-Wnoexcept  -Wnoexcept-type  -Wclass-memaccess @gol
+-Wnon-virtual-dtor  -Wreorder  -Wregister @gol
 -Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
 -Wno-non-template-friend  -Wold-style-cast @gol
 -Woverloaded-virtual  -Wno-pmf-conversions @gol
@@ -2913,6 +2914,23 @@ void g() noexcept;
 void h() @{ f(g); @} // in C++14 calls f<void(*)()>, in C++1z calls f<void(*)()noexcept>
 @end smallexample
 
+@item -Wclass-memaccess @r{(C++ and Objective-C++ only)}
+@opindex Wclass-memaccess
+Warn when the destination of a call to a raw memory function such as
+@code{memset} or @code{memcpy} is an object of class type writing into which
+might bypass the class non-trivial or deleted constructor or copy assignment,
+violate const-correctness or encapsulation, or corrupt the virtual table.
+Modifying the representation of such objects may violate invariants maintained
+by member functions of the class.  For example, the call to @code{memset}
+below is undefined becase it modifies a non-trivial class object and is,
+therefore, diagnosed.  The safe way to either initialize or clear the storage
+of objects of such types is by using the appropriate constructor or assignment
+operator, if one is available.
+@smallexample
+std::string str = "abc";
+memset (&str, 0, 3);
+@end smallexample
+The @option{-Wclass-memaccess} option is enabled by @option{-Wall}.
 
 @item -Wnon-virtual-dtor @r{(C++ and Objective-C++ only)}
 @opindex Wnon-virtual-dtor
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 6b9a47c..fe89a15 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -194,9 +194,16 @@ dump_register (const char *suffix, const char *swtch, const char *glob,
       m_extra_dump_files = XRESIZEVEC (struct dump_file_info,
 				       m_extra_dump_files,
 				       m_extra_dump_files_alloced);
+
+      /* Construct a new object in the space allocated above.  */
+      new (m_extra_dump_files + count) dump_file_info ();
+    }
+  else
+    {
+      /* Zero out the already constructed object.  */
+      m_extra_dump_files[count] = dump_file_info ();
     }
 
-  memset (&m_extra_dump_files[count], 0, sizeof (struct dump_file_info));
   m_extra_dump_files[count].suffix = suffix;
   m_extra_dump_files[count].swtch = swtch;
   m_extra_dump_files[count].glob = glob;
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 120c5c0..0ef4f10 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -1259,9 +1259,9 @@ struct compiler
   const char *cpp_spec;         /* If non-NULL, substitute this spec
 				   for `%C', rather than the usual
 				   cpp_spec.  */
-  const int combinable;          /* If nonzero, compiler can deal with
+  int combinable;               /* If nonzero, compiler can deal with
 				    multiple source files at once (IMA).  */
-  const int needs_preprocessing; /* If nonzero, source files need to
+  int needs_preprocessing;       /* If nonzero, source files need to
 				    be run through a preprocessor.  */
 };
 
diff --git a/gcc/genattrtab.c b/gcc/genattrtab.c
index 3629b5f..51dfe77 100644
--- a/gcc/genattrtab.c
+++ b/gcc/genattrtab.c
@@ -4703,8 +4703,8 @@ gen_insn_reserv (md_rtx_info *info)
   struct insn_reserv *decl = oballoc (struct insn_reserv);
   rtx def = info->def;
 
-  struct attr_desc attr;
-  memset (&attr, 0, sizeof (attr));
+  struct attr_desc attr = { };
+
   attr.name = DEF_ATTR_STRING (XSTR (def, 0));
   attr.loc = info->loc;
 
diff --git a/gcc/hash-table.h b/gcc/hash-table.h
index 0f7e21a..443d16c 100644
--- a/gcc/hash-table.h
+++ b/gcc/hash-table.h
@@ -803,7 +803,10 @@ hash_table<Descriptor, Allocator>::empty_slow ()
       m_size_prime_index = nindex;
     }
   else
-    memset (entries, 0, size * sizeof (value_type));
+    {
+      for ( ; size; ++entries, --size)
+	*entries = value_type ();
+    }
   m_n_deleted = 0;
   m_n_elements = 0;
 }
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 5e1d94c..2705cbc 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1472,8 +1472,7 @@ allocate_and_init_ipcp_value (tree source)
 {
   ipcp_value<tree> *val;
 
-  val = ipcp_cst_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_cst_values_pool.allocate ()) ipcp_value<tree>();
   val->value = source;
   return val;
 }
@@ -1487,8 +1486,8 @@ allocate_and_init_ipcp_value (ipa_polymorphic_call_context source)
   ipcp_value<ipa_polymorphic_call_context> *val;
 
   // TODO
-  val = ipcp_poly_ctx_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_poly_ctx_values_pool.allocate ())
+    ipcp_value<ipa_polymorphic_call_context>();
   val->value = source;
   return val;
 }
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index 5819f78..212f423 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -3730,7 +3730,7 @@ void
 ipa_free_edge_args_substructures (struct ipa_edge_args *args)
 {
   vec_free (args->jump_functions);
-  memset (args, 0, sizeof (*args));
+  *args = ipa_edge_args ();
 }
 
 /* Free all ipa_edge structures.  */
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 9cc2996..dc93dce 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -6292,7 +6292,11 @@ lower_omp_ordered_clauses (gimple_stmt_iterator *gsi_p, gomp_ordered *ord_stmt,
     return;
 
   wide_int *folded_deps = XALLOCAVEC (wide_int, 2 * len - 1);
-  memset (folded_deps, 0, sizeof (*folded_deps) * (2 * len - 1));
+
+  /* wide_int is not a POD so it must be default-constructed.  */
+  for (unsigned i = 0; i != 2 * len - 1; ++i)
+    new (static_cast<void*>(folded_deps + i)) wide_int ();
+
   tree folded_dep = NULL_TREE;
   /* TRUE if the first dimension's offset is negative.  */
   bool neg_offset_p = false;
diff --git a/gcc/params.h b/gcc/params.h
index b61cff9..8b91660 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -42,7 +42,7 @@ struct param_info
 {
   /* The name used with the `--param <name>=<value>' switch to set this
      value.  */
-  const char *const option;
+  const char *option;
 
   /* The default value.  */
   int default_value;
@@ -54,7 +54,7 @@ struct param_info
   int max_value;
 
   /* A short description of the option.  */
-  const char *const help;
+  const char *help;
 
   /* The optional names corresponding to the values.  */
   const char **value_names;
diff --git a/gcc/testsuite/g++.dg/Wclass-memaccess.C b/gcc/testsuite/g++.dg/Wclass-memaccess.C
new file mode 100644
index 0000000..8fbd7cc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wclass-memaccess.C
@@ -0,0 +1,1154 @@
+/* PR c++/80560 - warn on undefined memory operations involving non-trivial
+   types
+   { dg-do compile }
+   { dg-options "-Wclass-memaccess -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern "C"
+{
+void* memcpy (void*, const void*, size_t);
+void* memmove (void*, const void*, size_t);
+void* mempcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+void* realloc (void*, size_t);
+}
+
+/* Ordinary bzcopy and bzero aren't recognized as special.  */
+#define bcopy __builtin_bcopy
+#define bzero __builtin_bzero
+
+void sink (void*);
+
+#define T(fn, arglist) ((fn arglist), sink (p))
+
+#if !defined TEST || TEST == TEST_TRIVIAL
+
+/* Trivial can be manipulated by raw memory functions.  */
+struct Trivial { int i; unsigned bf: 1; char *s; char a[4]; };
+
+void test (Trivial *p, void *q, int x)
+{
+  const size_t n = x;
+
+  T (bzero, (p, 1));
+  T (bzero, (p, n));
+  T (bzero, (p, sizeof *p));
+  T (bzero, (q, 1));
+  T (bzero, (q, n));
+  T (bzero, (q, sizeof *p));
+
+  T (bcopy, (p, q, 1));
+  T (bcopy, (p, q, n));
+  T (bcopy, (p, q, sizeof *p));
+  T (bcopy, (q, p, 1));
+  T (bcopy, (q, p, n));
+  T (bcopy, (q, p, sizeof *p));
+
+  T (memcpy, (p, q, 1));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (q, p, 1));
+  T (memcpy, (q, p, n));
+  T (memcpy, (q, p, sizeof *p));
+
+  T (memset, (p, 0, 1));
+  T (memset, (p, 0, n));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (q, 0, 1));
+  T (memset, (q, 0, n));
+  T (memset, (q, 0, sizeof *p));
+
+  T (memset, (p, 1, 1));
+  T (memset, (p, 1, n));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (q, 1, 1));
+  T (memset, (q, 1, n));
+  T (memset, (q, 1, sizeof *p));
+
+  T (memset, (p, x, 1));
+  T (memset, (p, x, n));
+  T (memset, (p, x, sizeof *p));
+  T (memset, (q, x, 1));
+  T (memset, (q, x, n));
+  T (memset, (q, x, sizeof *p));
+
+  T (memmove, (p, q, 1));
+  T (memmove, (p, q, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (q, p, 1));
+  T (memmove, (q, p, n));
+  T (memmove, (q, p, sizeof *p));
+
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+
+  T (q = realloc, (q, 1));
+  T (q = realloc, (q, n));
+  T (q = realloc, (q, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DEFAULT_CTOR
+
+/* HasDefault is trivially copyable but should be initialized by
+   the ctor, not bzero or memset.  */
+struct HasDefault { char a[4]; HasDefault (); };
+
+void test (HasDefault *p, const HasDefault &x,
+	   void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefault is neither trivial nor standard-layout.  The warning
+  // should mention the former since it's more permissive than the latter
+  // and so more informative.
+  T (bzero, (p, sizeof *p));   // { dg-warning "bzero(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefault.; use assignment or value-initialization instead" }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefault.; use assignment or value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefault.; use assignment instead" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefault.; use assignment instead" }
+
+  // Copying from another object of the same type is fine.
+  T (bcopy, (&x, p, sizeof *p));
+  T (bcopy, (&x, p, n));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+
+  // Copying from a void* or character buffer is also fine.
+  T (bcopy, (q, p, sizeof *p));
+  T (bcopy, (q, p, n));
+  T (bcopy, (s, p, sizeof *p));
+  T (bcopy, (s, p, n));
+
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, n));
+
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, q, n));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, s, n));
+
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+
+  // ...but partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefault. leaves 3 bytes unchanged" } */
+  T (memmove, (p, q, 2));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefault. leaves 2 bytes unchanged" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefault. leaves 1 byte unchanged" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .const int." }
+  extern long *ip;
+  T (memcpy, (p, ip, sizeof *p));  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .long." }
+
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning ".void\\* memmove(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .const int." }
+
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning ".void\\* mempcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .const int." }
+
+  // Reallocating is the same as calling memcpy except that only
+  // shrinking reallocation is diagnosed.
+  T (q = realloc, (p, 1));   // { dg-warning "moving an object of non-trivial type .struct HasDefault. and size 4 into a region of size 1" }
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_COPY
+
+/* HasCopy should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  Since it's non-trivial, it should not be zeroed
+   out by bzero/memset either and should instead use assignment and/or
+   value initialization.  */
+struct HasCopy { int i; HasCopy (const HasCopy&); };
+
+void test (HasCopy *p, const HasCopy &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because value initialization is
+  // invalid (the copy ctor makes no default ctor unavailable).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (bcopy, (&x, p, sizeof *p));    // { dg-warning "bcopy" }
+  T (bcopy, (q, p, sizeof *p));     // { dg-warning "bcopy" }
+  T (bcopy, (s, p, sizeof *p));     // { dg-warning "bcopy" }
+  T (bcopy, (ia, p, sizeof *p));    // { dg-warning "bcopy" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_PRIVATE_COPY
+
+/* HasPrivateCopy cannot be copied using memcpy or memmove.  Since it's
+   non-trivial, it it should not be zeroed out by bzero/memset either
+   and should instead use assignment and/or value initialization.  */
+struct HasPrivateCopy {
+  int i;
+private:
+  HasPrivateCopy (const HasPrivateCopy&);
+};
+
+void test (HasPrivateCopy *p, const HasPrivateCopy &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because value initialization is
+  // invalid (the copy ctor makes no default ctor unavailable).
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of non-trivial type .struct HasPrivateCopy.; use assignment or value-initialization instead" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .HasPrivateCopy. with deleted copy constructor; use copy-assignment instead" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DTOR
+
+/* HasDtor should be initialized using aggregate or memberwise intialization,
+   not bzero or memset.  */
+struct HasDtor { int i; ~HasDtor (); };
+
+void test (HasDtor *p, const HasDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed only because it's difficult not to.
+  // Otherwise, a class that's non-trivial only because it has
+  // a non-trivial dtor can be safely zeroed out (that's what
+  // value-initializing it does).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed simply because
+  // a class with a user-defined dtor is not trivially copyable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DELETED_DTOR
+
+// HasDeletedDtor is trivial so clearing and cpying it is okay.
+// Relocation would bypass the deleted dtor and so it's diagnosed.
+
+struct HasDeletedDtor
+{
+  int i;
+  ~HasDeletedDtor () = delete;      // { dg-warning "defaulted and deleted functions" "c++ 98" { target { "c++98_only" } } }
+};
+
+void test (HasDeletedDtor *p, const HasDeletedDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (p, i, sizeof *p));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  // Reallocating is diagnosed.
+  T (q = realloc, (p, 1));          // { dg-warning "moving an object of type .struct HasDeletedDtor. with deleted destructor" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_PRIVATE_DTOR
+
+// Unlike HasDeletedDtor, HasPrivateDtor is okay to zero-out and copy
+// but not relocate because doing so would bypass the deleted dtor..
+
+struct HasPrivateDtor
+{
+  int i;
+private:
+  ~HasPrivateDtor ();
+};
+
+void test (HasPrivateDtor *p, const HasPrivateDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of non-trivial type .struct HasPrivateDtor.; use assignment or value-initialization instead" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of non-trivially copyable type .struct HasPrivateDtor.; use copy-assignment or copy-initialization instead" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is diagnosed.
+  T (q = realloc, (p, 1));          // { dg-warning "moving an object of non-trivially copyable type .struct HasPrivateDtor." }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ASSIGN
+
+/* HasAssign should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  */
+struct HasAssign { int i; void operator= (HasAssign&); };
+
+void test (HasAssign *p, const HasAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_VIRTUALS
+
+/* HasVirtuals should only be manipulated by the special member functions
+   and not by bzero, memcpy, or any other raw memory function. Doing
+   otherwse might corrupt the the vtable pointer.  */
+struct HasVirtuals { int i; virtual void foo (); };
+
+void test (HasVirtuals *p, const HasVirtuals &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it corrupts the vtable.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is diagnosed because when used to initialize an object
+  // could incorrectly initialize the vtable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_CONST_DATA
+
+/* HasConstData should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable allowing, raw memory functions to write into it would defeat
+   const-correctness.  */
+struct HasConstData { const char a[4]; };
+
+void test (HasConstData *p, const HasConstData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // The following is ill-formed because HasConstData's cannot
+  // be assigned (the assignment is implicitly deleted).  For
+  // that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object could break const correctness.
+  // Since copy assignment is deleted, verify that it's not
+  // suggested as a possible alternative.  No other good
+  // alternative is available.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasConstData. with deleted copy assignment \\\[" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "clearing an object of type .struct HasConstData. with deleted copy assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of type .struct HasConstData. with deleted copy assignment; use copy-initialization instead" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is not diagnosed except in C++ 98 due to a bug.
+  T (q = realloc, (p, 1));          // { dg-warning "moving an object of non-trivially copyable type .struct HasConstData.; use .new. and .delete. instead" "c++98" { target { c++98_only } } }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" "c++98" { target { c++98_only } } }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" "c++98" { target { c++98_only } } }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_REFERENCE
+
+/* HasReference should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable, allowing raw memory functions to write into it could
+   corrupt the reference.  */
+struct HasReference { int &ci; };
+
+void test (HasReference *p, const HasReference &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Similarly to HasConstData, the following is ill-formed because
+  // Hasreference cannot be assigned (the assignment is implicitly
+  // deleted).  For that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object would invalidate the reference.
+  // Since copy-assignment is deleted verify it's not suggested
+  // as an alternative.  (C++ 11 and later only; C++ 98 is broken).
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasReference. with deleted copy assignment \\\[" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "clearing an object of type .struct HasReference. with deleted copy assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (bzero, (p, n));                // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 0, n));            // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, n));            // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, n));            // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of type .struct HasReference. with deleted copy assignment; use copy-initialization instead" }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is not diagnosed because a type with a reference
+  // is (perhaps surprisingly) trivially copyable.  It is diagnosed
+  // in C++ 98 because of a bug, but it seems like it should be
+  // diagnosed in all modes.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_MEM_DATA_PTR
+
+/* HasMemDataPtr should only be initialized using aggregate initializatoon
+   and not cleared by bzero or written into using memset because its
+   representation is different from ordinary scalars (a null member data
+   pointer is all ones).  It can be copied into using memcpy from an object
+   of the same type or from a character buffer.  */
+struct HasMemDataPtr { int HasMemDataPtr::*p; };
+
+void test (HasMemDataPtr *p, const HasMemDataPtr &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because a null member data pointer has
+  // a representation that's all bits set.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasMemDataPtr. containing a pointer-to-member" }
+  T (bzero, (p, n));                // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 0, n));            // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, n));            // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, n));            // { dg-warning "memset" }
+
+  // Copying is not diagnosed.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, n));
+  T (memcpy, (p, ia, sizeof *p));
+  T (memcpy, (p, ia, n));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PRIVATE_DATA
+
+/* HasSomePrivateData can be initialized using value initialization
+   and should not be written to using memset with a non-zero argument.
+   Doing otherwise would break encapsulation.  */
+struct HasSomePrivateData { char a[2]; private: char b[2]; };
+
+void test (HasSomePrivateData *p, const HasSomePrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PROTECTED_DATA
+
+/* Similarly to HasSomePrivateData, HasSomeProtectedData can be
+   initialized using value initialization and should not be written
+   to using memset with a non-zero argument.  Doing otherwise would
+   break encapsulation.  */
+struct HasSomeProtectedData { char a[2]; protected: char b[2]; };
+
+void test (HasSomeProtectedData *p, const HasSomeProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PRIVATE_DATA
+
+/* Similarly to HasSomePrivateData, HasAllPrivateData should only be
+   initialized using value initializatoon and should not be written
+   to using memset with non-zero argument.  They are tested separately
+   because unlike the former classes, these are standard layout.  */
+struct HasAllPrivateData { private: char a[4]; };
+
+void test (HasAllPrivateData *p, const HasAllPrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PROTECTED_DATA
+
+/* Similarly to HasSomeProtectedData, HasAllProtectedData should only
+   be initialized using value initializatoon and should not be written
+   to using memset with non-zero argument.  They are tested separately
+   because unlike the former classes, these are standard layout.  */
+struct HasAllProtectedData { protected: char a[4]; };
+
+void test (HasAllProtectedData *p, const HasAllProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_DEFAULT_CTOR_PRIVATE_ASSIGN
+
+/* Used to verify suggested alternatives.  */
+struct HasDefaultPrivateAssign
+{
+  char a[4];
+  HasDefaultPrivateAssign ();
+private:
+  void operator= (HasDefaultPrivateAssign&);
+};
+
+void test (HasDefaultPrivateAssign *p, const HasDefaultPrivateAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefaultPrivateAssign isn't trivial or assignable.  Verify
+  // that the alternative suggested in the warning is to use copy or
+  // default but not assignment.
+  T (bzero, (p, sizeof *p));   // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultPrivateAssign. with deleted copy assignment; use value-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultPrivateAssign. with no trivial copy assignment; use value-initialization instead" "c++ 98" { target { c++98_only } } .-1 }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy assignment; use value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy assignment" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy assignment" }
+
+  // Copying from another object of the same type is diagnosed because
+  // the copy assignment is inaccessible.  Verify that the suggested
+  // alternative is not copy assignment (C++ 98 is busted).
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with deleted copy assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy assignment" "c++98" { target c++98_only } .-1 }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+
+  // Similarly for copying from a void* or character buffer.
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with deleted copy assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy assignment" "c++98" { target c++98_only } ,-1 }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, q, n));           // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, n));           // { dg-warning "memmove" }
+
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, n));           // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, n));           // { dg-warning "mempcpy" }
+
+  // Same for partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy assignment" } */
+  T (memmove, (p, q, 2));   // { dg-warning "memmove" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "mempcpy" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning "writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy assignment." }
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning "memmove" }
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_DEFAULT_CTOR_DELETED_ASSIGN
+
+/* Used to verify suggested alternatives.  */
+struct HasDefaultDeletedAssign
+{
+  char a[4];
+  HasDefaultDeletedAssign ();
+private:
+  void operator= (HasDefaultDeletedAssign&);
+};
+
+void test (HasDefaultDeletedAssign *p, const HasDefaultDeletedAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefaultDeletedAssign isn't trivial or assignable.  Verify
+  // that the alternative suggested in the warning is to use copy or
+  // default but not assignment.
+  T (bzero, (p, sizeof *p));   // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultDeletedAssign. with deleted copy assignment; use value-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultDeletedAssign. with no trivial copy assignment; use value-initialization instead" "c++ 98" { target { c++98_only } } .-1 }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy assignment; use value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy assignment" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy assignment" }
+
+  // Copying from another object of the same type is diagnosed because
+  // the copy assignment is inaccessible.  Verify that the suggested
+  // alternative is not copy assignment (C++ 98 is busted).
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with deleted copy assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy assignment" "c++98" { target c++98_only } .-1 }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+
+  // Similarly for copying from a void* or character buffer.
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with deleted copy assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy assignment" "c++98" { target c++98_only } ,-1 }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, q, n));           // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, n));           // { dg-warning "memmove" }
+
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, n));           // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, n));           // { dg-warning "mempcpy" }
+
+  // Same for partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy assignment" } */
+  T (memmove, (p, q, 2));   // { dg-warning "memmove" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "mempcpy" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning "writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy assignment." }
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning "memmove" }
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_EXPRESSION
+
+void test_expr (int i)
+{
+  struct TestClass { TestClass () { } };
+  TestClass a, b;
+
+  static void *p;
+
+  T (bzero, (i < 0 ? &a : &b, 1));  // { dg-warning "bzero" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_CTOR
+
+void test_ctor ()
+{
+#undef T
+#define T(fn, arglist) (fn arglist, sink (this))
+
+  static void *p;
+
+  struct TestBase
+  {
+    TestBase ()
+    {
+      /* A ctor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+
+    ~TestBase ()
+    {
+      /* A dtor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestBaseVtable
+  {
+    TestBaseVtable ()
+    {
+      /* A ctor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    ~TestBaseVtable ()
+    {
+      /* A dtor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    virtual void foo ();
+  };
+
+  struct TestDerived: HasDefault
+  {
+    TestDerived ()
+    {
+      /* A derived class ctor is treated as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestDerivedDtor: HasDefault
+  {
+    ~TestDerivedDtor ()
+    {
+      /* A derived class dtor is treated as an ordinary function though
+	 it probably shouldn't be unless the base dtor is trivial.  But
+	 it doesn't seem worth the trouble.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+  };
+}
+
+#endif
diff --git a/gcc/tree-switch-conversion.c b/gcc/tree-switch-conversion.c
index 0a2a840..7a58209 100644
--- a/gcc/tree-switch-conversion.c
+++ b/gcc/tree-switch-conversion.c
@@ -266,7 +266,7 @@ static void
 emit_case_bit_tests (gswitch *swtch, tree index_expr,
 		     tree minval, tree range, tree maxval)
 {
-  struct case_bit_test test[MAX_CASE_BIT_TESTS];
+  struct case_bit_test test[MAX_CASE_BIT_TESTS] = { };
   unsigned int i, j, k;
   unsigned int count;
 
@@ -291,8 +291,6 @@ emit_case_bit_tests (gswitch *swtch, tree index_expr,
   int prec = TYPE_PRECISION (word_type_node);
   wide_int wone = wi::one (prec);
 
-  memset (&test, 0, sizeof (test));
-
   /* Get the edge for the default case.  */
   tmp = gimple_switch_default_label (swtch);
   default_bb = label_to_block (CASE_LABEL (tmp));
diff --git a/gcc/vec.h b/gcc/vec.h
index fee4616..0e9a13b 100644
--- a/gcc/vec.h
+++ b/gcc/vec.h
@@ -407,6 +407,26 @@ struct GTY((user)) vec
 {
 };
 
+/* Default-construct N elements in DST.  */
+
+template <typename T>
+inline void
+vec_default_construct (T *dst, unsigned n)
+{
+  for ( ; n; ++dst, --n)
+    ::new (static_cast<void*>(dst)) T ();
+}
+
+/* Copy-construct N elements in DST from *SRC.  */
+
+template <typename T>
+inline void
+vec_copy_construct (T *dst, const T *src, unsigned n)
+{
+  for ( ; n; ++dst, ++src, --n)
+    ::new (static_cast<void*>(dst)) T (*src);
+}
+
 /* Type to provide NULL values for vec<T, A, L>.  This is used to
    provide nil initializers for vec instances.  Since vec must be
    a POD, we cannot have proper ctor/dtor for it.  To initialize
@@ -615,7 +635,7 @@ vec_safe_grow_cleared (vec<T, A, vl_embed> *&v, unsigned len CXX_MEM_STAT_INFO)
 {
   unsigned oldlen = vec_safe_length (v);
   vec_safe_grow (v, len PASS_MEM_STAT);
-  memset (&(v->address ()[oldlen]), 0, sizeof (T) * (len - oldlen));
+  vec_default_construct (v->address () + oldlen, len - oldlen);
 }
 
 
@@ -821,7 +841,7 @@ vec<T, A, vl_embed>::copy (ALONE_MEM_STAT_DECL) const
     {
       vec_alloc (new_vec, len PASS_MEM_STAT);
       new_vec->embedded_init (len, len);
-      memcpy (new_vec->address (), m_vecdata, sizeof (T) * len);
+      vec_copy_construct (new_vec->address (), m_vecdata, len);
     }
   return new_vec;
 }
@@ -838,7 +858,7 @@ vec<T, A, vl_embed>::splice (const vec<T, A, vl_embed> &src)
   if (len)
     {
       gcc_checking_assert (space (len));
-      memcpy (address () + length (), src.address (), len * sizeof (T));
+      vec_copy_construct (end (), src.address (), len);
       m_vecpfx.m_num += len;
     }
 }
@@ -1092,13 +1112,12 @@ inline void
 vec<T, A, vl_embed>::quick_grow_cleared (unsigned len)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   quick_grow (len);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
-
 /* Garbage collection support for vec<T, A, vl_embed>.  */
 
 template<typename T>
@@ -1445,7 +1464,7 @@ vec<T, va_heap, vl_ptr>::reserve (unsigned nelems, bool exact MEM_STAT_DECL)
   va_heap::reserve (m_vec, nelems, exact PASS_MEM_STAT);
   if (handle_auto_vec)
     {
-      memcpy (m_vec->address (), oldvec->address (), sizeof (T) * oldsize);
+      vec_copy_construct (m_vec->address (), oldvec->address (), oldsize);
       m_vec->m_vecpfx.m_num = oldsize;
     }
 
@@ -1607,10 +1626,10 @@ inline void
 vec<T, va_heap, vl_ptr>::safe_grow_cleared (unsigned len MEM_STAT_DECL)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   safe_grow (len PASS_MEM_STAT);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
 
diff --git a/libcpp/line-map.c b/libcpp/line-map.c
index c4b7cb2..56b7913 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -62,7 +62,8 @@ extern unsigned num_macro_tokens_counter;
 
 line_maps::~line_maps ()
 {
-  htab_delete (location_adhoc_data_map.htab);
+  if (location_adhoc_data_map.htab)
+    htab_delete (location_adhoc_data_map.htab);
 }
 
 /* Hash function for location_adhoc_data hashtable.  */
@@ -347,7 +348,7 @@ void
 linemap_init (struct line_maps *set,
 	      source_location builtin_location)
 {
-  memset (set, 0, sizeof (struct line_maps));
+  *set = line_maps ();
   set->highest_location = RESERVED_LOCATION_COUNT - 1;
   set->highest_line = RESERVED_LOCATION_COUNT - 1;
   set->location_adhoc_data_map.htab =
diff --git a/libitm/beginend.cc b/libitm/beginend.cc
index d04f3e9..c6550a3 100644
--- a/libitm/beginend.cc
+++ b/libitm/beginend.cc
@@ -431,7 +431,7 @@ GTM::gtm_transaction_cp::save(gtm_thread* tx)
   // Save everything that we might have to restore on restarts or aborts.
   jb = tx->jb;
   undolog_size = tx->undolog.size();
-  memcpy(&alloc_actions, &tx->alloc_actions, sizeof(alloc_actions));
+  alloc_actions = tx->alloc_actions;
   user_actions_size = tx->user_actions.size();
   id = tx->id;
   prop = tx->prop;
@@ -449,7 +449,7 @@ GTM::gtm_transaction_cp::commit(gtm_thread* tx)
   // commits of nested transactions. Allocation actions must be committed
   // before committing the snapshot.
   tx->jb = jb;
-  memcpy(&tx->alloc_actions, &alloc_actions, sizeof(alloc_actions));
+  tx->alloc_actions = alloc_actions;
   tx->id = id;
   tx->prop = prop;
 }
@@ -485,7 +485,7 @@ GTM::gtm_thread::rollback (gtm_transaction_cp *cp, bool aborting)
       prop = cp->prop;
       if (cp->disp != abi_disp())
 	set_abi_disp(cp->disp);
-      memcpy(&alloc_actions, &cp->alloc_actions, sizeof(alloc_actions));
+      alloc_actions = cp->alloc_actions;
       nesting = cp->nesting;
     }
   else
diff --git a/libitm/method-ml.cc b/libitm/method-ml.cc
index fcae334..b857bff 100644
--- a/libitm/method-ml.cc
+++ b/libitm/method-ml.cc
@@ -138,7 +138,11 @@ struct ml_mg : public method_group
     // This store is only executed while holding the serial lock, so relaxed
     // memory order is sufficient here.  Same holds for the memset.
     time.store(0, memory_order_relaxed);
-    memset(orecs, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
+    // The memset below isn't strictly kosher because it bypasses
+    // the non-trivial assignment operator defined by std::atomic.  Using
+    // a local void* is enough to prevent GCC from warning for this.
+    void *p = orecs;
+    memset(p, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
   }
 };
 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
       [not found]                           ` <cc62e93c-3b49-8e2f-70b9-acdd013fe760@redhat.com>
@ 2017-06-02 21:28                             ` Martin Sebor
  2017-06-05  2:02                               ` Jason Merrill
  0 siblings, 1 reply; 46+ messages in thread
From: Martin Sebor @ 2017-06-02 21:28 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Pedro Alves, Gcc Patch List

On 05/31/2017 05:34 PM, Jason Merrill wrote:
> On 05/27/2017 06:44 PM, Martin Sebor wrote:
>> +  /* True if the class is trivial and has a trivial non-deleted copy
>> +     assignment, copy ctor, and default ctor, respectively.  The last
>> +     one isn't used to issue warnings but only to decide what suitable
>> +     alternatives to offer as replacements for the raw memory
>> operation.  */
>> +  bool trivial = trivial_type_p (desttype);
>
> This comment seems out of date; there's only one variable here now.
>
>> +  /* True if the class is has a non-deleted trivial assignment.  Set
>
> s/is//
>
>> +  /* True if the class has a (possibly deleted) trivial copy ctor.  */
>> +  bool trivcopy = trivially_copyable_p (desttype);
>
> "True if the class is trivially copyable."
>
>> +      if (delassign)
>> +        warnfmt = G_("%qD writing to an object of type %#qT with "
>> +             "deleted copy assignment");
>> +      else if (!trivassign)
>> +        warnfmt = G_("%qD writing to an object of type %#qT with "
>> +             "no trivial copy assignment");
>> +      else if (!trivial)
>> +        warnfmt = G_("%qD writing to an object of non-trivial "
>> +             "type %#qT; use assignment instead");
>
> I'd still like the !trivial test to come first in all the memset cases,
> !trivcopy in the copy cases.

The tests are in the order they're in to provide as much useful
detail in the diagnostics as necessary to understand the problem
make the suggestion meaningful.  To what end you want to change
it?

AFAICS, all it will accomplish is shuffle the tests around
because starting with !trivial means I'll still need to test
for delassign and delcopy before issuing the warning so that
I can include the right suggestion (and avoid suggesting to
use assignment when it's not available).

>> +static bool
>> +has_trivial_special_function (tree ctype, special_function_kind sfk,
>> +                  bool *deleted_p)
>
> This seems redundant with type_has_trivial_fn.  If that function is
> giving the wrong answer for a class where all of the SFK are deleted,
> let's fix that, under check_bases_and_members, rather than introduce a
> new function.  I don't want to call synthesized_method_walk every time
> we want to check whether a function is trivial.

A deleted special function can be trivial.  type_has_trivial_fn()
only determines whether a function is trivial, not whether it's deleted. 
  I chose not to use the function because when a special
function is deleted I don't want to have the warning suggest it
as an alternative to the raw memory function.

Maybe I should use a different approach and instead of trying
to see if a function is deleted use trivially_xible to see if
it's usable.  That will mean changing the diagnostics from
"with a deleted special function" to "without trivial special
function" but it will avoid calling synthesized_method_walk
while still avoiding giving bogus suggestions.

Is this approach acceptable?

Martin

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-02 21:28                             ` Martin Sebor
@ 2017-06-05  2:02                               ` Jason Merrill
  2017-06-05  7:53                                 ` Jason Merrill
  0 siblings, 1 reply; 46+ messages in thread
From: Jason Merrill @ 2017-06-05  2:02 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Pedro Alves, Gcc Patch List

On 06/02/2017 05:28 PM, Martin Sebor wrote:
> On 05/31/2017 05:34 PM, Jason Merrill wrote:
>> On 05/27/2017 06:44 PM, Martin Sebor wrote:
>>> +  /* True if the class is trivial and has a trivial non-deleted copy
>>> +     assignment, copy ctor, and default ctor, respectively.  The last
>>> +     one isn't used to issue warnings but only to decide what suitable
>>> +     alternatives to offer as replacements for the raw memory
>>> operation.  */
>>> +  bool trivial = trivial_type_p (desttype);
>>
>> This comment seems out of date; there's only one variable here now.
>>
>>> +  /* True if the class is has a non-deleted trivial assignment.  Set
>>
>> s/is//
>>
>>> +  /* True if the class has a (possibly deleted) trivial copy ctor.  */
>>> +  bool trivcopy = trivially_copyable_p (desttype);
>>
>> "True if the class is trivially copyable."
>>
>>> +      if (delassign)
>>> +        warnfmt = G_("%qD writing to an object of type %#qT with "
>>> +             "deleted copy assignment");
>>> +      else if (!trivassign)
>>> +        warnfmt = G_("%qD writing to an object of type %#qT with "
>>> +             "no trivial copy assignment");
>>> +      else if (!trivial)
>>> +        warnfmt = G_("%qD writing to an object of non-trivial "
>>> +             "type %#qT; use assignment instead");
>>
>> I'd still like the !trivial test to come first in all the memset cases,
>> !trivcopy in the copy cases.
> 
> The tests are in the order they're in to provide as much useful
> detail in the diagnostics as necessary to understand the problem
> make the suggestion meaningful.  To what end you want to change
> it?

Mostly I was thinking that whether a class is trivial(ly copyable) is 
more to the point, but I guess what you have now is fine.

>>> +static bool
>>> +has_trivial_special_function (tree ctype, special_function_kind sfk,
>>> +                  bool *deleted_p)
>>
>> This seems redundant with type_has_trivial_fn.  If that function is
>> giving the wrong answer for a class where all of the SFK are deleted,
>> let's fix that, under check_bases_and_members, rather than introduce a
>> new function.  I don't want to call synthesized_method_walk every time
>> we want to check whether a function is trivial.
> 
> A deleted special function can be trivial.

I believe that in the language, the triviality of a deleted function 
cannot be determined.  But I believe you're right about the behavior of 
type_has_trivial_fn, which is why I mentioned changing it.

> Maybe I should use a different approach and instead of trying
> to see if a function is deleted use trivially_xible to see if
> it's usable.  That will mean changing the diagnostics from
> "with a deleted special function" to "without trivial special
> function" but it will avoid calling synthesized_method_walk
> while still avoiding giving bogus suggestions.
> 
> Is this approach acceptable?

Yes, that makes sense.

Jason

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-05  2:02                               ` Jason Merrill
@ 2017-06-05  7:53                                 ` Jason Merrill
  2017-06-05 16:07                                   ` Martin Sebor
  0 siblings, 1 reply; 46+ messages in thread
From: Jason Merrill @ 2017-06-05  7:53 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Pedro Alves, Gcc Patch List

On 06/04/2017 10:01 PM, Jason Merrill wrote:
> On 06/02/2017 05:28 PM, Martin Sebor wrote:
>> On 05/31/2017 05:34 PM, Jason Merrill wrote:
>>> On 05/27/2017 06:44 PM, Martin Sebor wrote:
>>>> +  /* True if the class is trivial and has a trivial non-deleted copy
>>>> +     assignment, copy ctor, and default ctor, respectively.  The last
>>>> +     one isn't used to issue warnings but only to decide what suitable
>>>> +     alternatives to offer as replacements for the raw memory
>>>> operation.  */
>>>> +  bool trivial = trivial_type_p (desttype);
>>>
>>> This comment seems out of date; there's only one variable here now.
>>>
>>>> +  /* True if the class is has a non-deleted trivial assignment.  Set
>>>
>>> s/is//
>>>
>>>> +  /* True if the class has a (possibly deleted) trivial copy ctor.  */
>>>> +  bool trivcopy = trivially_copyable_p (desttype);
>>>
>>> "True if the class is trivially copyable."
>>>
>>>> +      if (delassign)
>>>> +        warnfmt = G_("%qD writing to an object of type %#qT with "
>>>> +             "deleted copy assignment");
>>>> +      else if (!trivassign)
>>>> +        warnfmt = G_("%qD writing to an object of type %#qT with "
>>>> +             "no trivial copy assignment");
>>>> +      else if (!trivial)
>>>> +        warnfmt = G_("%qD writing to an object of non-trivial "
>>>> +             "type %#qT; use assignment instead");
>>>
>>> I'd still like the !trivial test to come first in all the memset cases,
>>> !trivcopy in the copy cases.
>>
>> The tests are in the order they're in to provide as much useful
>> detail in the diagnostics as necessary to understand the problem
>> make the suggestion meaningful.  To what end you want to change
>> it?
> 
> Mostly I was thinking that whether a class is trivial(ly copyable) is 
> more to the point, but I guess what you have now is fine.
> 
>>>> +static bool
>>>> +has_trivial_special_function (tree ctype, special_function_kind sfk,
>>>> +                  bool *deleted_p)
>>>
>>> This seems redundant with type_has_trivial_fn.  If that function is
>>> giving the wrong answer for a class where all of the SFK are deleted,
>>> let's fix that, under check_bases_and_members, rather than introduce a
>>> new function.  I don't want to call synthesized_method_walk every time
>>> we want to check whether a function is trivial.
>>
>> A deleted special function can be trivial.
> 
> I believe that in the language, the triviality of a deleted function 
> cannot be determined.  But I believe you're right about the behavior of 
> type_has_trivial_fn, which is why I mentioned changing it.
> 
>> Maybe I should use a different approach and instead of trying
>> to see if a function is deleted use trivially_xible to see if
>> it's usable.  That will mean changing the diagnostics from
>> "with a deleted special function" to "without trivial special
>> function" but it will avoid calling synthesized_method_walk
>> while still avoiding giving bogus suggestions.

Actually, this would check for one possible argument type and not 
others, so I think it's better to keep looking at the declarations.  You 
can do that by just looking them up (lookup_fnfields_slot) and iterating 
over them, you don't need to call synthesized_method_walk.

Jason

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-05  7:53                                 ` Jason Merrill
@ 2017-06-05 16:07                                   ` Martin Sebor
  2017-06-05 19:13                                     ` Martin Sebor
  0 siblings, 1 reply; 46+ messages in thread
From: Martin Sebor @ 2017-06-05 16:07 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Pedro Alves, Gcc Patch List

>>> Maybe I should use a different approach and instead of trying
>>> to see if a function is deleted use trivially_xible to see if
>>> it's usable.  That will mean changing the diagnostics from
>>> "with a deleted special function" to "without trivial special
>>> function" but it will avoid calling synthesized_method_walk
>>> while still avoiding giving bogus suggestions.
>
> Actually, this would check for one possible argument type and not
> others, so I think it's better to keep looking at the declarations.  You
> can do that by just looking them up (lookup_fnfields_slot) and iterating
> over them, you don't need to call synthesized_method_walk.

You mean using trivially_xible might check assignability or copy
constructibility from const T& but not from T& (or the other way
around), and you think both (or perhaps even other forms) should
be considered?

E.g., given:

   struct S
   {
     S& operator= (const S&) = default;
     void operator= (S&) = delete;
   };

   void f (S *d, const S *s)
   {
     memcpy(d, s, sizeof *d);   // don't warn here
   }

   void g (S *d, S *s)
   {
     memcpy(d, s, sizeof *d);   // but warn here
   }

And your suggestion is to iterate over the assignment operator
(and copy ctor) overloads for S looking for one that's trivial,
public, and not deleted?

If that's it, I was thinking of just checking for the const T&
overload (as if by using std::is_trivially_copy_assignable<T>()).

I don't mind trying the approach you suggest.  It should be more
accurate.  I just want to make sure we're on the same page.

Martin

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-05 16:07                                   ` Martin Sebor
@ 2017-06-05 19:13                                     ` Martin Sebor
  2017-06-06  1:53                                       ` Martin Sebor
  0 siblings, 1 reply; 46+ messages in thread
From: Martin Sebor @ 2017-06-05 19:13 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Pedro Alves, Gcc Patch List

On 06/05/2017 10:07 AM, Martin Sebor wrote:
>>>> Maybe I should use a different approach and instead of trying
>>>> to see if a function is deleted use trivially_xible to see if
>>>> it's usable.  That will mean changing the diagnostics from
>>>> "with a deleted special function" to "without trivial special
>>>> function" but it will avoid calling synthesized_method_walk
>>>> while still avoiding giving bogus suggestions.
>>
>> Actually, this would check for one possible argument type and not
>> others, so I think it's better to keep looking at the declarations.  You
>> can do that by just looking them up (lookup_fnfields_slot) and iterating
>> over them, you don't need to call synthesized_method_walk.
>
> You mean using trivially_xible might check assignability or copy
> constructibility from const T& but not from T& (or the other way
> around), and you think both (or perhaps even other forms) should
> be considered?
>
> E.g., given:
>
>   struct S
>   {
>     S& operator= (const S&) = default;
>     void operator= (S&) = delete;
>   };
>
>   void f (S *d, const S *s)
>   {
>     memcpy(d, s, sizeof *d);   // don't warn here
>   }
>
>   void g (S *d, S *s)
>   {
>     memcpy(d, s, sizeof *d);   // but warn here
>   }
>
> And your suggestion is to iterate over the assignment operator
> (and copy ctor) overloads for S looking for one that's trivial,
> public, and not deleted?
>
> If that's it, I was thinking of just checking for the const T&
> overload (as if by using std::is_trivially_copy_assignable<T>()).
>
> I don't mind trying the approach you suggest.  It should be more
> accurate.  I just want to make sure we're on the same page.

Actually, after some more thought and testing the approach I have
a feeling that distinguishing between the two cases above is not
what you meant.

Classes that overload copy assignment or copy ctors on the constness
of the argument are tricky to begin with and using raw memory calls
on them seems suspect and worthy of a warning.

I'm guessing what you meant by "checking for one possible argument
type and not others" is actually checking to make sure all copy
assignment (and copy ctor) overloads are trivial, not jut some,
and at least one of them is accessible.  I'll go with that unless
I hear otherwise.

Martin

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-05 19:13                                     ` Martin Sebor
@ 2017-06-06  1:53                                       ` Martin Sebor
  2017-06-06 22:24                                         ` Martin Sebor
  0 siblings, 1 reply; 46+ messages in thread
From: Martin Sebor @ 2017-06-06  1:53 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Pedro Alves, Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 2466 bytes --]

On 06/05/2017 01:13 PM, Martin Sebor wrote:
> On 06/05/2017 10:07 AM, Martin Sebor wrote:
>>>>> Maybe I should use a different approach and instead of trying
>>>>> to see if a function is deleted use trivially_xible to see if
>>>>> it's usable.  That will mean changing the diagnostics from
>>>>> "with a deleted special function" to "without trivial special
>>>>> function" but it will avoid calling synthesized_method_walk
>>>>> while still avoiding giving bogus suggestions.
>>>
>>> Actually, this would check for one possible argument type and not
>>> others, so I think it's better to keep looking at the declarations.  You
>>> can do that by just looking them up (lookup_fnfields_slot) and iterating
>>> over them, you don't need to call synthesized_method_walk.
>>
>> You mean using trivially_xible might check assignability or copy
>> constructibility from const T& but not from T& (or the other way
>> around), and you think both (or perhaps even other forms) should
>> be considered?
>>
>> E.g., given:
>>
>>   struct S
>>   {
>>     S& operator= (const S&) = default;
>>     void operator= (S&) = delete;
>>   };
>>
>>   void f (S *d, const S *s)
>>   {
>>     memcpy(d, s, sizeof *d);   // don't warn here
>>   }
>>
>>   void g (S *d, S *s)
>>   {
>>     memcpy(d, s, sizeof *d);   // but warn here
>>   }
>>
>> And your suggestion is to iterate over the assignment operator
>> (and copy ctor) overloads for S looking for one that's trivial,
>> public, and not deleted?
>>
>> If that's it, I was thinking of just checking for the const T&
>> overload (as if by using std::is_trivially_copy_assignable<T>()).
>>
>> I don't mind trying the approach you suggest.  It should be more
>> accurate.  I just want to make sure we're on the same page.
>
> Actually, after some more thought and testing the approach I have
> a feeling that distinguishing between the two cases above is not
> what you meant.
>
> Classes that overload copy assignment or copy ctors on the constness
> of the argument are tricky to begin with and using raw memory calls
> on them seems suspect and worthy of a warning.
>
> I'm guessing what you meant by "checking for one possible argument
> type and not others" is actually checking to make sure all copy
> assignment (and copy ctor) overloads are trivial, not jut some,
> and at least one of them is accessible.  I'll go with that unless
> I hear otherwise.

Attached is an update that implements this simplified approach.

Martin


[-- Attachment #2: gcc-80560.diff --]
[-- Type: text/x-patch, Size: 80757 bytes --]

PR c++/80560 - warn on undefined memory operations involving non-trivial types

gcc/c-family/ChangeLog:

	PR c++/80560
	* c.opt (-Wclass-memaccess): New option.

gcc/cp/ChangeLog:

	PR c++/80560
	* call.c (first_non_public_field, maybe_warn_class_memaccess): New
	functions.
	(has_trivial_copy_assign_p, has_trivial_copy_p): Ditto.
	(build_cxx_call): Call maybe_warn_class_memaccess.

gcc/ChangeLog:

	PR c++/80560
	* dumpfile.c (dump_register): Avoid calling memset to initialize
	a class with a default ctor.
	* gcc.c (struct compiler): Remove const qualification.
	* genattrtab.c (gen_insn_reserv): Replace memset with initialization.
	* hash-table.h: Ditto.
	* ipa-cp.c (allocate_and_init_ipcp_value): Replace memset with
	  assignment.
	* ipa-prop.c (ipa_free_edge_args_substructures): Ditto.
	* omp-low.c (lower_omp_ordered_clauses): Replace memset with
	default ctor.
	* params.h (struct param_info): Make struct members non-const.
	* tree-switch-conversion.c (emit_case_bit_tests): Replace memset
	with default initialization.
	* vec.h (vec_copy_construct, vec_default_construct): New helper
	functions.
	(vec<T>::copy, vec<T>::splice, vec<T>::reserve): Replace memcpy
	with vec_copy_construct.
	(vect<T>::quick_grow_cleared): Replace memset with default ctor.
	(vect<T>::vec_safe_grow_cleared, vec_safe_grow_cleared): Same.
	* doc/invoke.texi (-Wclass-memaccess): Document.

libcpp/ChangeLog:

	PR c++/80560
	* line-map.c (line_maps::~line_maps): Avoid calling htab_delete
	with a null pointer.
	(linemap_init): Avoid calling memset on an object of a non-trivial
	type.

libitm/ChangeLog:

	PR c++/80560
	* beginend.cc (GTM::gtm_thread::rollback): Avoid calling memset
	on an object of a non-trivial type.
	(GTM::gtm_transaction_cp::commit): Use assignment instead of memcpy
	to copy an object.
	* method-ml.cc (orec_iterator::reinit): Avoid -Wclass-memaccess.

gcc/testsuite/ChangeLog:

	PR c++/80560
	* g++.dg/Wclass-memaccess.C: New test.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 9ad2f6e..f777b8f 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -792,6 +792,10 @@ Wnon-template-friend
 C++ ObjC++ Var(warn_nontemplate_friend) Init(1) Warning
 Warn when non-templatized friend functions are declared within a template.
 
+Wclass-memaccess
+C++ ObjC++ Var(warn_class_memaccess) Warning LangEnabledBy(C++ ObjC++, Wall)
+Warn for unsafe raw memory writes to objects of class types.
+
 Wnon-virtual-dtor
 C++ ObjC++ Var(warn_nonvdtor) Warning LangEnabledBy(C++ ObjC++,Weffc++)
 Warn about non-virtual destructors.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index e348f29..df83688 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8153,6 +8153,376 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
   return call;
 }
 
+/* Return the DECL of the first non-public data member of class TYPE
+   or null if none can be found.  */
+
+static tree
+first_non_public_field (tree type)
+{
+  if (!CLASS_TYPE_P (type))
+    return NULL_TREE;
+
+  for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+	continue;
+      if (TREE_STATIC (field))
+	continue;
+      if (TREE_PRIVATE (field) || TREE_PROTECTED (field))
+	return field;
+    }
+
+  int i = 0;
+
+  for (tree base_binfo, binfo = TYPE_BINFO (type);
+       BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+    {
+      tree base = TREE_TYPE (base_binfo);
+
+      if (tree field = first_non_public_field (base))
+	return field;
+    }
+
+  return NULL_TREE;
+}
+
+/* Return true if all copy assignment operator overloads for class TYPE
+   are trivial and at least one of them is not deleted and, when ACCESS
+   is set, accessible.  Return false otherwise.  Set HASASSIGN to true
+   when the TYPE has a (not necessarily trivial) copy assignment.  */
+
+static bool
+has_trivial_copy_assign_p (tree type, bool access, bool *hasassign)
+{
+  tree fns = cp_assignment_operator_id (NOP_EXPR);
+
+  bool all_trivial = true;
+
+  /* Iterate over copy assignment overloads.  */
+  for (fns = lookup_fnfields_slot (type, fns); fns; fns = OVL_NEXT (fns))
+    {
+      tree f = OVL_CURRENT (fns);
+
+      bool accessible = !access || !(TREE_PRIVATE (f) || TREE_PROTECTED (f));
+
+      if (DECL_DELETED_FN (f))
+	continue;
+
+      if (accessible)
+	*hasassign = true;
+
+      if (!trivial_fn_p (f))
+	all_trivial = false;
+
+      /* Break early when both properties have been determined.  */
+      if (*hasassign && !all_trivial)
+	break;
+    }
+
+  return all_trivial && is_trivially_xible (MODIFY_EXPR, type, type);
+}
+
+/* Return true if all copy ctor overloads for class TYPE are trivial
+   and at least one of them is not deleted and, when ACCESS is set,
+   accessible.  Return false otherwise.  Set each element of HASCTOR[]
+   to true when the TYPE has a (not necessarily trivial) default and
+   copy ctor, respectively.  */
+
+static bool
+has_trivial_copy_p (tree type, bool access, bool hasctor[2])
+{
+  tree fns = complete_ctor_identifier;
+
+  /* The number of accessible trivial copy ctors.  */
+  unsigned count = 0;
+
+  for (fns = lookup_fnfields_slot (type, fns); fns; fns = OVL_NEXT (fns))
+    {
+      tree f = OVL_CURRENT (fns);
+      tree arg = TREE_CHAIN (DECL_ARGUMENTS (f));
+      bool cpyctor_p;
+      if (arg)
+	{
+	  tree argtype = TREE_TYPE (arg);
+	  if (TREE_CODE (argtype) == REFERENCE_TYPE)
+	    argtype = TREE_TYPE (argtype);
+
+	  if (!same_type_ignoring_top_level_qualifiers_p (type, argtype))
+	    continue;
+
+	  cpyctor_p = true;
+	}
+      else
+	cpyctor_p = false;
+
+      bool accessible = !access || !(TREE_PRIVATE (f) || TREE_PROTECTED (f));
+
+      if (accessible && !DECL_DELETED_FN (f))
+	hasctor[cpyctor_p] = true;
+
+      if (cpyctor_p && trivial_fn_p (f) && accessible)
+	++count;
+    }
+
+  return count != 0;
+}
+
+/* issue a warning on a call to the built-in function FNDECL if it is
+   a raw memory write whose destination is not an object of (something
+   like) trivial or standard layout type with a non-deleted assignment
+   and copy ctor.  Detects const correctness violations, corrupting
+   references, virtual table pointers, and bypassing non-trivial
+   assignments.  */
+
+static void
+maybe_warn_class_memaccess (location_t loc, tree fndecl, tree *args)
+{
+  /* Except for bcopy where it's second, the destination pointer is
+     the first argument for all functions handled here.  Compute
+     the index of the destination and source arguments.  */
+  unsigned dstidx = DECL_FUNCTION_CODE (fndecl) == BUILT_IN_BCOPY;
+  unsigned srcidx = !dstidx;
+
+  tree dest = args[dstidx];
+  if (!dest || !TREE_TYPE (dest) || !POINTER_TYPE_P (TREE_TYPE (dest)))
+    return;
+
+  STRIP_NOPS (dest);
+
+  tree srctype = NULL_TREE;
+
+  /* Determine the type of the pointed-to object and whether it's
+     a complete class type.  */
+  tree desttype = TREE_TYPE (TREE_TYPE (dest));
+
+  if (!desttype || !COMPLETE_TYPE_P (desttype) || !CLASS_TYPE_P (desttype))
+    return;
+
+  /* Check to see if the raw memory call is made by a ctor or dtor
+     with this as the destination argument for the destination type.
+     If so, be more permissive.  */
+  if (current_function_decl
+      && (DECL_CONSTRUCTOR_P (current_function_decl)
+	  || DECL_DESTRUCTOR_P (current_function_decl))
+      && is_this_parameter (dest))
+    {
+      tree ctx = DECL_CONTEXT (current_function_decl);
+      bool special = same_type_ignoring_top_level_qualifiers_p (ctx, desttype);
+
+      tree binfo = TYPE_BINFO (ctx);
+
+      /* A ctor and dtor for a class with no bases and no virtual functions
+	 can do whatever they want.  Bail early with no further checking.  */
+      if (special && !BINFO_VTABLE (binfo) && !BINFO_N_BASE_BINFOS (binfo))
+	return;
+    }
+
+  /* True if the class is trivial.  */
+  bool trivial = trivial_type_p (desttype);
+
+  /* Set to true if DESTYPE has an accessible copy assignment.  */
+  bool hasassign = false;
+  /* True if all of the class' overloaded copy assignment operators
+     are all trivial (and not deleted) and at least one of them is
+     accessible.  */
+  bool trivassign = has_trivial_copy_assign_p (desttype, true, &hasassign);
+
+  /* Set to true if DESTTYPE has an accessible defqault and copy ctor,
+     respectively.  */
+  bool hasctors[2] = { false, false };
+
+  /* True if all of the class' overloaded copy constructors are all
+     trivial (and not deleted) and at least one of them is accessible.  */
+  bool trivcopy = has_trivial_copy_p (desttype, true, hasctors);
+
+  /* Set FLD to the first private/protected member of the class.  */
+  tree fld = trivial ? first_non_public_field (desttype) : NULL_TREE;
+
+  /* The warning format string.  */
+  const char *warnfmt = NULL;
+  /* A suggested alternative to offer instead of the raw memory call.
+     Empty string when none can be come up with.  */
+  const char *suggest = "";
+  bool warned = false;
+
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    case BUILT_IN_MEMSET:
+      if (!integer_zerop (args[1]))
+	{
+	  /* Diagnose setting non-copy-assignable or non-trivial types,
+	     or types with a private member, to (potentially) non-zero
+	     bytes.  Since the value of the bytes being written is unknown,
+	     suggest using assignment instead (if one exists).  Also warn
+	     for writes into objects for which zero-initialization doesn't
+	     mean all bits clear (pointer-to-member data, where null is all
+	     bits set).  Since the value being written is (most likely)
+	     non-zero, simply suggest assignment (but not copy assignment).  */
+	  suggest = "; use assignment instead";
+	  if (!trivassign)
+	    warnfmt = G_("%qD writing to an object of type %#qT with "
+			 "no trivial copy-assignment");
+	  else if (!trivial)
+	    warnfmt = G_("%qD writing to an object of non-trivial type %#qT%s");
+	  else if (fld)
+	    {
+	      const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	      warned = warning_at (loc, OPT_Wclass_memaccess,
+				   "%qD writing to an object of type %#qT with "
+				   "%qs member %qD",
+				   fndecl, desttype, access, fld);
+	    }
+	  else if (!zero_init_p (desttype))
+	    warnfmt = G_("%qD writing to an object of type %#qT containing "
+			 "a pointer to data member%s");
+
+	  break;
+	}
+      /* Fall through.  */
+
+    case BUILT_IN_BZERO:
+      /* Similarly to the above, diagnose clearing non-trivial or non-
+	 standard layout objects, or objects of types with no assignmenmt.
+	 Since the value being written is known to be zero, suggest either
+	 copy assignment, copy ctor, or default ctor as an alternative,
+	 depending on what's available.  */
+
+      if (hasassign && hasctors[0])
+	suggest = G_("; use assignment or value-initialization instead");
+      else if (hasassign)
+	suggest = G_("; use assignment instead");
+      else if (hasctors[0])
+	suggest = G_("; use value-initialization instead");
+
+      if (!trivassign)
+	warnfmt = G_("%qD clearing an object of type %#qT with "
+		     "no trivial copy-assignment%s");
+      else if (!trivial)
+	warnfmt =  G_("%qD clearing an object of non-trivial type %#qT%s");
+      else if (!zero_init_p (desttype))
+	warnfmt = G_("%qD clearing an object of type %#qT containing "
+		     "a pointer-to-member%s");
+      break;
+
+    case BUILT_IN_BCOPY:
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMPCPY:
+      /* Determine the type of the source object.  */
+      srctype = STRIP_NOPS (args[srcidx]);
+      srctype = TREE_TYPE (TREE_TYPE (srctype));
+
+      /* Since it's impossible to determine wheter the byte copy is
+	 being used in place of assignment to an existing object or
+	 as a substitute for initialization, assume it's the former.
+	 Determine the best alternative to use instead depending on
+	 what's not deleted.  */
+      if (hasassign && hasctors[1])
+	suggest = G_("; use copy-assignment or copy-initialization instead");
+      else if (hasassign)
+	suggest = G_("; use copy-assignment instead");
+      else if (hasctors[1])
+	suggest = G_("; use copy-initialization instead");
+
+      if (!trivassign)
+	warnfmt = G_("%qD writing to an object of type %#qT with no trivial "
+		     "copy-assignment%s");
+      else if (!trivially_copyable_p (desttype))
+	warnfmt = G_("%qD writing to an object of non-trivially copyable "
+		     "type %#qT%s");
+      else if (!trivcopy)
+	warnfmt = G_("%qD writing to an object with a deleted copy constructor");
+
+      else if (!trivial
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  /* Warn when copying into a non-trivial object from an object
+	     of a different type other than void or char.  */
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD copying an object of non-trivial type "
+			       "%#qT from an array of %#qT",
+			       fndecl, desttype, srctype);
+	}
+      else if (fld
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD copying an object of type %#qT with "
+			       "%qs member %qD from an array of %#qT; use "
+			       "assignment or copy-initialization instead",
+			       fndecl, desttype, access, fld, srctype);
+	}
+      else if (!trivial && TREE_CODE (args[2]) == INTEGER_CST)
+	{
+	  /* Finally, warn on partial copies.  */
+	  unsigned HOST_WIDE_INT typesize
+	    = tree_to_uhwi (TYPE_SIZE_UNIT (desttype));
+	  if (unsigned HOST_WIDE_INT partial
+	      = tree_to_uhwi (args[2]) % typesize)
+	    warned = warning_at (loc, OPT_Wclass_memaccess,
+				 (typesize - partial > 1
+				  ? G_("%qD writing to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "bytes unchanged")
+				  : G_("%qD writing to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "byte unchanged")),
+				 fndecl, desttype, typesize - partial);
+	}
+      break;
+
+    case BUILT_IN_REALLOC:
+
+      if (!trivially_copyable_p (desttype))
+	warnfmt = G_("%qD moving an object of non-trivially copyable type "
+		     "%#qT; use %<new%> and %<delete%> instead");
+      else if (!trivcopy)
+	warnfmt = G_("%qD moving an object of type %#qT with deleted copy "
+		     "constructor; use %<new%> and %<delete%> instead");
+      else if (!get_dtor (desttype, tf_none))
+	warnfmt = G_("%qD moving an object of type %#qT with deleted "
+		     "destructor");
+      else if (!trivial
+	       && TREE_CODE (args[1]) == INTEGER_CST
+	       && tree_int_cst_lt (args[1], TYPE_SIZE_UNIT (desttype)))
+	{
+	  /* Finally, warn on reallocation into insufficient space.  */
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD moving an object of non-trivial type "
+			       "%#qT and size %E into a region of size %E",
+			       fndecl, desttype, TYPE_SIZE_UNIT (desttype),
+			       args[1]);
+	}
+      break;
+
+    default:
+      return;
+    }
+
+  if (!warned && !warnfmt)
+    return;
+
+  if (warnfmt)
+    {
+      if (suggest)
+	warned = warning_at (loc, OPT_Wclass_memaccess,
+			     warnfmt, fndecl, desttype, suggest);
+      else
+	warned = warning_at (loc, OPT_Wclass_memaccess,
+			     warnfmt, fndecl, desttype);
+    }
+
+  if (warned)
+    inform (location_of (desttype), "%#qT declared here", desttype);
+}
+
 /* Build and return a call to FN, using NARGS arguments in ARGARRAY.
    This function performs no overload resolution, conversion, or other
    high-level operations.  */
@@ -8185,6 +8555,10 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
       if (!check_builtin_function_arguments (EXPR_LOCATION (fn), vNULL, fndecl,
 					     nargs, argarray))
 	return error_mark_node;
+
+      /* Warn if the built-in writes to an object of a non-trivial type.  */
+      if (nargs)
+	maybe_warn_class_memaccess (loc, fndecl, argarray);
     }
 
     /* If it is a built-in array notation function, then the return type of
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 57c9678..daf1601 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -215,7 +215,8 @@ in the following sections.
 -Wabi=@var{n}  -Wabi-tag  -Wconversion-null  -Wctor-dtor-privacy @gol
 -Wdelete-non-virtual-dtor  -Wliteral-suffix  -Wmultiple-inheritance @gol
 -Wnamespaces  -Wnarrowing @gol
--Wnoexcept  -Wnoexcept-type  -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
+-Wnoexcept  -Wnoexcept-type  -Wclass-memaccess @gol
+-Wnon-virtual-dtor  -Wreorder  -Wregister @gol
 -Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
 -Wno-non-template-friend  -Wold-style-cast @gol
 -Woverloaded-virtual  -Wno-pmf-conversions @gol
@@ -2913,6 +2914,23 @@ void g() noexcept;
 void h() @{ f(g); @} // in C++14 calls f<void(*)()>, in C++1z calls f<void(*)()noexcept>
 @end smallexample
 
+@item -Wclass-memaccess @r{(C++ and Objective-C++ only)}
+@opindex Wclass-memaccess
+Warn when the destination of a call to a raw memory function such as
+@code{memset} or @code{memcpy} is an object of class type writing into which
+might bypass the class non-trivial or deleted constructor or copy assignment,
+violate const-correctness or encapsulation, or corrupt the virtual table.
+Modifying the representation of such objects may violate invariants maintained
+by member functions of the class.  For example, the call to @code{memset}
+below is undefined becase it modifies a non-trivial class object and is,
+therefore, diagnosed.  The safe way to either initialize or clear the storage
+of objects of such types is by using the appropriate constructor or assignment
+operator, if one is available.
+@smallexample
+std::string str = "abc";
+memset (&str, 0, 3);
+@end smallexample
+The @option{-Wclass-memaccess} option is enabled by @option{-Wall}.
 
 @item -Wnon-virtual-dtor @r{(C++ and Objective-C++ only)}
 @opindex Wnon-virtual-dtor
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 6b9a47c..fe89a15 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -194,9 +194,16 @@ dump_register (const char *suffix, const char *swtch, const char *glob,
       m_extra_dump_files = XRESIZEVEC (struct dump_file_info,
 				       m_extra_dump_files,
 				       m_extra_dump_files_alloced);
+
+      /* Construct a new object in the space allocated above.  */
+      new (m_extra_dump_files + count) dump_file_info ();
+    }
+  else
+    {
+      /* Zero out the already constructed object.  */
+      m_extra_dump_files[count] = dump_file_info ();
     }
 
-  memset (&m_extra_dump_files[count], 0, sizeof (struct dump_file_info));
   m_extra_dump_files[count].suffix = suffix;
   m_extra_dump_files[count].swtch = swtch;
   m_extra_dump_files[count].glob = glob;
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 120c5c0..0ef4f10 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -1259,9 +1259,9 @@ struct compiler
   const char *cpp_spec;         /* If non-NULL, substitute this spec
 				   for `%C', rather than the usual
 				   cpp_spec.  */
-  const int combinable;          /* If nonzero, compiler can deal with
+  int combinable;               /* If nonzero, compiler can deal with
 				    multiple source files at once (IMA).  */
-  const int needs_preprocessing; /* If nonzero, source files need to
+  int needs_preprocessing;       /* If nonzero, source files need to
 				    be run through a preprocessor.  */
 };
 
diff --git a/gcc/genattrtab.c b/gcc/genattrtab.c
index 3629b5f..51dfe77 100644
--- a/gcc/genattrtab.c
+++ b/gcc/genattrtab.c
@@ -4703,8 +4703,8 @@ gen_insn_reserv (md_rtx_info *info)
   struct insn_reserv *decl = oballoc (struct insn_reserv);
   rtx def = info->def;
 
-  struct attr_desc attr;
-  memset (&attr, 0, sizeof (attr));
+  struct attr_desc attr = { };
+
   attr.name = DEF_ATTR_STRING (XSTR (def, 0));
   attr.loc = info->loc;
 
diff --git a/gcc/hash-table.h b/gcc/hash-table.h
index 0f7e21a..443d16c 100644
--- a/gcc/hash-table.h
+++ b/gcc/hash-table.h
@@ -803,7 +803,10 @@ hash_table<Descriptor, Allocator>::empty_slow ()
       m_size_prime_index = nindex;
     }
   else
-    memset (entries, 0, size * sizeof (value_type));
+    {
+      for ( ; size; ++entries, --size)
+	*entries = value_type ();
+    }
   m_n_deleted = 0;
   m_n_elements = 0;
 }
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 5e1d94c..2705cbc 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1472,8 +1472,7 @@ allocate_and_init_ipcp_value (tree source)
 {
   ipcp_value<tree> *val;
 
-  val = ipcp_cst_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_cst_values_pool.allocate ()) ipcp_value<tree>();
   val->value = source;
   return val;
 }
@@ -1487,8 +1486,8 @@ allocate_and_init_ipcp_value (ipa_polymorphic_call_context source)
   ipcp_value<ipa_polymorphic_call_context> *val;
 
   // TODO
-  val = ipcp_poly_ctx_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_poly_ctx_values_pool.allocate ())
+    ipcp_value<ipa_polymorphic_call_context>();
   val->value = source;
   return val;
 }
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index 5819f78..212f423 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -3730,7 +3730,7 @@ void
 ipa_free_edge_args_substructures (struct ipa_edge_args *args)
 {
   vec_free (args->jump_functions);
-  memset (args, 0, sizeof (*args));
+  *args = ipa_edge_args ();
 }
 
 /* Free all ipa_edge structures.  */
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 9cc2996..dc93dce 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -6292,7 +6292,11 @@ lower_omp_ordered_clauses (gimple_stmt_iterator *gsi_p, gomp_ordered *ord_stmt,
     return;
 
   wide_int *folded_deps = XALLOCAVEC (wide_int, 2 * len - 1);
-  memset (folded_deps, 0, sizeof (*folded_deps) * (2 * len - 1));
+
+  /* wide_int is not a POD so it must be default-constructed.  */
+  for (unsigned i = 0; i != 2 * len - 1; ++i)
+    new (static_cast<void*>(folded_deps + i)) wide_int ();
+
   tree folded_dep = NULL_TREE;
   /* TRUE if the first dimension's offset is negative.  */
   bool neg_offset_p = false;
diff --git a/gcc/params.h b/gcc/params.h
index b61cff9..8b91660 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -42,7 +42,7 @@ struct param_info
 {
   /* The name used with the `--param <name>=<value>' switch to set this
      value.  */
-  const char *const option;
+  const char *option;
 
   /* The default value.  */
   int default_value;
@@ -54,7 +54,7 @@ struct param_info
   int max_value;
 
   /* A short description of the option.  */
-  const char *const help;
+  const char *help;
 
   /* The optional names corresponding to the values.  */
   const char **value_names;
diff --git a/gcc/testsuite/g++.dg/Wclass-memaccess.C b/gcc/testsuite/g++.dg/Wclass-memaccess.C
new file mode 100644
index 0000000..c67cd24
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wclass-memaccess.C
@@ -0,0 +1,1205 @@
+/* PR c++/80560 - warn on undefined memory operations involving non-trivial
+   types
+   { dg-do compile }
+   { dg-options "-Wclass-memaccess -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern "C"
+{
+void* memcpy (void*, const void*, size_t);
+void* memmove (void*, const void*, size_t);
+void* mempcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+void* realloc (void*, size_t);
+}
+
+/* Ordinary bzcopy and bzero aren't recognized as special.  */
+#define bcopy __builtin_bcopy
+#define bzero __builtin_bzero
+
+void sink (void*);
+
+#define T(fn, arglist) ((fn arglist), sink (p))
+
+#if !defined TEST || TEST == TEST_TRIVIAL
+
+/* Trivial can be manipulated by raw memory functions.  */
+struct Trivial { int i; unsigned bf: 1; char *s; char a[4]; };
+
+void test (Trivial *p, void *q, int x)
+{
+  const size_t n = x;
+
+  T (bzero, (p, 1));
+  T (bzero, (p, n));
+  T (bzero, (p, sizeof *p));
+  T (bzero, (q, 1));
+  T (bzero, (q, n));
+  T (bzero, (q, sizeof *p));
+
+  T (bcopy, (p, q, 1));
+  T (bcopy, (p, q, n));
+  T (bcopy, (p, q, sizeof *p));
+  T (bcopy, (q, p, 1));
+  T (bcopy, (q, p, n));
+  T (bcopy, (q, p, sizeof *p));
+
+  T (memcpy, (p, q, 1));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (q, p, 1));
+  T (memcpy, (q, p, n));
+  T (memcpy, (q, p, sizeof *p));
+
+  T (memset, (p, 0, 1));
+  T (memset, (p, 0, n));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (q, 0, 1));
+  T (memset, (q, 0, n));
+  T (memset, (q, 0, sizeof *p));
+
+  T (memset, (p, 1, 1));
+  T (memset, (p, 1, n));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (q, 1, 1));
+  T (memset, (q, 1, n));
+  T (memset, (q, 1, sizeof *p));
+
+  T (memset, (p, x, 1));
+  T (memset, (p, x, n));
+  T (memset, (p, x, sizeof *p));
+  T (memset, (q, x, 1));
+  T (memset, (q, x, n));
+  T (memset, (q, x, sizeof *p));
+
+  T (memmove, (p, q, 1));
+  T (memmove, (p, q, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (q, p, 1));
+  T (memmove, (q, p, n));
+  T (memmove, (q, p, sizeof *p));
+
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+
+  T (q = realloc, (q, 1));
+  T (q = realloc, (q, n));
+  T (q = realloc, (q, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DEFAULT
+
+/* HasDefault is trivially copyable but should be initialized by
+   the ctor, not bzero or memset.  */
+struct HasDefault { char a[4]; HasDefault (); };
+
+void test (HasDefault *p, const HasDefault &x,
+	   void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefault is neither trivial nor standard-layout.  The warning
+  // should mention the former since it's more permissive than the latter
+  // and so more informative.
+  T (bzero, (p, sizeof *p));   // { dg-warning "bzero(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefault.; use assignment or value-initialization instead" }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefault.; use assignment or value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefault.; use assignment instead" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefault.; use assignment instead" }
+
+  // Copying from another object of the same type is fine.
+  T (bcopy, (&x, p, sizeof *p));
+  T (bcopy, (&x, p, n));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+
+  // Copying from a void* or character buffer is also fine.
+  T (bcopy, (q, p, sizeof *p));
+  T (bcopy, (q, p, n));
+  T (bcopy, (s, p, sizeof *p));
+  T (bcopy, (s, p, n));
+
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, n));
+
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, q, n));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, s, n));
+
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+
+  // ...but partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefault. leaves 3 bytes unchanged" } */
+  T (memmove, (p, q, 2));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefault. leaves 2 bytes unchanged" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefault. leaves 1 byte unchanged" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .const int." }
+  extern long *ip;
+  T (memcpy, (p, ip, sizeof *p));  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .long." }
+
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning ".void\\* memmove(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .const int." }
+
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning ".void\\* mempcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .const int." }
+
+  // Reallocating is the same as calling memcpy except that only
+  // shrinking reallocation is diagnosed.
+  T (q = realloc, (p, 1));   // { dg-warning "moving an object of non-trivial type .struct HasDefault. and size 4 into a region of size 1" }
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_COPY
+
+/* HasCopy should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  Since it's non-trivial, it should not be zeroed
+   out by bzero/memset either and should instead use assignment and/or
+   value initialization.  */
+struct HasCopy { int i; HasCopy (const HasCopy&); };
+
+void test (HasCopy *p, const HasCopy &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because value initialization is
+  // invalid (the copy ctor makes no default ctor unavailable).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (bcopy, (&x, p, sizeof *p));    // { dg-warning "bcopy" }
+  T (bcopy, (q, p, sizeof *p));     // { dg-warning "bcopy" }
+  T (bcopy, (s, p, sizeof *p));     // { dg-warning "bcopy" }
+  T (bcopy, (ia, p, sizeof *p));    // { dg-warning "bcopy" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_PRIVATE_COPY
+
+/* HasPrivateCopy cannot be copied using memcpy or memmove.  Since it's
+   non-trivial, it it should not be zeroed out by bzero/memset either
+   and should instead use assignment and/or value initialization.  */
+struct HasPrivateCopy {
+  int i;
+private:
+  HasPrivateCopy (const HasPrivateCopy&);
+};
+
+void test (HasPrivateCopy *p, const HasPrivateCopy &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because value initialization is
+  // invalid (the copy ctor makes no default ctor unavailable).
+  // Verify also that the suggestion offers assignment but not
+  // value initialization (since the lattare is not available).
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of non-trivial type .struct HasPrivateCopy.; use assignment instead" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of non-trivially copyable type .struct HasPrivateCopy.; use copy-assignment instead" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DTOR
+
+/* HasDtor should be initialized using aggregate or memberwise intialization,
+   not bzero or memset.  */
+struct HasDtor { int i; ~HasDtor (); };
+
+void test (HasDtor *p, const HasDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed only because it's difficult not to.
+  // Otherwise, a class that's non-trivial only because it has
+  // a non-trivial dtor can be safely zeroed out (that's what
+  // value-initializing it does).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed simply because
+  // a class with a user-defined dtor is not trivially copyable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DELETED_DTOR
+
+// HasDeletedDtor is trivial so clearing and cpying it is okay.
+// Relocation would bypass the deleted dtor and so it's diagnosed.
+
+struct HasDeletedDtor
+{
+  int i;
+  ~HasDeletedDtor () = delete;
+};
+
+void test (HasDeletedDtor *p, const HasDeletedDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (p, i, sizeof *p));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  // Reallocating is diagnosed.
+  T (q = realloc, (p, 1));          // { dg-warning "moving an object of type .struct HasDeletedDtor. with deleted destructor" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_PRIVATE_DTOR
+
+// Unlike HasDeletedDtor, HasPrivateDtor is okay to zero-out and copy
+// but not relocate because doing so would bypass the deleted dtor..
+
+struct HasPrivateDtor
+{
+  int i;
+private:
+  ~HasPrivateDtor ();
+};
+
+void test (HasPrivateDtor *p, const HasPrivateDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of non-trivial type .struct HasPrivateDtor.; use assignment or value-initialization instead" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of non-trivially copyable type .struct HasPrivateDtor.; use copy-assignment or copy-initialization instead" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is diagnosed.
+  T (q = realloc, (p, 1));          // { dg-warning "moving an object of non-trivially copyable type .struct HasPrivateDtor." }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ASSIGN
+
+/* HasAssign should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  */
+struct HasAssign { int i; void operator= (HasAssign&); };
+
+void test (HasAssign *p, const HasAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_ASSIGN_OVERLOADS
+
+/* TrivialAssignOverloads is a trivial type.  */
+struct TrivialAssignOverloads {
+  int i;
+  typedef TrivialAssignOverloads Self;
+
+  Self& operator= (Self&) = delete;
+  Self& operator= (const Self&) = default;
+  Self& operator= (volatile Self&) = delete;
+  Self& operator= (const volatile Self&) = delete;
+};
+
+void test (TrivialAssignOverloads *p, const TrivialAssignOverloads &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (p, i, sizeof *p));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_VIRTUALS
+
+/* HasVirtuals should only be manipulated by the special member functions
+   and not by bzero, memcpy, or any other raw memory function. Doing
+   otherwse might corrupt the the vtable pointer.  */
+struct HasVirtuals { int i; virtual void foo (); };
+
+void test (HasVirtuals *p, const HasVirtuals &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it corrupts the vtable.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is diagnosed because when used to initialize an object
+  // could incorrectly initialize the vtable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_CONST_DATA
+
+/* HasConstData should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable allowing, raw memory functions to write into it would defeat
+   const-correctness.  */
+struct HasConstData { const char a[4]; };
+
+void test (HasConstData *p, const HasConstData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // The following is ill-formed because HasConstData's cannot
+  // be assigned (the assignment is implicitly deleted).  For
+  // that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object could break const correctness.
+  // Since the default ctor and copy assignment are both deleted,
+  // verify that they're not suggested as a possible alternative.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasConstData. with no trivial copy-assignment \\\[" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "clearing an object of type .struct HasConstData. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of type .struct HasConstData. with no trivial copy-assignment; use copy-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "writing to an object of type .struct HasConstData. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is not diagnosed except in C++ 98 due to a bug.
+  T (q = realloc, (p, 1));          // { dg-warning "moving an object of non-trivially copyable type .struct HasConstData.; use .new. and .delete. instead" "c++98" { target { c++98_only } } }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" "c++98" { target { c++98_only } } }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" "c++98" { target { c++98_only } } }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_REFERENCE
+
+/* HasReference should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable, allowing raw memory functions to write into it could
+   corrupt the reference.  */
+struct HasReference { int &ci; };
+
+void test (HasReference *p, const HasReference &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Similarly to HasConstData, the following is ill-formed because
+  // Hasreference cannot be assigned (the assignment is implicitly
+  // deleted).  For that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object would invalidate the reference.
+  // Since copy-assignment is deleted verify it's not suggested
+  // as an alternative.  (C++ 11 and later only; C++ 98 is broken).
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasReference. with no trivial copy-assignment \\\[" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "clearing an object of type .struct HasReference. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (bzero, (p, n));                // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 0, n));            // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, n));            // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, n));            // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of type .struct HasReference. with no trivial copy-assignment; use copy-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "writing to an object of type .struct HasReference. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is not diagnosed because a type with a reference
+  // is (perhaps surprisingly) trivially copyable.  It is diagnosed
+  // in C++ 98 because of a bug, but it seems like it should be
+  // diagnosed in all modes.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_MEM_DATA_PTR
+
+/* HasMemDataPtr should only be initialized using aggregate initializatoon
+   and not cleared by bzero or written into using memset because its
+   representation is different from ordinary scalars (a null member data
+   pointer is all ones).  It can be copied into using memcpy from an object
+   of the same type or from a character buffer.  */
+struct HasMemDataPtr { int HasMemDataPtr::*p; };
+
+void test (HasMemDataPtr *p, const HasMemDataPtr &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because a null member data pointer has
+  // a representation that's all bits set.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasMemDataPtr. containing a pointer-to-member" }
+  T (bzero, (p, n));                // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 0, n));            // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, n));            // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, n));            // { dg-warning "memset" }
+
+  // Copying is not diagnosed.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, n));
+  T (memcpy, (p, ia, sizeof *p));
+  T (memcpy, (p, ia, n));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PRIVATE_DATA
+
+/* HasSomePrivateData can be initialized using value initialization
+   and should not be written to using memset with a non-zero argument.
+   Doing otherwise would break encapsulation.  */
+struct HasSomePrivateData { char a[2]; private: char b[2]; };
+
+void test (HasSomePrivateData *p, const HasSomePrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PROTECTED_DATA
+
+/* Similarly to HasSomePrivateData, HasSomeProtectedData can be
+   initialized using value initialization and should not be written
+   to using memset with a non-zero argument.  Doing otherwise would
+   break encapsulation.  */
+struct HasSomeProtectedData { char a[2]; protected: char b[2]; };
+
+void test (HasSomeProtectedData *p, const HasSomeProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PRIVATE_DATA
+
+/* Similarly to HasSomePrivateData, HasAllPrivateData should only be
+   initialized using value initializatoon and should not be written
+   to using memset with non-zero argument.  They are tested separately
+   because unlike the former classes, these are standard layout.  */
+struct HasAllPrivateData { private: char a[4]; };
+
+void test (HasAllPrivateData *p, const HasAllPrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PROTECTED_DATA
+
+/* Similarly to HasSomeProtectedData, HasAllProtectedData should only
+   be initialized using value initializatoon and should not be written
+   to using memset with non-zero argument.  They are tested separately
+   because unlike the former classes, these are standard layout.  */
+struct HasAllProtectedData { protected: char a[4]; };
+
+void test (HasAllProtectedData *p, const HasAllProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_DEFAULT_CTOR_PRIVATE_ASSIGN
+
+/* Used to verify suggested alternatives.  */
+struct HasDefaultPrivateAssign
+{
+  char a[4];
+  HasDefaultPrivateAssign ();
+private:
+  void operator= (HasDefaultPrivateAssign&);
+};
+
+void test (HasDefaultPrivateAssign *p, const HasDefaultPrivateAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefaultPrivateAssign isn't trivial or assignable.  Verify
+  // that the alternative suggested in the warning is to use copy or
+  // default but not assignment.
+  T (bzero, (p, sizeof *p));   // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 98" { target { c++98_only } } .-1 }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment; use value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment" }
+
+  // Copying from another object of the same type is diagnosed because
+  // the copy assignment is inaccessible.  Verify that the suggested
+  // alternative is not copy assignment (C++ 98 is busted).
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment" "c++98" { target c++98_only } .-1 }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+
+  // Similarly for copying from a void* or character buffer.
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment" "c++98" { target c++98_only } ,-1 }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, q, n));           // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, n));           // { dg-warning "memmove" }
+
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, n));           // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, n));           // { dg-warning "mempcpy" }
+
+  // Same for partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment" } */
+  T (memmove, (p, q, 2));   // { dg-warning "memmove" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "mempcpy" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning "writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment." }
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning "memmove" }
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_DEFAULT_CTOR_DELETED_ASSIGN
+
+/* Used to verify suggested alternatives.  */
+struct HasDefaultDeletedAssign
+{
+  char a[4];
+  HasDefaultDeletedAssign ();
+private:
+  void operator= (HasDefaultDeletedAssign&);
+};
+
+void test (HasDefaultDeletedAssign *p, const HasDefaultDeletedAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefaultDeletedAssign isn't trivial or assignable.  Verify
+  // that the alternative suggested in the warning is to use copy or
+  // default but not assignment.
+  T (bzero, (p, sizeof *p));   // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 98" { target { c++98_only } } .-1 }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment; use value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment" }
+
+  // Copying from another object of the same type is diagnosed because
+  // the copy assignment is inaccessible.  Verify that the suggested
+  // alternative is not copy assignment (C++ 98 is busted).
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment" "c++98" { target c++98_only } .-1 }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+
+  // Similarly for copying from a void* or character buffer.
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment" "c++98" { target c++98_only } ,-1 }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, q, n));           // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, n));           // { dg-warning "memmove" }
+
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, n));           // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, n));           // { dg-warning "mempcpy" }
+
+  // Same for partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment" } */
+  T (memmove, (p, q, 2));   // { dg-warning "memmove" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "mempcpy" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning "writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment." }
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning "memmove" }
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_EXPRESSION
+
+void test_expr (int i)
+{
+  struct TestClass { TestClass () { } };
+  TestClass a, b;
+
+  static void *p;
+
+  T (bzero, (i < 0 ? &a : &b, 1));  // { dg-warning "bzero" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_CTOR
+
+void test_ctor ()
+{
+#undef T
+#define T(fn, arglist) (fn arglist, sink (this))
+
+  static void *p;
+
+  struct TestBase
+  {
+    TestBase ()
+    {
+      /* A ctor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+
+    ~TestBase ()
+    {
+      /* A dtor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestBaseVtable
+  {
+    TestBaseVtable ()
+    {
+      /* A ctor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    ~TestBaseVtable ()
+    {
+      /* A dtor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    virtual void foo ();
+  };
+
+  struct TestDerived: HasDefault
+  {
+    TestDerived ()
+    {
+      /* A derived class ctor is treated as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestDerivedDtor: HasDefault
+  {
+    ~TestDerivedDtor ()
+    {
+      /* A derived class dtor is treated as an ordinary function though
+	 it probably shouldn't be unless the base dtor is trivial.  But
+	 it doesn't seem worth the trouble.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+  };
+}
+
+#endif
+
+// { dg-prune-output "defaulted and deleted functions" }
diff --git a/gcc/tree-switch-conversion.c b/gcc/tree-switch-conversion.c
index 0a2a840..7a58209 100644
--- a/gcc/tree-switch-conversion.c
+++ b/gcc/tree-switch-conversion.c
@@ -266,7 +266,7 @@ static void
 emit_case_bit_tests (gswitch *swtch, tree index_expr,
 		     tree minval, tree range, tree maxval)
 {
-  struct case_bit_test test[MAX_CASE_BIT_TESTS];
+  struct case_bit_test test[MAX_CASE_BIT_TESTS] = { };
   unsigned int i, j, k;
   unsigned int count;
 
@@ -291,8 +291,6 @@ emit_case_bit_tests (gswitch *swtch, tree index_expr,
   int prec = TYPE_PRECISION (word_type_node);
   wide_int wone = wi::one (prec);
 
-  memset (&test, 0, sizeof (test));
-
   /* Get the edge for the default case.  */
   tmp = gimple_switch_default_label (swtch);
   default_bb = label_to_block (CASE_LABEL (tmp));
diff --git a/gcc/vec.h b/gcc/vec.h
index fee4616..0e9a13b 100644
--- a/gcc/vec.h
+++ b/gcc/vec.h
@@ -407,6 +407,26 @@ struct GTY((user)) vec
 {
 };
 
+/* Default-construct N elements in DST.  */
+
+template <typename T>
+inline void
+vec_default_construct (T *dst, unsigned n)
+{
+  for ( ; n; ++dst, --n)
+    ::new (static_cast<void*>(dst)) T ();
+}
+
+/* Copy-construct N elements in DST from *SRC.  */
+
+template <typename T>
+inline void
+vec_copy_construct (T *dst, const T *src, unsigned n)
+{
+  for ( ; n; ++dst, ++src, --n)
+    ::new (static_cast<void*>(dst)) T (*src);
+}
+
 /* Type to provide NULL values for vec<T, A, L>.  This is used to
    provide nil initializers for vec instances.  Since vec must be
    a POD, we cannot have proper ctor/dtor for it.  To initialize
@@ -615,7 +635,7 @@ vec_safe_grow_cleared (vec<T, A, vl_embed> *&v, unsigned len CXX_MEM_STAT_INFO)
 {
   unsigned oldlen = vec_safe_length (v);
   vec_safe_grow (v, len PASS_MEM_STAT);
-  memset (&(v->address ()[oldlen]), 0, sizeof (T) * (len - oldlen));
+  vec_default_construct (v->address () + oldlen, len - oldlen);
 }
 
 
@@ -821,7 +841,7 @@ vec<T, A, vl_embed>::copy (ALONE_MEM_STAT_DECL) const
     {
       vec_alloc (new_vec, len PASS_MEM_STAT);
       new_vec->embedded_init (len, len);
-      memcpy (new_vec->address (), m_vecdata, sizeof (T) * len);
+      vec_copy_construct (new_vec->address (), m_vecdata, len);
     }
   return new_vec;
 }
@@ -838,7 +858,7 @@ vec<T, A, vl_embed>::splice (const vec<T, A, vl_embed> &src)
   if (len)
     {
       gcc_checking_assert (space (len));
-      memcpy (address () + length (), src.address (), len * sizeof (T));
+      vec_copy_construct (end (), src.address (), len);
       m_vecpfx.m_num += len;
     }
 }
@@ -1092,13 +1112,12 @@ inline void
 vec<T, A, vl_embed>::quick_grow_cleared (unsigned len)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   quick_grow (len);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
-
 /* Garbage collection support for vec<T, A, vl_embed>.  */
 
 template<typename T>
@@ -1445,7 +1464,7 @@ vec<T, va_heap, vl_ptr>::reserve (unsigned nelems, bool exact MEM_STAT_DECL)
   va_heap::reserve (m_vec, nelems, exact PASS_MEM_STAT);
   if (handle_auto_vec)
     {
-      memcpy (m_vec->address (), oldvec->address (), sizeof (T) * oldsize);
+      vec_copy_construct (m_vec->address (), oldvec->address (), oldsize);
       m_vec->m_vecpfx.m_num = oldsize;
     }
 
@@ -1607,10 +1626,10 @@ inline void
 vec<T, va_heap, vl_ptr>::safe_grow_cleared (unsigned len MEM_STAT_DECL)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   safe_grow (len PASS_MEM_STAT);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
 
diff --git a/libcpp/line-map.c b/libcpp/line-map.c
index c4b7cb2..56b7913 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -62,7 +62,8 @@ extern unsigned num_macro_tokens_counter;
 
 line_maps::~line_maps ()
 {
-  htab_delete (location_adhoc_data_map.htab);
+  if (location_adhoc_data_map.htab)
+    htab_delete (location_adhoc_data_map.htab);
 }
 
 /* Hash function for location_adhoc_data hashtable.  */
@@ -347,7 +348,7 @@ void
 linemap_init (struct line_maps *set,
 	      source_location builtin_location)
 {
-  memset (set, 0, sizeof (struct line_maps));
+  *set = line_maps ();
   set->highest_location = RESERVED_LOCATION_COUNT - 1;
   set->highest_line = RESERVED_LOCATION_COUNT - 1;
   set->location_adhoc_data_map.htab =
diff --git a/libitm/beginend.cc b/libitm/beginend.cc
index d04f3e9..c6550a3 100644
--- a/libitm/beginend.cc
+++ b/libitm/beginend.cc
@@ -431,7 +431,7 @@ GTM::gtm_transaction_cp::save(gtm_thread* tx)
   // Save everything that we might have to restore on restarts or aborts.
   jb = tx->jb;
   undolog_size = tx->undolog.size();
-  memcpy(&alloc_actions, &tx->alloc_actions, sizeof(alloc_actions));
+  alloc_actions = tx->alloc_actions;
   user_actions_size = tx->user_actions.size();
   id = tx->id;
   prop = tx->prop;
@@ -449,7 +449,7 @@ GTM::gtm_transaction_cp::commit(gtm_thread* tx)
   // commits of nested transactions. Allocation actions must be committed
   // before committing the snapshot.
   tx->jb = jb;
-  memcpy(&tx->alloc_actions, &alloc_actions, sizeof(alloc_actions));
+  tx->alloc_actions = alloc_actions;
   tx->id = id;
   tx->prop = prop;
 }
@@ -485,7 +485,7 @@ GTM::gtm_thread::rollback (gtm_transaction_cp *cp, bool aborting)
       prop = cp->prop;
       if (cp->disp != abi_disp())
 	set_abi_disp(cp->disp);
-      memcpy(&alloc_actions, &cp->alloc_actions, sizeof(alloc_actions));
+      alloc_actions = cp->alloc_actions;
       nesting = cp->nesting;
     }
   else
diff --git a/libitm/method-ml.cc b/libitm/method-ml.cc
index fcae334..b857bff 100644
--- a/libitm/method-ml.cc
+++ b/libitm/method-ml.cc
@@ -138,7 +138,11 @@ struct ml_mg : public method_group
     // This store is only executed while holding the serial lock, so relaxed
     // memory order is sufficient here.  Same holds for the memset.
     time.store(0, memory_order_relaxed);
-    memset(orecs, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
+    // The memset below isn't strictly kosher because it bypasses
+    // the non-trivial assignment operator defined by std::atomic.  Using
+    // a local void* is enough to prevent GCC from warning for this.
+    void *p = orecs;
+    memset(p, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
   }
 };
 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-06  1:53                                       ` Martin Sebor
@ 2017-06-06 22:24                                         ` Martin Sebor
  2017-06-08  1:09                                           ` Jason Merrill
  0 siblings, 1 reply; 46+ messages in thread
From: Martin Sebor @ 2017-06-06 22:24 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Pedro Alves, Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 2886 bytes --]

On 06/05/2017 07:53 PM, Martin Sebor wrote:
> On 06/05/2017 01:13 PM, Martin Sebor wrote:
>> On 06/05/2017 10:07 AM, Martin Sebor wrote:
>>>>>> Maybe I should use a different approach and instead of trying
>>>>>> to see if a function is deleted use trivially_xible to see if
>>>>>> it's usable.  That will mean changing the diagnostics from
>>>>>> "with a deleted special function" to "without trivial special
>>>>>> function" but it will avoid calling synthesized_method_walk
>>>>>> while still avoiding giving bogus suggestions.
>>>>
>>>> Actually, this would check for one possible argument type and not
>>>> others, so I think it's better to keep looking at the declarations.
>>>> You
>>>> can do that by just looking them up (lookup_fnfields_slot) and
>>>> iterating
>>>> over them, you don't need to call synthesized_method_walk.
>>>
>>> You mean using trivially_xible might check assignability or copy
>>> constructibility from const T& but not from T& (or the other way
>>> around), and you think both (or perhaps even other forms) should
>>> be considered?
>>>
>>> E.g., given:
>>>
>>>   struct S
>>>   {
>>>     S& operator= (const S&) = default;
>>>     void operator= (S&) = delete;
>>>   };
>>>
>>>   void f (S *d, const S *s)
>>>   {
>>>     memcpy(d, s, sizeof *d);   // don't warn here
>>>   }
>>>
>>>   void g (S *d, S *s)
>>>   {
>>>     memcpy(d, s, sizeof *d);   // but warn here
>>>   }
>>>
>>> And your suggestion is to iterate over the assignment operator
>>> (and copy ctor) overloads for S looking for one that's trivial,
>>> public, and not deleted?
>>>
>>> If that's it, I was thinking of just checking for the const T&
>>> overload (as if by using std::is_trivially_copy_assignable<T>()).
>>>
>>> I don't mind trying the approach you suggest.  It should be more
>>> accurate.  I just want to make sure we're on the same page.
>>
>> Actually, after some more thought and testing the approach I have
>> a feeling that distinguishing between the two cases above is not
>> what you meant.
>>
>> Classes that overload copy assignment or copy ctors on the constness
>> of the argument are tricky to begin with and using raw memory calls
>> on them seems suspect and worthy of a warning.
>>
>> I'm guessing what you meant by "checking for one possible argument
>> type and not others" is actually checking to make sure all copy
>> assignment (and copy ctor) overloads are trivial, not jut some,
>> and at least one of them is accessible.  I'll go with that unless
>> I hear otherwise.
>
> Attached is an update that implements this simplified approach.

Another update, this one rebased on top of trunk (where OVL_NEXT
doesn't exist).  This version also adds a couple of minor tweaks
to handle template ctors and assignment operators, and to handle
and exercise move ctors and move assignment and const/non-const
overloads of each.  Tested on x86_64-linux.

Martin

[-- Attachment #2: gcc-80560.diff --]
[-- Type: text/x-patch, Size: 95178 bytes --]

PR c++/80560 - warn on undefined memory operations involving non-trivial types

gcc/c-family/ChangeLog:

	PR c++/80560
	* c.opt (-Wclass-memaccess): New option.

gcc/cp/ChangeLog:

	PR c++/80560
	* call.c (first_non_public_field, maybe_warn_class_memaccess): New
	functions.
	(has_trivial_copy_assign_p, has_trivial_copy_p): Ditto.
	(build_cxx_call): Call maybe_warn_class_memaccess.

gcc/ChangeLog:

	PR c++/80560
	* dumpfile.c (dump_register): Avoid calling memset to initialize
	a class with a default ctor.
	* gcc.c (struct compiler): Remove const qualification.
	* genattrtab.c (gen_insn_reserv): Replace memset with initialization.
	* hash-table.h: Ditto.
	* ipa-cp.c (allocate_and_init_ipcp_value): Replace memset with
	  assignment.
	* ipa-prop.c (ipa_free_edge_args_substructures): Ditto.
	* omp-low.c (lower_omp_ordered_clauses): Replace memset with
	default ctor.
	* params.h (struct param_info): Make struct members non-const.
	* tree-switch-conversion.c (emit_case_bit_tests): Replace memset
	with default initialization.
	* vec.h (vec_copy_construct, vec_default_construct): New helper
	functions.
	(vec<T>::copy, vec<T>::splice, vec<T>::reserve): Replace memcpy
	with vec_copy_construct.
	(vect<T>::quick_grow_cleared): Replace memset with default ctor.
	(vect<T>::vec_safe_grow_cleared, vec_safe_grow_cleared): Same.
	* doc/invoke.texi (-Wclass-memaccess): Document.

libcpp/ChangeLog:

	PR c++/80560
	* line-map.c (line_maps::~line_maps): Avoid calling htab_delete
	with a null pointer.
	(linemap_init): Avoid calling memset on an object of a non-trivial
	type.

libitm/ChangeLog:

	PR c++/80560
	* beginend.cc (GTM::gtm_thread::rollback): Avoid calling memset
	on an object of a non-trivial type.
	(GTM::gtm_transaction_cp::commit): Use assignment instead of memcpy
	to copy an object.
	* method-ml.cc (orec_iterator::reinit): Avoid -Wclass-memaccess.

gcc/testsuite/ChangeLog:

	PR c++/80560
	* g++.dg/Wclass-memaccess.C: New test.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 37bb236..363d104 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -804,6 +804,10 @@ Wnon-template-friend
 C++ ObjC++ Var(warn_nontemplate_friend) Init(1) Warning
 Warn when non-templatized friend functions are declared within a template.
 
+Wclass-memaccess
+C++ ObjC++ Var(warn_class_memaccess) Warning LangEnabledBy(C++ ObjC++, Wall)
+Warn for unsafe raw memory writes to objects of class types.
+
 Wnon-virtual-dtor
 C++ ObjC++ Var(warn_nonvdtor) Warning LangEnabledBy(C++ ObjC++,Weffc++)
 Warn about non-virtual destructors.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 51260f0..6d8e77e 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8146,6 +8146,396 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
   return call;
 }
 
+/* Return the DECL of the first non-public data member of class TYPE
+   or null if none can be found.  */
+
+static tree
+first_non_public_field (tree type)
+{
+  if (!CLASS_TYPE_P (type))
+    return NULL_TREE;
+
+  for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+	continue;
+      if (TREE_STATIC (field))
+	continue;
+      if (TREE_PRIVATE (field) || TREE_PROTECTED (field))
+	return field;
+    }
+
+  int i = 0;
+
+  for (tree base_binfo, binfo = TYPE_BINFO (type);
+       BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+    {
+      tree base = TREE_TYPE (base_binfo);
+
+      if (tree field = first_non_public_field (base))
+	return field;
+    }
+
+  return NULL_TREE;
+}
+
+/* Return true if all copy and move assignment operator overloads for
+   class TYPE are trivial and at least one of them is not deleted and,
+   when ACCESS is set, accessible.  Return false otherwise.  Set
+   HASASSIGN to true when the TYPE has a (not necessarily trivial)
+   copy or move assignment.  */
+
+static bool
+has_trivial_copy_assign_p (tree type, bool access, bool *hasassign)
+{
+  tree fns = cp_assignment_operator_id (NOP_EXPR);
+  fns = lookup_fnfields_slot (type, fns);
+
+  bool all_trivial = true;
+
+  /* Iterate over copy and move assignment overloads.  */
+
+  for (ovl_iterator oi (fns); oi; ++oi)
+    {
+      tree f = *oi;
+
+      bool accessible = !access || !(TREE_PRIVATE (f) || TREE_PROTECTED (f));
+
+      /* Skip template assignment operators and deleted functions.  */
+      if (TREE_CODE (f) != FUNCTION_DECL || DECL_DELETED_FN (f))
+	continue;
+
+      if (accessible)
+	*hasassign = true;
+
+      if (!accessible || !trivial_fn_p (f))
+	all_trivial = false;
+
+      /* Break early when both properties have been determined.  */
+      if (*hasassign && !all_trivial)
+	break;
+    }
+
+  /* Return true if they're all trivial and one of the expressions
+     TYPE() = TYPE() or TYPE() = (TYPE&)() is valid.  */
+  tree ref = cp_build_reference_type (type, false);
+  return (all_trivial
+	  && (is_trivially_xible (MODIFY_EXPR, type, type)
+	      || is_trivially_xible (MODIFY_EXPR, type, ref)));
+}
+
+/* Return true if all copy and move ctor overloads for class TYPE are
+   trivial and at least one of them is not deleted and, when ACCESS is
+   set, accessible.  Return false otherwise.  Set each element of HASCTOR[]
+   to true when the TYPE has a (not necessarily trivial) default and copy
+   (or move) ctor, respectively.  */
+
+static bool
+has_trivial_copy_p (tree type, bool access, bool hasctor[2])
+{
+  tree fns = lookup_fnfields_slot (type, complete_ctor_identifier);
+
+  bool all_trivial = true;
+
+  for (ovl_iterator oi (fns); oi; ++oi)
+    {
+      tree f = *oi;
+
+      /* Skip template constructors.  */
+      if (TREE_CODE (f) != FUNCTION_DECL)
+	continue;
+
+      tree arg = TREE_CHAIN (DECL_ARGUMENTS (f));
+      bool cpy_or_move_ctor_p;
+      if (arg)
+	{
+	  tree argtype = TREE_TYPE (arg);
+	  if (TREE_CODE (argtype) == REFERENCE_TYPE)
+	    argtype = TREE_TYPE (argtype);
+
+	  if (!same_type_ignoring_top_level_qualifiers_p (type, argtype))
+	    continue;
+
+	  cpy_or_move_ctor_p = true;
+	}
+      else
+	cpy_or_move_ctor_p = false;
+
+      if (DECL_DELETED_FN (f))
+	continue;
+
+      bool accessible = !access || !(TREE_PRIVATE (f) || TREE_PROTECTED (f));
+
+      if (accessible)
+	hasctor[cpy_or_move_ctor_p] = true;
+
+      if (cpy_or_move_ctor_p && (!accessible || !trivial_fn_p (f)))
+	all_trivial = false;
+
+      /* Break early when both properties have been determined.  */
+      if (hasctor[0] && hasctor[1] && !all_trivial)
+	break;
+    }
+
+  return all_trivial;
+}
+
+/* issue a warning on a call to the built-in function FNDECL if it is
+   a raw memory write whose destination is not an object of (something
+   like) trivial or standard layout type with a non-deleted assignment
+   and copy ctor.  Detects const correctness violations, corrupting
+   references, virtual table pointers, and bypassing non-trivial
+   assignments.  */
+
+static void
+maybe_warn_class_memaccess (location_t loc, tree fndecl, tree *args)
+{
+  /* Except for bcopy where it's second, the destination pointer is
+     the first argument for all functions handled here.  Compute
+     the index of the destination and source arguments.  */
+  unsigned dstidx = DECL_FUNCTION_CODE (fndecl) == BUILT_IN_BCOPY;
+  unsigned srcidx = !dstidx;
+
+  tree dest = args[dstidx];
+  if (!dest || !TREE_TYPE (dest) || !POINTER_TYPE_P (TREE_TYPE (dest)))
+    return;
+
+  STRIP_NOPS (dest);
+
+  tree srctype = NULL_TREE;
+
+  /* Determine the type of the pointed-to object and whether it's
+     a complete class type.  */
+  tree desttype = TREE_TYPE (TREE_TYPE (dest));
+
+  if (!desttype || !COMPLETE_TYPE_P (desttype) || !CLASS_TYPE_P (desttype))
+    return;
+
+  /* Check to see if the raw memory call is made by a ctor or dtor
+     with this as the destination argument for the destination type.
+     If so, be more permissive.  */
+  if (current_function_decl
+      && (DECL_CONSTRUCTOR_P (current_function_decl)
+	  || DECL_DESTRUCTOR_P (current_function_decl))
+      && is_this_parameter (dest))
+    {
+      tree ctx = DECL_CONTEXT (current_function_decl);
+      bool special = same_type_ignoring_top_level_qualifiers_p (ctx, desttype);
+
+      tree binfo = TYPE_BINFO (ctx);
+
+      /* A ctor and dtor for a class with no bases and no virtual functions
+	 can do whatever they want.  Bail early with no further checking.  */
+      if (special && !BINFO_VTABLE (binfo) && !BINFO_N_BASE_BINFOS (binfo))
+	return;
+    }
+
+  /* True if the class is trivial.  */
+  bool trivial = trivial_type_p (desttype);
+
+  /* Set to true if DESTYPE has an accessible copy assignment.  */
+  bool hasassign = false;
+  /* True if all of the class' overloaded copy assignment operators
+     are all trivial (and not deleted) and at least one of them is
+     accessible.  */
+  bool trivassign = has_trivial_copy_assign_p (desttype, true, &hasassign);
+
+  /* Set to true if DESTTYPE has an accessible defqault and copy ctor,
+     respectively.  */
+  bool hasctors[2] = { false, false };
+
+  /* True if all of the class' overloaded copy constructors are all
+     trivial (and not deleted) and at least one of them is accessible.  */
+  bool trivcopy = has_trivial_copy_p (desttype, true, hasctors);
+
+  /* Set FLD to the first private/protected member of the class.  */
+  tree fld = trivial ? first_non_public_field (desttype) : NULL_TREE;
+
+  /* The warning format string.  */
+  const char *warnfmt = NULL;
+  /* A suggested alternative to offer instead of the raw memory call.
+     Empty string when none can be come up with.  */
+  const char *suggest = "";
+  bool warned = false;
+
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    case BUILT_IN_MEMSET:
+      if (!integer_zerop (args[1]))
+	{
+	  /* Diagnose setting non-copy-assignable or non-trivial types,
+	     or types with a private member, to (potentially) non-zero
+	     bytes.  Since the value of the bytes being written is unknown,
+	     suggest using assignment instead (if one exists).  Also warn
+	     for writes into objects for which zero-initialization doesn't
+	     mean all bits clear (pointer-to-member data, where null is all
+	     bits set).  Since the value being written is (most likely)
+	     non-zero, simply suggest assignment (but not copy assignment).  */
+	  suggest = "; use assignment instead";
+	  if (!trivassign)
+	    warnfmt = G_("%qD writing to an object of type %#qT with "
+			 "no trivial copy-assignment");
+	  else if (!trivial)
+	    warnfmt = G_("%qD writing to an object of non-trivial type %#qT%s");
+	  else if (fld)
+	    {
+	      const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	      warned = warning_at (loc, OPT_Wclass_memaccess,
+				   "%qD writing to an object of type %#qT with "
+				   "%qs member %qD",
+				   fndecl, desttype, access, fld);
+	    }
+	  else if (!zero_init_p (desttype))
+	    warnfmt = G_("%qD writing to an object of type %#qT containing "
+			 "a pointer to data member%s");
+
+	  break;
+	}
+      /* Fall through.  */
+
+    case BUILT_IN_BZERO:
+      /* Similarly to the above, diagnose clearing non-trivial or non-
+	 standard layout objects, or objects of types with no assignmenmt.
+	 Since the value being written is known to be zero, suggest either
+	 copy assignment, copy ctor, or default ctor as an alternative,
+	 depending on what's available.  */
+
+      if (hasassign && hasctors[0])
+	suggest = G_("; use assignment or value-initialization instead");
+      else if (hasassign)
+	suggest = G_("; use assignment instead");
+      else if (hasctors[0])
+	suggest = G_("; use value-initialization instead");
+
+      if (!trivassign)
+	warnfmt = G_("%qD clearing an object of type %#qT with "
+		     "no trivial copy-assignment%s");
+      else if (!trivial)
+	warnfmt =  G_("%qD clearing an object of non-trivial type %#qT%s");
+      else if (!zero_init_p (desttype))
+	warnfmt = G_("%qD clearing an object of type %#qT containing "
+		     "a pointer-to-member%s");
+      break;
+
+    case BUILT_IN_BCOPY:
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMPCPY:
+      /* Determine the type of the source object.  */
+      srctype = STRIP_NOPS (args[srcidx]);
+      srctype = TREE_TYPE (TREE_TYPE (srctype));
+
+      /* Since it's impossible to determine wheter the byte copy is
+	 being used in place of assignment to an existing object or
+	 as a substitute for initialization, assume it's the former.
+	 Determine the best alternative to use instead depending on
+	 what's not deleted.  */
+      if (hasassign && hasctors[1])
+	suggest = G_("; use copy-assignment or copy-initialization instead");
+      else if (hasassign)
+	suggest = G_("; use copy-assignment instead");
+      else if (hasctors[1])
+	suggest = G_("; use copy-initialization instead");
+
+      if (!trivassign)
+	warnfmt = G_("%qD writing to an object of type %#qT with no trivial "
+		     "copy-assignment%s");
+      else if (!trivially_copyable_p (desttype))
+	warnfmt = G_("%qD writing to an object of non-trivially copyable "
+		     "type %#qT%s");
+      else if (!trivcopy)
+	warnfmt = G_("%qD writing to an object with a deleted copy constructor");
+
+      else if (!trivial
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  /* Warn when copying into a non-trivial object from an object
+	     of a different type other than void or char.  */
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD copying an object of non-trivial type "
+			       "%#qT from an array of %#qT",
+			       fndecl, desttype, srctype);
+	}
+      else if (fld
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD copying an object of type %#qT with "
+			       "%qs member %qD from an array of %#qT; use "
+			       "assignment or copy-initialization instead",
+			       fndecl, desttype, access, fld, srctype);
+	}
+      else if (!trivial && TREE_CODE (args[2]) == INTEGER_CST)
+	{
+	  /* Finally, warn on partial copies.  */
+	  unsigned HOST_WIDE_INT typesize
+	    = tree_to_uhwi (TYPE_SIZE_UNIT (desttype));
+	  if (unsigned HOST_WIDE_INT partial
+	      = tree_to_uhwi (args[2]) % typesize)
+	    warned = warning_at (loc, OPT_Wclass_memaccess,
+				 (typesize - partial > 1
+				  ? G_("%qD writing to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "bytes unchanged")
+				  : G_("%qD writing to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "byte unchanged")),
+				 fndecl, desttype, typesize - partial);
+	}
+      break;
+
+    case BUILT_IN_REALLOC:
+
+      if (!trivially_copyable_p (desttype))
+	warnfmt = G_("%qD moving an object of non-trivially copyable type "
+		     "%#qT; use %<new%> and %<delete%> instead");
+      else if (!trivcopy)
+	warnfmt = G_("%qD moving an object of type %#qT with deleted copy "
+		     "constructor; use %<new%> and %<delete%> instead");
+      else if (!get_dtor (desttype, tf_none))
+	warnfmt = G_("%qD moving an object of type %#qT with deleted "
+		     "destructor");
+      else if (!trivial
+	       && TREE_CODE (args[1]) == INTEGER_CST
+	       && tree_int_cst_lt (args[1], TYPE_SIZE_UNIT (desttype)))
+	{
+	  /* Finally, warn on reallocation into insufficient space.  */
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD moving an object of non-trivial type "
+			       "%#qT and size %E into a region of size %E",
+			       fndecl, desttype, TYPE_SIZE_UNIT (desttype),
+			       args[1]);
+	}
+      break;
+
+    default:
+      return;
+    }
+
+  if (!warned && !warnfmt)
+    return;
+
+  if (warnfmt)
+    {
+      if (suggest)
+	warned = warning_at (loc, OPT_Wclass_memaccess,
+			     warnfmt, fndecl, desttype, suggest);
+      else
+	warned = warning_at (loc, OPT_Wclass_memaccess,
+			     warnfmt, fndecl, desttype);
+    }
+
+  if (warned)
+    inform (location_of (desttype), "%#qT declared here", desttype);
+}
+
 /* Build and return a call to FN, using NARGS arguments in ARGARRAY.
    This function performs no overload resolution, conversion, or other
    high-level operations.  */
@@ -8178,6 +8568,10 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
       if (!check_builtin_function_arguments (EXPR_LOCATION (fn), vNULL, fndecl,
 					     nargs, argarray))
 	return error_mark_node;
+
+      /* Warn if the built-in writes to an object of a non-trivial type.  */
+      if (nargs)
+	maybe_warn_class_memaccess (loc, fndecl, argarray);
     }
 
     /* If it is a built-in array notation function, then the return type of
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 4a83a3e..475673c 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -215,7 +215,8 @@ in the following sections.
 -Wabi=@var{n}  -Wabi-tag  -Wconversion-null  -Wctor-dtor-privacy @gol
 -Wdelete-non-virtual-dtor  -Wliteral-suffix  -Wmultiple-inheritance @gol
 -Wnamespaces  -Wnarrowing @gol
--Wnoexcept  -Wnoexcept-type  -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
+-Wnoexcept  -Wnoexcept-type  -Wclass-memaccess @gol
+-Wnon-virtual-dtor  -Wreorder  -Wregister @gol
 -Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
 -Wno-non-template-friend  -Wold-style-cast @gol
 -Woverloaded-virtual  -Wno-pmf-conversions @gol
@@ -2920,6 +2921,23 @@ void g() noexcept;
 void h() @{ f(g); @} // in C++14 calls f<void(*)()>, in C++1z calls f<void(*)()noexcept>
 @end smallexample
 
+@item -Wclass-memaccess @r{(C++ and Objective-C++ only)}
+@opindex Wclass-memaccess
+Warn when the destination of a call to a raw memory function such as
+@code{memset} or @code{memcpy} is an object of class type writing into which
+might bypass the class non-trivial or deleted constructor or copy assignment,
+violate const-correctness or encapsulation, or corrupt the virtual table.
+Modifying the representation of such objects may violate invariants maintained
+by member functions of the class.  For example, the call to @code{memset}
+below is undefined becase it modifies a non-trivial class object and is,
+therefore, diagnosed.  The safe way to either initialize or clear the storage
+of objects of such types is by using the appropriate constructor or assignment
+operator, if one is available.
+@smallexample
+std::string str = "abc";
+memset (&str, 0, 3);
+@end smallexample
+The @option{-Wclass-memaccess} option is enabled by @option{-Wall}.
 
 @item -Wnon-virtual-dtor @r{(C++ and Objective-C++ only)}
 @opindex Wnon-virtual-dtor
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index b8bda3c..b336d5b 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -185,9 +185,16 @@ dump_register (const char *suffix, const char *swtch, const char *glob,
       m_extra_dump_files = XRESIZEVEC (struct dump_file_info,
 				       m_extra_dump_files,
 				       m_extra_dump_files_alloced);
+
+      /* Construct a new object in the space allocated above.  */
+      new (m_extra_dump_files + count) dump_file_info ();
+    }
+  else
+    {
+      /* Zero out the already constructed object.  */
+      m_extra_dump_files[count] = dump_file_info ();
     }
 
-  memset (&m_extra_dump_files[count], 0, sizeof (struct dump_file_info));
   m_extra_dump_files[count].suffix = suffix;
   m_extra_dump_files[count].swtch = swtch;
   m_extra_dump_files[count].glob = glob;
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 4724276..a65bdbf 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -1259,9 +1259,9 @@ struct compiler
   const char *cpp_spec;         /* If non-NULL, substitute this spec
 				   for `%C', rather than the usual
 				   cpp_spec.  */
-  const int combinable;          /* If nonzero, compiler can deal with
+  int combinable;               /* If nonzero, compiler can deal with
 				    multiple source files at once (IMA).  */
-  const int needs_preprocessing; /* If nonzero, source files need to
+  int needs_preprocessing;       /* If nonzero, source files need to
 				    be run through a preprocessor.  */
 };
 
diff --git a/gcc/genattrtab.c b/gcc/genattrtab.c
index 3629b5f..51dfe77 100644
--- a/gcc/genattrtab.c
+++ b/gcc/genattrtab.c
@@ -4703,8 +4703,8 @@ gen_insn_reserv (md_rtx_info *info)
   struct insn_reserv *decl = oballoc (struct insn_reserv);
   rtx def = info->def;
 
-  struct attr_desc attr;
-  memset (&attr, 0, sizeof (attr));
+  struct attr_desc attr = { };
+
   attr.name = DEF_ATTR_STRING (XSTR (def, 0));
   attr.loc = info->loc;
 
diff --git a/gcc/hash-table.h b/gcc/hash-table.h
index 0f7e21a..443d16c 100644
--- a/gcc/hash-table.h
+++ b/gcc/hash-table.h
@@ -803,7 +803,10 @@ hash_table<Descriptor, Allocator>::empty_slow ()
       m_size_prime_index = nindex;
     }
   else
-    memset (entries, 0, size * sizeof (value_type));
+    {
+      for ( ; size; ++entries, --size)
+	*entries = value_type ();
+    }
   m_n_deleted = 0;
   m_n_elements = 0;
 }
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 3c9c3f2..c7e3c71 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1471,8 +1471,7 @@ allocate_and_init_ipcp_value (tree source)
 {
   ipcp_value<tree> *val;
 
-  val = ipcp_cst_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_cst_values_pool.allocate ()) ipcp_value<tree>();
   val->value = source;
   return val;
 }
@@ -1486,8 +1485,8 @@ allocate_and_init_ipcp_value (ipa_polymorphic_call_context source)
   ipcp_value<ipa_polymorphic_call_context> *val;
 
   // TODO
-  val = ipcp_poly_ctx_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_poly_ctx_values_pool.allocate ())
+    ipcp_value<ipa_polymorphic_call_context>();
   val->value = source;
   return val;
 }
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index c73ffd7..292f3e2 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -3711,7 +3711,7 @@ void
 ipa_free_edge_args_substructures (struct ipa_edge_args *args)
 {
   vec_free (args->jump_functions);
-  memset (args, 0, sizeof (*args));
+  *args = ipa_edge_args ();
 }
 
 /* Free all ipa_edge structures.  */
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 9a16248..dd4a092 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -6320,7 +6320,11 @@ lower_omp_ordered_clauses (gimple_stmt_iterator *gsi_p, gomp_ordered *ord_stmt,
     return;
 
   wide_int *folded_deps = XALLOCAVEC (wide_int, 2 * len - 1);
-  memset (folded_deps, 0, sizeof (*folded_deps) * (2 * len - 1));
+
+  /* wide_int is not a POD so it must be default-constructed.  */
+  for (unsigned i = 0; i != 2 * len - 1; ++i)
+    new (static_cast<void*>(folded_deps + i)) wide_int ();
+
   tree folded_dep = NULL_TREE;
   /* TRUE if the first dimension's offset is negative.  */
   bool neg_offset_p = false;
diff --git a/gcc/params.h b/gcc/params.h
index b61cff9..8b91660 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -42,7 +42,7 @@ struct param_info
 {
   /* The name used with the `--param <name>=<value>' switch to set this
      value.  */
-  const char *const option;
+  const char *option;
 
   /* The default value.  */
   int default_value;
@@ -54,7 +54,7 @@ struct param_info
   int max_value;
 
   /* A short description of the option.  */
-  const char *const help;
+  const char *help;
 
   /* The optional names corresponding to the values.  */
   const char **value_names;
diff --git a/gcc/testsuite/g++.dg/Wclass-memaccess.C b/gcc/testsuite/g++.dg/Wclass-memaccess.C
new file mode 100644
index 0000000..0c5b0ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wclass-memaccess.C
@@ -0,0 +1,1574 @@
+/* PR c++/80560 - warn on undefined memory operations involving non-trivial
+   types
+   { dg-do compile }
+   { dg-options "-Wclass-memaccess -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern "C"
+{
+void* memcpy (void*, const void*, size_t);
+void* memmove (void*, const void*, size_t);
+void* mempcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+void* realloc (void*, size_t);
+}
+
+/* Ordinary bzcopy and bzero aren't recognized as special.  */
+#define bcopy __builtin_bcopy
+#define bzero __builtin_bzero
+
+void sink (void*);
+
+#define T(fn, arglist) ((fn arglist), sink (p))
+
+#if !defined TEST || TEST == TEST_TRIVIAL
+
+/* Trivial can be manipulated by raw memory functions.  */
+struct Trivial
+{
+  int i; unsigned bf: 1; char *s; char a[4];
+
+  // Template assignment doesn't make the class non-trivial or not
+  // trivially assignable.
+  template <class U>
+  Trivial& operator= (U);
+};
+
+void test (Trivial *p, void *q, int x)
+{
+  const size_t n = x;
+
+  T (bzero, (p, 1));
+  T (bzero, (p, n));
+  T (bzero, (p, sizeof *p));
+  T (bzero, (q, 1));
+  T (bzero, (q, n));
+  T (bzero, (q, sizeof *p));
+
+  T (bcopy, (p, q, 1));
+  T (bcopy, (p, q, n));
+  T (bcopy, (p, q, sizeof *p));
+  T (bcopy, (q, p, 1));
+  T (bcopy, (q, p, n));
+  T (bcopy, (q, p, sizeof *p));
+
+  T (memcpy, (p, q, 1));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (q, p, 1));
+  T (memcpy, (q, p, n));
+  T (memcpy, (q, p, sizeof *p));
+
+  T (memset, (p, 0, 1));
+  T (memset, (p, 0, n));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (q, 0, 1));
+  T (memset, (q, 0, n));
+  T (memset, (q, 0, sizeof *p));
+
+  T (memset, (p, 1, 1));
+  T (memset, (p, 1, n));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (q, 1, 1));
+  T (memset, (q, 1, n));
+  T (memset, (q, 1, sizeof *p));
+
+  T (memset, (p, x, 1));
+  T (memset, (p, x, n));
+  T (memset, (p, x, sizeof *p));
+  T (memset, (q, x, 1));
+  T (memset, (q, x, n));
+  T (memset, (q, x, sizeof *p));
+
+  T (memmove, (p, q, 1));
+  T (memmove, (p, q, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (q, p, 1));
+  T (memmove, (q, p, n));
+  T (memmove, (q, p, sizeof *p));
+
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+
+  T (q = realloc, (q, 1));
+  T (q = realloc, (q, n));
+  T (q = realloc, (q, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DEFAULT
+
+/* HasDefault is trivially copyable but should be initialized by
+   the ctor, not bzero or memset.  */
+struct HasDefault { char a[4]; HasDefault (); };
+
+void test (HasDefault *p, const HasDefault &x,
+	   void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefault is neither trivial nor standard-layout.  The warning
+  // should mention the former since it's more permissive than the latter
+  // and so more informative.
+  T (bzero, (p, sizeof *p));   // { dg-warning "bzero(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefault.; use assignment or value-initialization instead" }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefault.; use assignment or value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefault.; use assignment instead" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefault.; use assignment instead" }
+
+  // Copying from another object of the same type is fine.
+  T (bcopy, (&x, p, sizeof *p));
+  T (bcopy, (&x, p, n));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+
+  // Copying from a void* or character buffer is also fine.
+  T (bcopy, (q, p, sizeof *p));
+  T (bcopy, (q, p, n));
+  T (bcopy, (s, p, sizeof *p));
+  T (bcopy, (s, p, n));
+
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, n));
+
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, q, n));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, s, n));
+
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+
+  // ...but partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefault. leaves 3 bytes unchanged" } */
+  T (memmove, (p, q, 2));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefault. leaves 2 bytes unchanged" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefault. leaves 1 byte unchanged" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .const int." }
+  extern long *ip;
+  T (memcpy, (p, ip, sizeof *p));  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .long." }
+
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning ".void\\* memmove(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .const int." }
+
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning ".void\\* mempcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .const int." }
+
+  // Reallocating is the same as calling memcpy except that only
+  // shrinking reallocation is diagnosed.
+  T (q = realloc, (p, 1));   // { dg-warning "moving an object of non-trivial type .struct HasDefault. and size 4 into a region of size 1" }
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_TEMPLATE_DEFAULT
+
+/* HasTemplateDefault should be initialized by means of the ctor,
+   not zeroed out by bzero/memset.  */
+struct HasTemplateDefault
+{
+  template <class U>
+  HasTemplateDefault (U);
+};
+
+void test (HasTemplateDefault *p, const HasTemplateDefault &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because value initialization is
+  // invalid (the template ctor makes default ctor unavailable).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is okay.
+  T (bcopy, (&x, p, sizeof *p));
+  T (bcopy, (q, p, sizeof *p));
+  T (bcopy, (s, p, sizeof *p));
+  T (bcopy, (ia, p, sizeof *p));    // { dg-warning "bcopy" }
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_COPY
+
+/* HasCopy should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  Since it's non-trivial, it should not be zeroed
+   out by bzero/memset either and should instead use assignment and/or
+   value initialization.  */
+struct HasCopy { int i; HasCopy (const HasCopy&); };
+
+void test (HasCopy *p, const HasCopy &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because value initialization is
+  // invalid (the copy ctor makes no default ctor unavailable).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (bcopy, (&x, p, sizeof *p));    // { dg-warning "bcopy" }
+  T (bcopy, (q, p, sizeof *p));     // { dg-warning "bcopy" }
+  T (bcopy, (s, p, sizeof *p));     // { dg-warning "bcopy" }
+  T (bcopy, (ia, p, sizeof *p));    // { dg-warning "bcopy" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_PRIVATE_COPY
+
+/* HasPrivateCopy cannot be copied using memcpy or memmove.  Since it's
+   non-trivial, it it should not be zeroed out by bzero/memset either
+   and should instead use assignment and/or value initialization.  */
+struct HasPrivateCopy {
+  int i;
+private:
+  HasPrivateCopy (const HasPrivateCopy&);
+};
+
+void test (HasPrivateCopy *p, const HasPrivateCopy &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because value initialization is
+  // invalid (the copy ctor makes no default ctor unavailable).
+  // Verify also that the suggestion offers assignment but not
+  // value initialization (since the lattare is not available).
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of non-trivial type .struct HasPrivateCopy.; use assignment instead" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of non-trivially copyable type .struct HasPrivateCopy.; use copy-assignment instead" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DTOR
+
+/* HasDtor should be initialized using aggregate or memberwise intialization,
+   not bzero or memset.  */
+struct HasDtor { int i; ~HasDtor (); };
+
+void test (HasDtor *p, const HasDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed only because it's difficult not to.
+  // Otherwise, a class that's non-trivial only because it has
+  // a non-trivial dtor can be safely zeroed out (that's what
+  // value-initializing it does).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed simply because
+  // a class with a user-defined dtor is not trivially copyable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DELETED_DTOR
+
+// HasDeletedDtor is trivial so clearing and cpying it is okay.
+// Relocation would bypass the deleted dtor and so it's diagnosed.
+
+struct HasDeletedDtor
+{
+  int i;
+  ~HasDeletedDtor () = delete;
+};
+
+void test (HasDeletedDtor *p, const HasDeletedDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (p, i, sizeof *p));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  // Reallocating is diagnosed.
+  T (q = realloc, (p, 1));          // { dg-warning "moving an object of type .struct HasDeletedDtor. with deleted destructor" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_PRIVATE_DTOR
+
+// Unlike HasDeletedDtor, HasPrivateDtor is okay to zero-out and copy
+// but not relocate because doing so would bypass the deleted dtor..
+
+struct HasPrivateDtor
+{
+  int i;
+private:
+  ~HasPrivateDtor ();
+};
+
+void test (HasPrivateDtor *p, const HasPrivateDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of non-trivial type .struct HasPrivateDtor.; use assignment or value-initialization instead" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of non-trivially copyable type .struct HasPrivateDtor.; use copy-assignment or copy-initialization instead" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is diagnosed.
+  T (q = realloc, (p, 1));          // { dg-warning "moving an object of non-trivially copyable type .struct HasPrivateDtor." }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_COPY_ASSIGN
+
+/* HasCopyAssign should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  */
+struct HasCopyAssign { void operator= (HasCopyAssign&); };
+
+void test (HasCopyAssign *p, const HasCopyAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_MOVE_ASSIGN
+
+/* Like HasCopyAssign, HasMoveAssign should be copied using the copy
+   ctor or assignment, not by memcpy or memmove.  */
+struct HasMoveAssign
+{
+#if __cplusplus > 199711L
+  void operator= (HasMoveAssign&&);
+#else
+  // C++ 98 has no reference references.  Simply repeat the HasCopyAssign
+  // test to avoid having to add a conditional to every dg-warning directive.
+  void operator= (const HasMoveAssign&);
+#endif
+};
+
+void test (HasMoveAssign *p, const HasMoveAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_COPY_HAS_MOVE_ASSIGN
+
+/* TrivialCopyHasMoveAssign should be copied using the copy ctor
+   or assignment, not by memcpy or memmove.  */
+struct TrivialCopyHasMoveAssign
+{
+  typedef TrivialCopyHasMoveAssign Self;
+
+  Self& operator= (const Self&) = default;
+
+#if __cplusplus > 199711L
+  Self& operator= (Self&&);
+#else
+  // C++ 98 has no reference references.  Fake the test by adding
+  // a non-const overload of the assignment operator (which should
+  // have the same effect).
+  Self& operator= (Self&);
+#endif
+};
+
+void test (TrivialCopyHasMoveAssign *p, const TrivialCopyHasMoveAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_MOVE_HAS_COPY_ASSIGN
+
+/* TrivialMoveNontrivialCopyAssign should be copied using the copy ctor
+   or assignment, not by memcpy or memmove.  */
+struct TrivialMoveNontrivialCopyAssign
+{
+  typedef TrivialMoveNontrivialCopyAssign Self;
+
+  Self& operator= (const Self&);
+#if __cplusplus > 199711L
+  // C++ 98 has no reference references.  Fake the test by simply
+  // not declaring the move assignment.
+  Self& operator= (Self&&) = default;
+#endif
+};
+
+void test (TrivialMoveNontrivialCopyAssign *p,
+	   const TrivialMoveNontrivialCopyAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_ASSIGN_REF_OVERLOAD
+
+/* TrivialAssignRefOverload is a trivial type.  */
+struct TrivialAssignRefOverload {
+  int i;
+  typedef TrivialAssignRefOverload Self;
+
+  Self& operator= (Self&) = default;
+  Self& operator= (const Self&) = delete;
+  Self& operator= (volatile Self&) = delete;
+  Self& operator= (const volatile Self&) = delete;
+};
+
+void test (TrivialAssignRefOverload *p, const TrivialAssignRefOverload &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (p, i, sizeof *p));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_ASSIGN_CSTREF_OVERLOAD
+
+/* TrivialAssignCstOverload is a trivial type.  */
+struct TrivialAssignCstRefOverload {
+  int i;
+  typedef TrivialAssignCstRefOverload Self;
+
+  Self& operator= (Self&) = delete;
+  Self& operator= (const Self&) = default;
+  Self& operator= (volatile Self&) = delete;
+  Self& operator= (const volatile Self&) = delete;
+};
+
+void test (TrivialAssignCstRefOverload *p,
+	   const TrivialAssignCstRefOverload &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (p, i, sizeof *p));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_REF_HAS_VOLREF_ASSIGN
+
+struct TrivialRefHasVolRefAssign
+{
+  typedef TrivialRefHasVolRefAssign Self;
+
+  Self& operator= (Self&) = default;
+  Self& operator= (volatile Self&);
+};
+
+void test (TrivialRefHasVolRefAssign *p,
+	   const TrivialRefHasVolRefAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_VOLREF_ASSIGN
+
+struct HasVolRefAssign {
+  int i;
+  typedef HasVolRefAssign Self;
+
+  Self& operator= (volatile Self&);
+};
+
+void test (HasVolRefAssign *p, const HasVolRefAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_VIRTUALS
+
+/* HasVirtuals should only be manipulated by the special member functions
+   and not by bzero, memcpy, or any other raw memory function. Doing
+   otherwse might corrupt the the vtable pointer.  */
+struct HasVirtuals { int i; virtual void foo (); };
+
+void test (HasVirtuals *p, const HasVirtuals &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it corrupts the vtable.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is diagnosed because when used to initialize an object
+  // could incorrectly initialize the vtable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_CONST_DATA
+
+/* HasConstData should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable allowing, raw memory functions to write into it would defeat
+   const-correctness.  */
+struct HasConstData { const char a[4]; };
+
+void test (HasConstData *p, const HasConstData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // The following is ill-formed because HasConstData's cannot
+  // be assigned (the assignment is implicitly deleted).  For
+  // that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object could break const correctness.
+  // Since the default ctor and copy assignment are both deleted,
+  // verify that they're not suggested as a possible alternative.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasConstData. with no trivial copy-assignment \\\[" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "clearing an object of type .struct HasConstData. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of type .struct HasConstData. with no trivial copy-assignment; use copy-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "writing to an object of type .struct HasConstData. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is not diagnosed except in C++ 98 due to a bug.
+  T (q = realloc, (p, 1));          // { dg-warning "moving an object of non-trivially copyable type .struct HasConstData.; use .new. and .delete. instead" "c++98" { target { c++98_only } } }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" "c++98" { target { c++98_only } } }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" "c++98" { target { c++98_only } } }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_REFERENCE
+
+/* HasReference should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable, allowing raw memory functions to write into it could
+   corrupt the reference.  */
+struct HasReference { int &ci; };
+
+void test (HasReference *p, const HasReference &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Similarly to HasConstData, the following is ill-formed because
+  // Hasreference cannot be assigned (the assignment is implicitly
+  // deleted).  For that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object would invalidate the reference.
+  // Since copy-assignment is deleted verify it's not suggested
+  // as an alternative.  (C++ 11 and later only; C++ 98 is broken).
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasReference. with no trivial copy-assignment \\\[" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "clearing an object of type .struct HasReference. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (bzero, (p, n));                // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 0, n));            // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, n));            // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, n));            // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of type .struct HasReference. with no trivial copy-assignment; use copy-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "writing to an object of type .struct HasReference. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is not diagnosed because a type with a reference
+  // is (perhaps surprisingly) trivially copyable.  It is diagnosed
+  // in C++ 98 because of a bug, but it seems like it should be
+  // diagnosed in all modes.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_MEM_DATA_PTR
+
+/* HasMemDataPtr should only be initialized using aggregate initializatoon
+   and not cleared by bzero or written into using memset because its
+   representation is different from ordinary scalars (a null member data
+   pointer is all ones).  It can be copied into using memcpy from an object
+   of the same type or from a character buffer.  */
+struct HasMemDataPtr { int HasMemDataPtr::*p; };
+
+void test (HasMemDataPtr *p, const HasMemDataPtr &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because a null member data pointer has
+  // a representation that's all bits set.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasMemDataPtr. containing a pointer-to-member" }
+  T (bzero, (p, n));                // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 0, n));            // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, n));            // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, n));            // { dg-warning "memset" }
+
+  // Copying is not diagnosed.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, n));
+  T (memcpy, (p, ia, sizeof *p));
+  T (memcpy, (p, ia, n));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PRIVATE_DATA
+
+/* HasSomePrivateData can be initialized using value initialization
+   and should not be written to using memset with a non-zero argument.
+   Doing otherwise would break encapsulation.  */
+struct HasSomePrivateData { char a[2]; private: char b[2]; };
+
+void test (HasSomePrivateData *p, const HasSomePrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PROTECTED_DATA
+
+/* Similarly to HasSomePrivateData, HasSomeProtectedData can be
+   initialized using value initialization and should not be written
+   to using memset with a non-zero argument.  Doing otherwise would
+   break encapsulation.  */
+struct HasSomeProtectedData { char a[2]; protected: char b[2]; };
+
+void test (HasSomeProtectedData *p, const HasSomeProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PRIVATE_DATA
+
+/* Similarly to HasSomePrivateData, HasAllPrivateData should only be
+   initialized using value initializatoon and should not be written
+   to using memset with non-zero argument.  They are tested separately
+   because unlike the former classes, these are standard layout.  */
+struct HasAllPrivateData { private: char a[4]; };
+
+void test (HasAllPrivateData *p, const HasAllPrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PROTECTED_DATA
+
+/* Similarly to HasSomeProtectedData, HasAllProtectedData should only
+   be initialized using value initializatoon and should not be written
+   to using memset with non-zero argument.  They are tested separately
+   because unlike the former classes, these are standard layout.  */
+struct HasAllProtectedData { protected: char a[4]; };
+
+void test (HasAllProtectedData *p, const HasAllProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_DEFAULT_CTOR_PRIVATE_ASSIGN
+
+/* Used to verify suggested alternatives.  */
+struct HasDefaultPrivateAssign
+{
+  char a[4];
+  HasDefaultPrivateAssign ();
+private:
+  void operator= (HasDefaultPrivateAssign&);
+};
+
+void test (HasDefaultPrivateAssign *p, const HasDefaultPrivateAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefaultPrivateAssign isn't trivial or assignable.  Verify
+  // that the alternative suggested in the warning is to use copy or
+  // default but not assignment.
+  T (bzero, (p, sizeof *p));   // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 98" { target { c++98_only } } .-1 }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment; use value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment" }
+
+  // Copying from another object of the same type is diagnosed because
+  // the copy assignment is inaccessible.  Verify that the suggested
+  // alternative is not copy assignment (C++ 98 is busted).
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment" "c++98" { target c++98_only } .-1 }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+
+  // Similarly for copying from a void* or character buffer.
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment" "c++98" { target c++98_only } ,-1 }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, q, n));           // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, n));           // { dg-warning "memmove" }
+
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, n));           // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, n));           // { dg-warning "mempcpy" }
+
+  // Same for partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment" } */
+  T (memmove, (p, q, 2));   // { dg-warning "memmove" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "mempcpy" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning "writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment." }
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning "memmove" }
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_DEFAULT_CTOR_DELETED_ASSIGN
+
+/* Used to verify suggested alternatives.  */
+struct HasDefaultDeletedAssign
+{
+  char a[4];
+  HasDefaultDeletedAssign ();
+private:
+  void operator= (HasDefaultDeletedAssign&);
+};
+
+void test (HasDefaultDeletedAssign *p, const HasDefaultDeletedAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefaultDeletedAssign isn't trivial or assignable.  Verify
+  // that the alternative suggested in the warning is to use copy or
+  // default but not assignment.
+  T (bzero, (p, sizeof *p));   // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 98" { target { c++98_only } } .-1 }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment; use value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment" }
+
+  // Copying from another object of the same type is diagnosed because
+  // the copy assignment is inaccessible.  Verify that the suggested
+  // alternative is not copy assignment (C++ 98 is busted).
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment" "c++98" { target c++98_only } .-1 }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+
+  // Similarly for copying from a void* or character buffer.
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment" "c++98" { target c++98_only } ,-1 }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, q, n));           // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, n));           // { dg-warning "memmove" }
+
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, n));           // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, n));           // { dg-warning "mempcpy" }
+
+  // Same for partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment" } */
+  T (memmove, (p, q, 2));   // { dg-warning "memmove" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "mempcpy" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning "writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment." }
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning "memmove" }
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_EXPRESSION
+
+void test_expr (int i)
+{
+  struct TestClass { TestClass () { } };
+  TestClass a, b;
+
+  static void *p;
+
+  T (bzero, (i < 0 ? &a : &b, 1));  // { dg-warning "bzero" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_CTOR
+
+void test_ctor ()
+{
+#undef T
+#define T(fn, arglist) (fn arglist, sink (this))
+
+  static void *p;
+
+  struct TestBase
+  {
+    TestBase ()
+    {
+      /* A ctor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+
+    ~TestBase ()
+    {
+      /* A dtor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestBaseVtable
+  {
+    TestBaseVtable ()
+    {
+      /* A ctor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    ~TestBaseVtable ()
+    {
+      /* A dtor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    virtual void foo ();
+  };
+
+  struct TestDerived: HasDefault
+  {
+    TestDerived ()
+    {
+      /* A derived class ctor is treated as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestDerivedDtor: HasDefault
+  {
+    ~TestDerivedDtor ()
+    {
+      /* A derived class dtor is treated as an ordinary function though
+	 it probably shouldn't be unless the base dtor is trivial.  But
+	 it doesn't seem worth the trouble.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+  };
+}
+
+#endif
+
+// { dg-prune-output "defaulted and deleted functions" }
diff --git a/gcc/tree-switch-conversion.c b/gcc/tree-switch-conversion.c
index 66db20f..72927bf 100644
--- a/gcc/tree-switch-conversion.c
+++ b/gcc/tree-switch-conversion.c
@@ -268,7 +268,7 @@ static void
 emit_case_bit_tests (gswitch *swtch, tree index_expr,
 		     tree minval, tree range, tree maxval)
 {
-  struct case_bit_test test[MAX_CASE_BIT_TESTS];
+  struct case_bit_test test[MAX_CASE_BIT_TESTS] = { };
   unsigned int i, j, k;
   unsigned int count;
 
@@ -293,8 +293,6 @@ emit_case_bit_tests (gswitch *swtch, tree index_expr,
   int prec = TYPE_PRECISION (word_type_node);
   wide_int wone = wi::one (prec);
 
-  memset (&test, 0, sizeof (test));
-
   /* Get the edge for the default case.  */
   tmp = gimple_switch_default_label (swtch);
   default_bb = label_to_block (CASE_LABEL (tmp));
diff --git a/gcc/vec.h b/gcc/vec.h
index 755a1f8..cbdd439 100644
--- a/gcc/vec.h
+++ b/gcc/vec.h
@@ -407,6 +407,26 @@ struct GTY((user)) vec
 {
 };
 
+/* Default-construct N elements in DST.  */
+
+template <typename T>
+inline void
+vec_default_construct (T *dst, unsigned n)
+{
+  for ( ; n; ++dst, --n)
+    ::new (static_cast<void*>(dst)) T ();
+}
+
+/* Copy-construct N elements in DST from *SRC.  */
+
+template <typename T>
+inline void
+vec_copy_construct (T *dst, const T *src, unsigned n)
+{
+  for ( ; n; ++dst, ++src, --n)
+    ::new (static_cast<void*>(dst)) T (*src);
+}
+
 /* Type to provide NULL values for vec<T, A, L>.  This is used to
    provide nil initializers for vec instances.  Since vec must be
    a POD, we cannot have proper ctor/dtor for it.  To initialize
@@ -612,7 +632,7 @@ vec_safe_grow_cleared (vec<T, A, vl_embed> *&v, unsigned len CXX_MEM_STAT_INFO)
 {
   unsigned oldlen = vec_safe_length (v);
   vec_safe_grow (v, len PASS_MEM_STAT);
-  memset (&(v->address ()[oldlen]), 0, sizeof (T) * (len - oldlen));
+  vec_default_construct (v->address () + oldlen, len - oldlen);
 }
 
 
@@ -818,7 +838,7 @@ vec<T, A, vl_embed>::copy (ALONE_MEM_STAT_DECL) const
     {
       vec_alloc (new_vec, len PASS_MEM_STAT);
       new_vec->embedded_init (len, len);
-      memcpy (new_vec->address (), m_vecdata, sizeof (T) * len);
+      vec_copy_construct (new_vec->address (), m_vecdata, len);
     }
   return new_vec;
 }
@@ -835,7 +855,7 @@ vec<T, A, vl_embed>::splice (const vec<T, A, vl_embed> &src)
   if (len)
     {
       gcc_checking_assert (space (len));
-      memcpy (address () + length (), src.address (), len * sizeof (T));
+      vec_copy_construct (end (), src.address (), len);
       m_vecpfx.m_num += len;
     }
 }
@@ -1089,13 +1109,12 @@ inline void
 vec<T, A, vl_embed>::quick_grow_cleared (unsigned len)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   quick_grow (len);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
-
 /* Garbage collection support for vec<T, A, vl_embed>.  */
 
 template<typename T>
@@ -1454,7 +1473,7 @@ vec<T, va_heap, vl_ptr>::reserve (unsigned nelems, bool exact MEM_STAT_DECL)
   va_heap::reserve (m_vec, nelems, exact PASS_MEM_STAT);
   if (handle_auto_vec)
     {
-      memcpy (m_vec->address (), oldvec->address (), sizeof (T) * oldsize);
+      vec_copy_construct (m_vec->address (), oldvec->address (), oldsize);
       m_vec->m_vecpfx.m_num = oldsize;
     }
 
@@ -1616,10 +1635,10 @@ inline void
 vec<T, va_heap, vl_ptr>::safe_grow_cleared (unsigned len MEM_STAT_DECL)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   safe_grow (len PASS_MEM_STAT);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
 
diff --git a/libcpp/line-map.c b/libcpp/line-map.c
index c4b7cb2..56b7913 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -62,7 +62,8 @@ extern unsigned num_macro_tokens_counter;
 
 line_maps::~line_maps ()
 {
-  htab_delete (location_adhoc_data_map.htab);
+  if (location_adhoc_data_map.htab)
+    htab_delete (location_adhoc_data_map.htab);
 }
 
 /* Hash function for location_adhoc_data hashtable.  */
@@ -347,7 +348,7 @@ void
 linemap_init (struct line_maps *set,
 	      source_location builtin_location)
 {
-  memset (set, 0, sizeof (struct line_maps));
+  *set = line_maps ();
   set->highest_location = RESERVED_LOCATION_COUNT - 1;
   set->highest_line = RESERVED_LOCATION_COUNT - 1;
   set->location_adhoc_data_map.htab =
diff --git a/libitm/beginend.cc b/libitm/beginend.cc
index d04f3e9..c6550a3 100644
--- a/libitm/beginend.cc
+++ b/libitm/beginend.cc
@@ -431,7 +431,7 @@ GTM::gtm_transaction_cp::save(gtm_thread* tx)
   // Save everything that we might have to restore on restarts or aborts.
   jb = tx->jb;
   undolog_size = tx->undolog.size();
-  memcpy(&alloc_actions, &tx->alloc_actions, sizeof(alloc_actions));
+  alloc_actions = tx->alloc_actions;
   user_actions_size = tx->user_actions.size();
   id = tx->id;
   prop = tx->prop;
@@ -449,7 +449,7 @@ GTM::gtm_transaction_cp::commit(gtm_thread* tx)
   // commits of nested transactions. Allocation actions must be committed
   // before committing the snapshot.
   tx->jb = jb;
-  memcpy(&tx->alloc_actions, &alloc_actions, sizeof(alloc_actions));
+  tx->alloc_actions = alloc_actions;
   tx->id = id;
   tx->prop = prop;
 }
@@ -485,7 +485,7 @@ GTM::gtm_thread::rollback (gtm_transaction_cp *cp, bool aborting)
       prop = cp->prop;
       if (cp->disp != abi_disp())
 	set_abi_disp(cp->disp);
-      memcpy(&alloc_actions, &cp->alloc_actions, sizeof(alloc_actions));
+      alloc_actions = cp->alloc_actions;
       nesting = cp->nesting;
     }
   else
diff --git a/libitm/method-ml.cc b/libitm/method-ml.cc
index fcae334..b857bff 100644
--- a/libitm/method-ml.cc
+++ b/libitm/method-ml.cc
@@ -138,7 +138,11 @@ struct ml_mg : public method_group
     // This store is only executed while holding the serial lock, so relaxed
     // memory order is sufficient here.  Same holds for the memset.
     time.store(0, memory_order_relaxed);
-    memset(orecs, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
+    // The memset below isn't strictly kosher because it bypasses
+    // the non-trivial assignment operator defined by std::atomic.  Using
+    // a local void* is enough to prevent GCC from warning for this.
+    void *p = orecs;
+    memset(p, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
   }
 };
 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-06 22:24                                         ` Martin Sebor
@ 2017-06-08  1:09                                           ` Jason Merrill
  2017-06-08 20:25                                             ` Martin Sebor
  0 siblings, 1 reply; 46+ messages in thread
From: Jason Merrill @ 2017-06-08  1:09 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Pedro Alves, Gcc Patch List

On 06/06/2017 03:24 PM, Martin Sebor wrote:
> +  /* Iterate over copy and move assignment overloads.  */
> +
> +  for (ovl_iterator oi (fns); oi; ++oi)
> +    {
> +      tree f = *oi;
> +
> +      bool accessible = !access || !(TREE_PRIVATE (f) || TREE_PROTECTED (f));
> +
> +      /* Skip template assignment operators and deleted functions.  */
> +      if (TREE_CODE (f) != FUNCTION_DECL || DECL_DELETED_FN (f))
> +	continue;
> +
> +      if (accessible)
> +	*hasassign = true;
> +
> +      if (!accessible || !trivial_fn_p (f))
> +	all_trivial = false;
> +
> +      /* Break early when both properties have been determined.  */
> +      if (*hasassign && !all_trivial)
> +	break;
> +    }

This is iterating over all assignment operators, not just copy/move.  I 
think you want copy_fn_p, here and in has_trivial_copy_p.

It seems redundant to check access here and also check is 
is_trivially_xible, which takes access into account.  And if we're going 
to check access at all, it should consider the access of the current 
scope, not just whether the function is private or protected.

Jason

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-08  1:09                                           ` Jason Merrill
@ 2017-06-08 20:25                                             ` Martin Sebor
  2017-06-12 21:36                                               ` Jason Merrill
  0 siblings, 1 reply; 46+ messages in thread
From: Martin Sebor @ 2017-06-08 20:25 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Pedro Alves, Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 2092 bytes --]

On 06/07/2017 07:09 PM, Jason Merrill wrote:
> On 06/06/2017 03:24 PM, Martin Sebor wrote:
>> +  /* Iterate over copy and move assignment overloads.  */
>> +
>> +  for (ovl_iterator oi (fns); oi; ++oi)
>> +    {
>> +      tree f = *oi;
>> +
>> +      bool accessible = !access || !(TREE_PRIVATE (f) ||
>> TREE_PROTECTED (f));
>> +
>> +      /* Skip template assignment operators and deleted functions.  */
>> +      if (TREE_CODE (f) != FUNCTION_DECL || DECL_DELETED_FN (f))
>> +    continue;
>> +
>> +      if (accessible)
>> +    *hasassign = true;
>> +
>> +      if (!accessible || !trivial_fn_p (f))
>> +    all_trivial = false;
>> +
>> +      /* Break early when both properties have been determined.  */
>> +      if (*hasassign && !all_trivial)
>> +    break;
>> +    }
>
> This is iterating over all assignment operators, not just copy/move.  I
> think you want copy_fn_p, here and in has_trivial_copy_p.

I assumed the cp_ in cp_assignment_operator_id meant copy.  Clearly,
even a 1,500 line test wasn't enough to catch this mistake.  I've
added more test cases.

FWIW, giving the macro a more descriptive name and/or adding
a comment above it explaining that's any assignment would help.

I also expected to see a similar thing for ctor but the closest
I could find was the three xxx_ctor_identifiers (or, conversely,
I expected to see an assignment_identifier).  I picked one based
on some of its uses, but I'm sure that it's the best choice
(ctor_identifier seems to work too).

> It seems redundant to check access here and also check is
> is_trivially_xible, which takes access into account.

The access check is done separately so that the right suggestion
can be made.  If the class is (non-trivially) copy assignable but
none of the copy assignment operators is public then the warning
doesn't suggest to use the copy assignment if it's not accessible.

> And if we're going
> to check access at all, it should consider the access of the current
> scope, not just whether the function is private or protected.

Okay, I've added this.  Please see the attached update.

Martin

[-- Attachment #2: gcc-80560.diff --]
[-- Type: text/x-patch, Size: 97627 bytes --]

PR c++/80560 - warn on undefined memory operations involving non-trivial types

gcc/c-family/ChangeLog:

	PR c++/80560
	* c.opt (-Wclass-memaccess): New option.

gcc/cp/ChangeLog:

	PR c++/80560
	* call.c (first_non_public_field, maybe_warn_class_memaccess): New
	functions.
	(has_trivial_copy_assign_p, has_trivial_copy_p): Ditto.
	(build_cxx_call): Call maybe_warn_class_memaccess.

gcc/ChangeLog:

	PR c++/80560
	* dumpfile.c (dump_register): Avoid calling memset to initialize
	a class with a default ctor.
	* gcc.c (struct compiler): Remove const qualification.
	* genattrtab.c (gen_insn_reserv): Replace memset with initialization.
	* hash-table.h: Ditto.
	* ipa-cp.c (allocate_and_init_ipcp_value): Replace memset with
	  assignment.
	* ipa-prop.c (ipa_free_edge_args_substructures): Ditto.
	* omp-low.c (lower_omp_ordered_clauses): Replace memset with
	default ctor.
	* params.h (struct param_info): Make struct members non-const.
	* tree-switch-conversion.c (emit_case_bit_tests): Replace memset
	with default initialization.
	* vec.h (vec_copy_construct, vec_default_construct): New helper
	functions.
	(vec<T>::copy, vec<T>::splice, vec<T>::reserve): Replace memcpy
	with vec_copy_construct.
	(vect<T>::quick_grow_cleared): Replace memset with default ctor.
	(vect<T>::vec_safe_grow_cleared, vec_safe_grow_cleared): Same.
	* doc/invoke.texi (-Wclass-memaccess): Document.

libcpp/ChangeLog:

	PR c++/80560
	* line-map.c (line_maps::~line_maps): Avoid calling htab_delete
	with a null pointer.
	(linemap_init): Avoid calling memset on an object of a non-trivial
	type.

libitm/ChangeLog:

	PR c++/80560
	* beginend.cc (GTM::gtm_thread::rollback): Avoid calling memset
	on an object of a non-trivial type.
	(GTM::gtm_transaction_cp::commit): Use assignment instead of memcpy
	to copy an object.
	* method-ml.cc (orec_iterator::reinit): Avoid -Wclass-memaccess.

gcc/testsuite/ChangeLog:

	PR c++/80560
	* g++.dg/Wclass-memaccess.C: New test.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 37bb236..363d104 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -804,6 +804,10 @@ Wnon-template-friend
 C++ ObjC++ Var(warn_nontemplate_friend) Init(1) Warning
 Warn when non-templatized friend functions are declared within a template.
 
+Wclass-memaccess
+C++ ObjC++ Var(warn_class_memaccess) Warning LangEnabledBy(C++ ObjC++, Wall)
+Warn for unsafe raw memory writes to objects of class types.
+
 Wnon-virtual-dtor
 C++ ObjC++ Var(warn_nonvdtor) Warning LangEnabledBy(C++ ObjC++,Weffc++)
 Warn about non-virtual destructors.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 51260f0..6e0951e 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8146,6 +8146,402 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
   return call;
 }
 
+/* Return the DECL of the first non-public data member of class TYPE
+   or null if none can be found.  */
+
+static tree
+first_non_public_field (tree type)
+{
+  if (!CLASS_TYPE_P (type))
+    return NULL_TREE;
+
+  for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+	continue;
+      if (TREE_STATIC (field))
+	continue;
+      if (TREE_PRIVATE (field) || TREE_PROTECTED (field))
+	return field;
+    }
+
+  int i = 0;
+
+  for (tree base_binfo, binfo = TYPE_BINFO (type);
+       BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+    {
+      tree base = TREE_TYPE (base_binfo);
+
+      if (tree field = first_non_public_field (base))
+	return field;
+    }
+
+  return NULL_TREE;
+}
+
+/* Return true if all copy and move assignment operator overloads for
+   class TYPE are trivial and at least one of them is not deleted and,
+   when ACCESS is set, accessible.  Return false otherwise.  Set
+   HASASSIGN to true when the TYPE has a (not necessarily trivial)
+   copy or move assignment.  */
+
+static bool
+has_trivial_copy_assign_p (tree type, bool access, bool *hasassign)
+{
+  tree fns = cp_assignment_operator_id (NOP_EXPR);
+  fns = lookup_fnfields_slot (type, fns);
+
+  bool all_trivial = true;
+
+  /* Iterate over overloads of the assignment operator, checking
+     accessible copy assignments for triviality.  */
+
+  for (ovl_iterator oi (fns); oi; ++oi)
+    {
+      tree f = *oi;
+
+      /* Skip operators that aren't copy assignments.  */
+      if (!copy_fn_p (f))
+	continue;
+
+      bool accessible = (!access || !(TREE_PRIVATE (f) || TREE_PROTECTED (f))
+			 || accessible_p (TYPE_BINFO (type), f, true));
+
+      /* Skip template assignment operators and deleted functions.  */
+      if (TREE_CODE (f) != FUNCTION_DECL || DECL_DELETED_FN (f))
+	continue;
+
+      if (accessible)
+	*hasassign = true;
+
+      if (!accessible || !trivial_fn_p (f))
+	all_trivial = false;
+
+      /* Break early when both properties have been determined.  */
+      if (*hasassign && !all_trivial)
+	break;
+    }
+
+  /* Return true if they're all trivial and one of the expressions
+     TYPE() = TYPE() or TYPE() = (TYPE&)() is valid.  */
+  tree ref = cp_build_reference_type (type, false);
+  return (all_trivial
+	  && (is_trivially_xible (MODIFY_EXPR, type, type)
+	      || is_trivially_xible (MODIFY_EXPR, type, ref)));
+}
+
+/* Return true if all copy and move ctor overloads for class TYPE are
+   trivial and at least one of them is not deleted and, when ACCESS is
+   set, accessible.  Return false otherwise.  Set each element of HASCTOR[]
+   to true when the TYPE has a (not necessarily trivial) default and copy
+   (or move) ctor, respectively.  */
+
+static bool
+has_trivial_copy_p (tree type, bool access, bool hasctor[2])
+{
+  tree fns = lookup_fnfields_slot (type, complete_ctor_identifier);
+
+  bool all_trivial = true;
+
+  for (ovl_iterator oi (fns); oi; ++oi)
+    {
+      tree f = *oi;
+
+      /* Skip template constructors.  */
+      if (TREE_CODE (f) != FUNCTION_DECL)
+	continue;
+
+      bool cpy_or_move_ctor_p;
+      if (TREE_CHAIN (DECL_ARGUMENTS (f)))
+	{
+	  /* Skip constructors that aren't copy or move ctors.  */
+	  if (!copy_fn_p (f))
+	    continue;
+
+	  cpy_or_move_ctor_p = true;
+	}
+      else
+	{
+	  /* Constructor is a default ctor.  */
+	  cpy_or_move_ctor_p = false;
+	}
+
+      if (DECL_DELETED_FN (f))
+	continue;
+
+      bool accessible = (!access || !(TREE_PRIVATE (f) || TREE_PROTECTED (f))
+			 || accessible_p (TYPE_BINFO (type), f, true));
+
+      if (accessible)
+	hasctor[cpy_or_move_ctor_p] = true;
+
+      if (cpy_or_move_ctor_p && (!accessible || !trivial_fn_p (f)))
+	all_trivial = false;
+
+      /* Break early when both properties have been determined.  */
+      if (hasctor[0] && hasctor[1] && !all_trivial)
+	break;
+    }
+
+  return all_trivial;
+}
+
+/* issue a warning on a call to the built-in function FNDECL if it is
+   a raw memory write whose destination is not an object of (something
+   like) trivial or standard layout type with a non-deleted assignment
+   and copy ctor.  Detects const correctness violations, corrupting
+   references, virtual table pointers, and bypassing non-trivial
+   assignments.  */
+
+static void
+maybe_warn_class_memaccess (location_t loc, tree fndecl, tree *args)
+{
+  /* Except for bcopy where it's second, the destination pointer is
+     the first argument for all functions handled here.  Compute
+     the index of the destination and source arguments.  */
+  unsigned dstidx = DECL_FUNCTION_CODE (fndecl) == BUILT_IN_BCOPY;
+  unsigned srcidx = !dstidx;
+
+  tree dest = args[dstidx];
+  if (!dest || !TREE_TYPE (dest) || !POINTER_TYPE_P (TREE_TYPE (dest)))
+    return;
+
+  STRIP_NOPS (dest);
+
+  tree srctype = NULL_TREE;
+
+  /* Determine the type of the pointed-to object and whether it's
+     a complete class type.  */
+  tree desttype = TREE_TYPE (TREE_TYPE (dest));
+
+  if (!desttype || !COMPLETE_TYPE_P (desttype) || !CLASS_TYPE_P (desttype))
+    return;
+
+  /* Check to see if the raw memory call is made by a ctor or dtor
+     with this as the destination argument for the destination type.
+     If so, be more permissive.  */
+  if (current_function_decl
+      && (DECL_CONSTRUCTOR_P (current_function_decl)
+	  || DECL_DESTRUCTOR_P (current_function_decl))
+      && is_this_parameter (dest))
+    {
+      tree ctx = DECL_CONTEXT (current_function_decl);
+      bool special = same_type_ignoring_top_level_qualifiers_p (ctx, desttype);
+
+      tree binfo = TYPE_BINFO (ctx);
+
+      /* A ctor and dtor for a class with no bases and no virtual functions
+	 can do whatever they want.  Bail early with no further checking.  */
+      if (special && !BINFO_VTABLE (binfo) && !BINFO_N_BASE_BINFOS (binfo))
+	return;
+    }
+
+  /* True if the class is trivial.  */
+  bool trivial = trivial_type_p (desttype);
+
+  /* Set to true if DESTYPE has an accessible copy assignment.  */
+  bool hasassign = false;
+  /* True if all of the class' overloaded copy assignment operators
+     are all trivial (and not deleted) and at least one of them is
+     accessible.  */
+  bool trivassign = has_trivial_copy_assign_p (desttype, true, &hasassign);
+
+  /* Set to true if DESTTYPE has an accessible defqault and copy ctor,
+     respectively.  */
+  bool hasctors[2] = { false, false };
+
+  /* True if all of the class' overloaded copy constructors are all
+     trivial (and not deleted) and at least one of them is accessible.  */
+  bool trivcopy = has_trivial_copy_p (desttype, true, hasctors);
+
+  /* Set FLD to the first private/protected member of the class.  */
+  tree fld = trivial ? first_non_public_field (desttype) : NULL_TREE;
+
+  /* The warning format string.  */
+  const char *warnfmt = NULL;
+  /* A suggested alternative to offer instead of the raw memory call.
+     Empty string when none can be come up with.  */
+  const char *suggest = "";
+  bool warned = false;
+
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    case BUILT_IN_MEMSET:
+      if (!integer_zerop (args[1]))
+	{
+	  /* Diagnose setting non-copy-assignable or non-trivial types,
+	     or types with a private member, to (potentially) non-zero
+	     bytes.  Since the value of the bytes being written is unknown,
+	     suggest using assignment instead (if one exists).  Also warn
+	     for writes into objects for which zero-initialization doesn't
+	     mean all bits clear (pointer-to-member data, where null is all
+	     bits set).  Since the value being written is (most likely)
+	     non-zero, simply suggest assignment (but not copy assignment).  */
+	  suggest = "; use assignment instead";
+	  if (!trivassign)
+	    warnfmt = G_("%qD writing to an object of type %#qT with "
+			 "no trivial copy-assignment");
+	  else if (!trivial)
+	    warnfmt = G_("%qD writing to an object of non-trivial type %#qT%s");
+	  else if (fld)
+	    {
+	      const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	      warned = warning_at (loc, OPT_Wclass_memaccess,
+				   "%qD writing to an object of type %#qT with "
+				   "%qs member %qD",
+				   fndecl, desttype, access, fld);
+	    }
+	  else if (!zero_init_p (desttype))
+	    warnfmt = G_("%qD writing to an object of type %#qT containing "
+			 "a pointer to data member%s");
+
+	  break;
+	}
+      /* Fall through.  */
+
+    case BUILT_IN_BZERO:
+      /* Similarly to the above, diagnose clearing non-trivial or non-
+	 standard layout objects, or objects of types with no assignmenmt.
+	 Since the value being written is known to be zero, suggest either
+	 copy assignment, copy ctor, or default ctor as an alternative,
+	 depending on what's available.  */
+
+      if (hasassign && hasctors[0])
+	suggest = G_("; use assignment or value-initialization instead");
+      else if (hasassign)
+	suggest = G_("; use assignment instead");
+      else if (hasctors[0])
+	suggest = G_("; use value-initialization instead");
+
+      if (!trivassign)
+	warnfmt = G_("%qD clearing an object of type %#qT with "
+		     "no trivial copy-assignment%s");
+      else if (!trivial)
+	warnfmt =  G_("%qD clearing an object of non-trivial type %#qT%s");
+      else if (!zero_init_p (desttype))
+	warnfmt = G_("%qD clearing an object of type %#qT containing "
+		     "a pointer-to-member%s");
+      break;
+
+    case BUILT_IN_BCOPY:
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMPCPY:
+      /* Determine the type of the source object.  */
+      srctype = STRIP_NOPS (args[srcidx]);
+      srctype = TREE_TYPE (TREE_TYPE (srctype));
+
+      /* Since it's impossible to determine wheter the byte copy is
+	 being used in place of assignment to an existing object or
+	 as a substitute for initialization, assume it's the former.
+	 Determine the best alternative to use instead depending on
+	 what's not deleted.  */
+      if (hasassign && hasctors[1])
+	suggest = G_("; use copy-assignment or copy-initialization instead");
+      else if (hasassign)
+	suggest = G_("; use copy-assignment instead");
+      else if (hasctors[1])
+	suggest = G_("; use copy-initialization instead");
+
+      if (!trivassign)
+	warnfmt = G_("%qD writing to an object of type %#qT with no trivial "
+		     "copy-assignment%s");
+      else if (!trivially_copyable_p (desttype))
+	warnfmt = G_("%qD writing to an object of non-trivially copyable "
+		     "type %#qT%s");
+      else if (!trivcopy)
+	warnfmt = G_("%qD writing to an object with a deleted copy constructor");
+
+      else if (!trivial
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  /* Warn when copying into a non-trivial object from an object
+	     of a different type other than void or char.  */
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD copying an object of non-trivial type "
+			       "%#qT from an array of %#qT",
+			       fndecl, desttype, srctype);
+	}
+      else if (fld
+	       && !VOID_TYPE_P (srctype)
+	       && !char_type_p (TYPE_MAIN_VARIANT (srctype))
+	       && !same_type_ignoring_top_level_qualifiers_p (desttype,
+							      srctype))
+	{
+	  const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD copying an object of type %#qT with "
+			       "%qs member %qD from an array of %#qT; use "
+			       "assignment or copy-initialization instead",
+			       fndecl, desttype, access, fld, srctype);
+	}
+      else if (!trivial && TREE_CODE (args[2]) == INTEGER_CST)
+	{
+	  /* Finally, warn on partial copies.  */
+	  unsigned HOST_WIDE_INT typesize
+	    = tree_to_uhwi (TYPE_SIZE_UNIT (desttype));
+	  if (unsigned HOST_WIDE_INT partial
+	      = tree_to_uhwi (args[2]) % typesize)
+	    warned = warning_at (loc, OPT_Wclass_memaccess,
+				 (typesize - partial > 1
+				  ? G_("%qD writing to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "bytes unchanged")
+				  : G_("%qD writing to an object of "
+				       "a non-trivial type %#qT leaves %wu "
+				       "byte unchanged")),
+				 fndecl, desttype, typesize - partial);
+	}
+      break;
+
+    case BUILT_IN_REALLOC:
+
+      if (!trivially_copyable_p (desttype))
+	warnfmt = G_("%qD moving an object of non-trivially copyable type "
+		     "%#qT; use %<new%> and %<delete%> instead");
+      else if (!trivcopy)
+	warnfmt = G_("%qD moving an object of type %#qT with deleted copy "
+		     "constructor; use %<new%> and %<delete%> instead");
+      else if (!get_dtor (desttype, tf_none))
+	warnfmt = G_("%qD moving an object of type %#qT with deleted "
+		     "destructor");
+      else if (!trivial
+	       && TREE_CODE (args[1]) == INTEGER_CST
+	       && tree_int_cst_lt (args[1], TYPE_SIZE_UNIT (desttype)))
+	{
+	  /* Finally, warn on reallocation into insufficient space.  */
+	  warned = warning_at (loc, OPT_Wclass_memaccess,
+			       "%qD moving an object of non-trivial type "
+			       "%#qT and size %E into a region of size %E",
+			       fndecl, desttype, TYPE_SIZE_UNIT (desttype),
+			       args[1]);
+	}
+      break;
+
+    default:
+      return;
+    }
+
+  if (!warned && !warnfmt)
+    return;
+
+  if (warnfmt)
+    {
+      if (suggest)
+	warned = warning_at (loc, OPT_Wclass_memaccess,
+			     warnfmt, fndecl, desttype, suggest);
+      else
+	warned = warning_at (loc, OPT_Wclass_memaccess,
+			     warnfmt, fndecl, desttype);
+    }
+
+  if (warned)
+    inform (location_of (desttype), "%#qT declared here", desttype);
+}
+
 /* Build and return a call to FN, using NARGS arguments in ARGARRAY.
    This function performs no overload resolution, conversion, or other
    high-level operations.  */
@@ -8178,6 +8574,10 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
       if (!check_builtin_function_arguments (EXPR_LOCATION (fn), vNULL, fndecl,
 					     nargs, argarray))
 	return error_mark_node;
+
+      /* Warn if the built-in writes to an object of a non-trivial type.  */
+      if (nargs)
+	maybe_warn_class_memaccess (loc, fndecl, argarray);
     }
 
     /* If it is a built-in array notation function, then the return type of
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 4a83a3e..475673c 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -215,7 +215,8 @@ in the following sections.
 -Wabi=@var{n}  -Wabi-tag  -Wconversion-null  -Wctor-dtor-privacy @gol
 -Wdelete-non-virtual-dtor  -Wliteral-suffix  -Wmultiple-inheritance @gol
 -Wnamespaces  -Wnarrowing @gol
--Wnoexcept  -Wnoexcept-type  -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
+-Wnoexcept  -Wnoexcept-type  -Wclass-memaccess @gol
+-Wnon-virtual-dtor  -Wreorder  -Wregister @gol
 -Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
 -Wno-non-template-friend  -Wold-style-cast @gol
 -Woverloaded-virtual  -Wno-pmf-conversions @gol
@@ -2920,6 +2921,23 @@ void g() noexcept;
 void h() @{ f(g); @} // in C++14 calls f<void(*)()>, in C++1z calls f<void(*)()noexcept>
 @end smallexample
 
+@item -Wclass-memaccess @r{(C++ and Objective-C++ only)}
+@opindex Wclass-memaccess
+Warn when the destination of a call to a raw memory function such as
+@code{memset} or @code{memcpy} is an object of class type writing into which
+might bypass the class non-trivial or deleted constructor or copy assignment,
+violate const-correctness or encapsulation, or corrupt the virtual table.
+Modifying the representation of such objects may violate invariants maintained
+by member functions of the class.  For example, the call to @code{memset}
+below is undefined becase it modifies a non-trivial class object and is,
+therefore, diagnosed.  The safe way to either initialize or clear the storage
+of objects of such types is by using the appropriate constructor or assignment
+operator, if one is available.
+@smallexample
+std::string str = "abc";
+memset (&str, 0, 3);
+@end smallexample
+The @option{-Wclass-memaccess} option is enabled by @option{-Wall}.
 
 @item -Wnon-virtual-dtor @r{(C++ and Objective-C++ only)}
 @opindex Wnon-virtual-dtor
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index b8bda3c..b336d5b 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -185,9 +185,16 @@ dump_register (const char *suffix, const char *swtch, const char *glob,
       m_extra_dump_files = XRESIZEVEC (struct dump_file_info,
 				       m_extra_dump_files,
 				       m_extra_dump_files_alloced);
+
+      /* Construct a new object in the space allocated above.  */
+      new (m_extra_dump_files + count) dump_file_info ();
+    }
+  else
+    {
+      /* Zero out the already constructed object.  */
+      m_extra_dump_files[count] = dump_file_info ();
     }
 
-  memset (&m_extra_dump_files[count], 0, sizeof (struct dump_file_info));
   m_extra_dump_files[count].suffix = suffix;
   m_extra_dump_files[count].swtch = swtch;
   m_extra_dump_files[count].glob = glob;
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 4724276..a65bdbf 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -1259,9 +1259,9 @@ struct compiler
   const char *cpp_spec;         /* If non-NULL, substitute this spec
 				   for `%C', rather than the usual
 				   cpp_spec.  */
-  const int combinable;          /* If nonzero, compiler can deal with
+  int combinable;               /* If nonzero, compiler can deal with
 				    multiple source files at once (IMA).  */
-  const int needs_preprocessing; /* If nonzero, source files need to
+  int needs_preprocessing;       /* If nonzero, source files need to
 				    be run through a preprocessor.  */
 };
 
diff --git a/gcc/genattrtab.c b/gcc/genattrtab.c
index 3629b5f..51dfe77 100644
--- a/gcc/genattrtab.c
+++ b/gcc/genattrtab.c
@@ -4703,8 +4703,8 @@ gen_insn_reserv (md_rtx_info *info)
   struct insn_reserv *decl = oballoc (struct insn_reserv);
   rtx def = info->def;
 
-  struct attr_desc attr;
-  memset (&attr, 0, sizeof (attr));
+  struct attr_desc attr = { };
+
   attr.name = DEF_ATTR_STRING (XSTR (def, 0));
   attr.loc = info->loc;
 
diff --git a/gcc/hash-table.h b/gcc/hash-table.h
index 0f7e21a..443d16c 100644
--- a/gcc/hash-table.h
+++ b/gcc/hash-table.h
@@ -803,7 +803,10 @@ hash_table<Descriptor, Allocator>::empty_slow ()
       m_size_prime_index = nindex;
     }
   else
-    memset (entries, 0, size * sizeof (value_type));
+    {
+      for ( ; size; ++entries, --size)
+	*entries = value_type ();
+    }
   m_n_deleted = 0;
   m_n_elements = 0;
 }
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 3c9c3f2..c7e3c71 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1471,8 +1471,7 @@ allocate_and_init_ipcp_value (tree source)
 {
   ipcp_value<tree> *val;
 
-  val = ipcp_cst_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_cst_values_pool.allocate ()) ipcp_value<tree>();
   val->value = source;
   return val;
 }
@@ -1486,8 +1485,8 @@ allocate_and_init_ipcp_value (ipa_polymorphic_call_context source)
   ipcp_value<ipa_polymorphic_call_context> *val;
 
   // TODO
-  val = ipcp_poly_ctx_values_pool.allocate ();
-  memset (val, 0, sizeof (*val));
+  val = new (ipcp_poly_ctx_values_pool.allocate ())
+    ipcp_value<ipa_polymorphic_call_context>();
   val->value = source;
   return val;
 }
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index c73ffd7..292f3e2 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -3711,7 +3711,7 @@ void
 ipa_free_edge_args_substructures (struct ipa_edge_args *args)
 {
   vec_free (args->jump_functions);
-  memset (args, 0, sizeof (*args));
+  *args = ipa_edge_args ();
 }
 
 /* Free all ipa_edge structures.  */
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 9a16248..dd4a092 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -6320,7 +6320,11 @@ lower_omp_ordered_clauses (gimple_stmt_iterator *gsi_p, gomp_ordered *ord_stmt,
     return;
 
   wide_int *folded_deps = XALLOCAVEC (wide_int, 2 * len - 1);
-  memset (folded_deps, 0, sizeof (*folded_deps) * (2 * len - 1));
+
+  /* wide_int is not a POD so it must be default-constructed.  */
+  for (unsigned i = 0; i != 2 * len - 1; ++i)
+    new (static_cast<void*>(folded_deps + i)) wide_int ();
+
   tree folded_dep = NULL_TREE;
   /* TRUE if the first dimension's offset is negative.  */
   bool neg_offset_p = false;
diff --git a/gcc/params.h b/gcc/params.h
index b61cff9..8b91660 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -42,7 +42,7 @@ struct param_info
 {
   /* The name used with the `--param <name>=<value>' switch to set this
      value.  */
-  const char *const option;
+  const char *option;
 
   /* The default value.  */
   int default_value;
@@ -54,7 +54,7 @@ struct param_info
   int max_value;
 
   /* A short description of the option.  */
-  const char *const help;
+  const char *help;
 
   /* The optional names corresponding to the values.  */
   const char **value_names;
diff --git a/gcc/testsuite/g++.dg/Wclass-memaccess.C b/gcc/testsuite/g++.dg/Wclass-memaccess.C
new file mode 100644
index 0000000..d7ba44f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wclass-memaccess.C
@@ -0,0 +1,1648 @@
+/* PR c++/80560 - warn on undefined memory operations involving non-trivial
+   types
+   { dg-do compile }
+   { dg-options "-Wclass-memaccess -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern "C"
+{
+void* memcpy (void*, const void*, size_t);
+void* memmove (void*, const void*, size_t);
+void* mempcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+void* realloc (void*, size_t);
+}
+
+/* Ordinary bzcopy and bzero aren't recognized as special.  */
+#define bcopy __builtin_bcopy
+#define bzero __builtin_bzero
+
+void sink (void*);
+
+#define T(fn, arglist) ((fn arglist), sink (p))
+
+#if !defined TEST || TEST == TEST_TRIVIAL
+
+/* Trivial can be manipulated by raw memory functions.  */
+struct Trivial
+{
+  int i; unsigned bf: 1; char *s; char a[4];
+
+  // Non-copy assignment doesn't make the class non-trivial or not
+  // trivially assignable.
+  Trivial& operator= (int);
+
+  // Likewise, template assignment doesn't make the class non-trivial
+  // or not trivially assignable.
+  template <class U>
+  Trivial& operator= (U);
+};
+
+void test (Trivial *p, void *q, int x)
+{
+  const size_t n = x;
+
+  T (bzero, (p, 1));
+  T (bzero, (p, n));
+  T (bzero, (p, sizeof *p));
+  T (bzero, (q, 1));
+  T (bzero, (q, n));
+  T (bzero, (q, sizeof *p));
+
+  T (bcopy, (p, q, 1));
+  T (bcopy, (p, q, n));
+  T (bcopy, (p, q, sizeof *p));
+  T (bcopy, (q, p, 1));
+  T (bcopy, (q, p, n));
+  T (bcopy, (q, p, sizeof *p));
+
+  T (memcpy, (p, q, 1));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (q, p, 1));
+  T (memcpy, (q, p, n));
+  T (memcpy, (q, p, sizeof *p));
+
+  T (memset, (p, 0, 1));
+  T (memset, (p, 0, n));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (q, 0, 1));
+  T (memset, (q, 0, n));
+  T (memset, (q, 0, sizeof *p));
+
+  T (memset, (p, 1, 1));
+  T (memset, (p, 1, n));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (q, 1, 1));
+  T (memset, (q, 1, n));
+  T (memset, (q, 1, sizeof *p));
+
+  T (memset, (p, x, 1));
+  T (memset, (p, x, n));
+  T (memset, (p, x, sizeof *p));
+  T (memset, (q, x, 1));
+  T (memset, (q, x, n));
+  T (memset, (q, x, sizeof *p));
+
+  T (memmove, (p, q, 1));
+  T (memmove, (p, q, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (q, p, 1));
+  T (memmove, (q, p, n));
+  T (memmove, (q, p, sizeof *p));
+
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+
+  T (q = realloc, (q, 1));
+  T (q = realloc, (q, n));
+  T (q = realloc, (q, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_ACCESS
+
+/* TrivialAccess can be manipulated by raw memory functions in contexts
+   that have access to the trivial specia functions.  */
+struct TrivialAccess
+{
+  int i; unsigned bf: 1; char *s; char a[4];
+
+private:
+  TrivialAccess () = default;
+  TrivialAccess (const TrivialAccess&) = default;
+protected:
+  TrivialAccess& operator= (const TrivialAccess&) = default;
+
+  void test_member (const TrivialAccess*, int);
+
+  friend void test_friend (TrivialAccess*, const TrivialAccess*, int);
+};
+
+void test (TrivialAccess *p, const TrivialAccess *q, int i)
+{
+  void *pv;
+  (void)&pv;
+
+  /* Verify that a warning is issued when the copy ctor and copy
+     assignment are inaccessible.  */
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (bcopy, (q, p, sizeof *p));     // { dg-warning "bcopy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (pv = realloc, (p, sizeof *p)); // { dg-warning "realloc" }
+}
+
+void test_friend (TrivialAccess *p, const TrivialAccess *q, int i)
+{
+  void *pv;
+  (void)&pv;
+
+  /* Verify that no warning is issued when the otherwise inaccessible
+     copy ctor and copy assignment can be accessed within the current
+     context.  */
+  T (bzero, (p, sizeof *p));
+  T (bcopy, (q, p, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memset, (p, i, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (pv = realloc, (p, sizeof *p));
+}
+
+void TrivialAccess::test_member (const TrivialAccess *q, int i)
+{
+  void *pv;
+  (void)&pv;
+
+  TrivialAccess *p = this;
+
+  /* Verify that no warning is issued when the otherwise inaccessible
+     copy ctor and copy assignment can be accessed within the current
+     context.  */
+  T (bzero, (p, sizeof *p));
+  T (bcopy, (q, p, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memset, (p, i, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (pv = realloc, (p, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DEFAULT
+
+/* HasDefault is trivially copyable but should be initialized by
+   the ctor, not bzero or memset.  */
+struct HasDefault { char a[4]; HasDefault (); };
+
+void test (HasDefault *p, const HasDefault &x,
+	   void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefault is neither trivial nor standard-layout.  The warning
+  // should mention the former since it's more permissive than the latter
+  // and so more informative.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefault.; use assignment or value-initialization instead" }
+
+  T (memset, (p, 0, sizeof *p));    // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefault.; use assignment or value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));    // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefault.; use assignment instead" }
+
+  T (memset, (p, i, sizeof *p));    // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefault.; use assignment instead" }
+
+  // Copying from another object of the same type is fine.
+  T (bcopy, (&x, p, sizeof *p));
+  T (bcopy, (&x, p, n));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+
+  // Copying from a void* or character buffer is also fine.
+  T (bcopy, (q, p, sizeof *p));
+  T (bcopy, (q, p, n));
+  T (bcopy, (s, p, sizeof *p));
+  T (bcopy, (s, p, n));
+
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, n));
+
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, q, n));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, s, n));
+
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+
+  // ...but partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefault. leaves 3 bytes unchanged" } */
+  T (memmove, (p, q, 2));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefault. leaves 2 bytes unchanged" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "writing to an object of a non-trivial type .struct HasDefault. leaves 1 byte unchanged" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .const int." }
+  extern long *ip;
+  T (memcpy, (p, ip, sizeof *p));  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .long." }
+
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning ".void\\* memmove(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .const int." }
+
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning ".void\\* mempcpy(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefault. from an array of .const int." }
+
+  // Reallocating is the same as calling memcpy except that only
+  // shrinking reallocation is diagnosed.
+  T (q = realloc, (p, 1));   // { dg-warning "moving an object of non-trivial type .struct HasDefault. and size 4 into a region of size 1" }
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_TEMPLATE_DEFAULT
+
+/* HasTemplateDefault should be initialized by means of the ctor,
+   not zeroed out by bzero/memset.  */
+struct HasTemplateDefault
+{
+  template <class U>
+  HasTemplateDefault (U);
+};
+
+void test (HasTemplateDefault *p, const HasTemplateDefault &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because value initialization is
+  // invalid (the template ctor makes default ctor unavailable).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is okay.
+  T (bcopy, (&x, p, sizeof *p));
+  T (bcopy, (q, p, sizeof *p));
+  T (bcopy, (s, p, sizeof *p));
+  T (bcopy, (ia, p, sizeof *p));    // { dg-warning "bcopy" }
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_COPY
+
+/* HasCopy should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  Since it's non-trivial, it should not be zeroed
+   out by bzero/memset either and should instead use assignment and/or
+   value initialization.  */
+struct HasCopy { int i; HasCopy (const HasCopy&); };
+
+void test (HasCopy *p, const HasCopy &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because value initialization is
+  // invalid (the copy ctor makes no default ctor unavailable).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (bcopy, (&x, p, sizeof *p));    // { dg-warning "bcopy" }
+  T (bcopy, (q, p, sizeof *p));     // { dg-warning "bcopy" }
+  T (bcopy, (s, p, sizeof *p));     // { dg-warning "bcopy" }
+  T (bcopy, (ia, p, sizeof *p));    // { dg-warning "bcopy" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_PRIVATE_COPY
+
+/* HasPrivateCopy cannot be copied using memcpy or memmove.  Since it's
+   non-trivial, it it should not be zeroed out by bzero/memset either
+   and should instead use assignment and/or value initialization.  */
+struct HasPrivateCopy {
+  int i;
+private:
+  HasPrivateCopy (const HasPrivateCopy&);
+};
+
+void test (HasPrivateCopy *p, const HasPrivateCopy &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because value initialization is
+  // invalid (the copy ctor makes no default ctor unavailable).
+  // Verify also that the suggestion offers assignment but not
+  // value initialization (since the lattare is not available).
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of non-trivial type .struct HasPrivateCopy.; use assignment instead" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of non-trivially copyable type .struct HasPrivateCopy.; use copy-assignment instead" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DTOR
+
+/* HasDtor should be initialized using aggregate or memberwise intialization,
+   not bzero or memset.  */
+struct HasDtor { int i; ~HasDtor (); };
+
+void test (HasDtor *p, const HasDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed only because it's difficult not to.
+  // Otherwise, a class that's non-trivial only because it has
+  // a non-trivial dtor can be safely zeroed out (that's what
+  // value-initializing it does).
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed simply because
+  // a class with a user-defined dtor is not trivially copyable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_DELETED_DTOR
+
+// HasDeletedDtor is trivial so clearing and cpying it is okay.
+// Relocation would bypass the deleted dtor and so it's diagnosed.
+
+struct HasDeletedDtor
+{
+  int i;
+  ~HasDeletedDtor () = delete;
+};
+
+void test (HasDeletedDtor *p, const HasDeletedDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (p, i, sizeof *p));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  // Reallocating is diagnosed.
+  T (q = realloc, (p, 1));          // { dg-warning "moving an object of type .struct HasDeletedDtor. with deleted destructor" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_PRIVATE_DTOR
+
+// Unlike HasDeletedDtor, HasPrivateDtor is okay to zero-out and copy
+// but not relocate because doing so would bypass the deleted dtor..
+
+struct HasPrivateDtor
+{
+  int i;
+private:
+  ~HasPrivateDtor ();
+};
+
+void test (HasPrivateDtor *p, const HasPrivateDtor &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of non-trivial type .struct HasPrivateDtor.; use assignment or value-initialization instead" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of non-trivially copyable type .struct HasPrivateDtor.; use copy-assignment or copy-initialization instead" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is diagnosed.
+  T (q = realloc, (p, 1));          // { dg-warning "moving an object of non-trivially copyable type .struct HasPrivateDtor." }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_COPY_ASSIGN
+
+/* HasCopyAssign should be copied using the copy ctor or assignment, not
+   by memcpy or memmove.  */
+struct HasCopyAssign { void operator= (HasCopyAssign&); };
+
+void test (HasCopyAssign *p, const HasCopyAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_MOVE_ASSIGN
+
+/* Like HasCopyAssign, HasMoveAssign should be copied using the copy
+   ctor or assignment, not by memcpy or memmove.  */
+struct HasMoveAssign
+{
+#if __cplusplus > 199711L
+  void operator= (HasMoveAssign&&);
+#else
+  // C++ 98 has no reference references.  Simply repeat the HasCopyAssign
+  // test to avoid having to add a conditional to every dg-warning directive.
+  void operator= (const HasMoveAssign&);
+#endif
+};
+
+void test (HasMoveAssign *p, const HasMoveAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_COPY_HAS_MOVE_ASSIGN
+
+/* TrivialCopyHasMoveAssign should be copied using the copy ctor
+   or assignment, not by memcpy or memmove.  */
+struct TrivialCopyHasMoveAssign
+{
+  typedef TrivialCopyHasMoveAssign Self;
+
+  Self& operator= (const Self&) = default;
+
+#if __cplusplus > 199711L
+  Self& operator= (Self&&);
+#else
+  // C++ 98 has no reference references.  Fake the test by adding
+  // a non-const overload of the assignment operator (which should
+  // have the same effect).
+  Self& operator= (Self&);
+#endif
+};
+
+void test (TrivialCopyHasMoveAssign *p, const TrivialCopyHasMoveAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_MOVE_HAS_COPY_ASSIGN
+
+/* TrivialMoveNontrivialCopyAssign should be copied using the copy ctor
+   or assignment, not by memcpy or memmove.  */
+struct TrivialMoveNontrivialCopyAssign
+{
+  typedef TrivialMoveNontrivialCopyAssign Self;
+
+  Self& operator= (const Self&);
+#if __cplusplus > 199711L
+  // C++ 98 has no reference references.  Fake the test by simply
+  // not declaring the move assignment.
+  Self& operator= (Self&&) = default;
+#endif
+};
+
+void test (TrivialMoveNontrivialCopyAssign *p,
+	   const TrivialMoveNontrivialCopyAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_ASSIGN_REF_OVERLOAD
+
+/* TrivialAssignRefOverload is a trivial type.  */
+struct TrivialAssignRefOverload {
+  int i;
+  typedef TrivialAssignRefOverload Self;
+
+  Self& operator= (Self&) = default;
+  Self& operator= (const Self&) = delete;
+  Self& operator= (volatile Self&) = delete;
+  Self& operator= (const volatile Self&) = delete;
+};
+
+void test (TrivialAssignRefOverload *p, const TrivialAssignRefOverload &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (p, i, sizeof *p));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_ASSIGN_CSTREF_OVERLOAD
+
+/* TrivialAssignCstOverload is a trivial type.  */
+struct TrivialAssignCstRefOverload {
+  int i;
+  typedef TrivialAssignCstRefOverload Self;
+
+  Self& operator= (Self&) = delete;
+  Self& operator= (const Self&) = default;
+  Self& operator= (volatile Self&) = delete;
+  Self& operator= (const volatile Self&) = delete;
+};
+
+void test (TrivialAssignCstRefOverload *p,
+	   const TrivialAssignCstRefOverload &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  T (memset, (p, 1, sizeof *p));
+  T (memset, (p, i, sizeof *p));
+
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_TRIVIAL_REF_HAS_VOLREF_ASSIGN
+
+struct TrivialRefHasVolRefAssign
+{
+  typedef TrivialRefHasVolRefAssign Self;
+
+  Self& operator= (Self&) = default;
+  Self& operator= (volatile Self&);
+};
+
+void test (TrivialRefHasVolRefAssign *p,
+	   const TrivialRefHasVolRefAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_VOLREF_ASSIGN
+
+struct HasVolRefAssign {
+  int i;
+  typedef HasVolRefAssign Self;
+
+  Self& operator= (volatile Self&);
+};
+
+void test (HasVolRefAssign *p, const HasVolRefAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it when used with an existing
+  // (already constructed) object in lieu of assigning a new value
+  // to it would bypass the user-defined assignment.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying from an object of any type is diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_VIRTUALS
+
+/* HasVirtuals should only be manipulated by the special member functions
+   and not by bzero, memcpy, or any other raw memory function. Doing
+   otherwse might corrupt the the vtable pointer.  */
+struct HasVirtuals { int i; virtual void foo (); };
+
+void test (HasVirtuals *p, const HasVirtuals &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because it corrupts the vtable.
+  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is diagnosed because when used to initialize an object
+  // could incorrectly initialize the vtable.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_CONST_DATA
+
+/* HasConstData should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable allowing, raw memory functions to write into it would defeat
+   const-correctness.  */
+struct HasConstData { const char a[4]; };
+
+void test (HasConstData *p, const HasConstData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // The following is ill-formed because HasConstData's cannot
+  // be assigned (the assignment is implicitly deleted).  For
+  // that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object could break const correctness.
+  // Since the default ctor and copy assignment are both deleted,
+  // verify that they're not suggested as a possible alternative.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasConstData. with no trivial copy-assignment \\\[" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "clearing an object of type .struct HasConstData. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of type .struct HasConstData. with no trivial copy-assignment; use copy-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "writing to an object of type .struct HasConstData. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is not diagnosed except in C++ 98 due to a bug.
+  T (q = realloc, (p, 1));          // { dg-warning "moving an object of non-trivially copyable type .struct HasConstData.; use .new. and .delete. instead" "c++98" { target { c++98_only } } }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" "c++98" { target { c++98_only } } }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" "c++98" { target { c++98_only } } }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_REFERENCE
+
+/* HasReference should only be initialized using aggregate initializatoon
+   and not cleared by bzero, or copied into using memcpy.  Since it's not
+   assignable, allowing raw memory functions to write into it could
+   corrupt the reference.  */
+struct HasReference { int &ci; };
+
+void test (HasReference *p, const HasReference &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Similarly to HasConstData, the following is ill-formed because
+  // Hasreference cannot be assigned (the assignment is implicitly
+  // deleted).  For that reason all raw memory operations are diagnosed.
+  // *p = x;
+
+  // Zeroing out is diagnosed because if used with an existing
+  // (already initialized) object would invalidate the reference.
+  // Since copy-assignment is deleted verify it's not suggested
+  // as an alternative.  (C++ 11 and later only; C++ 98 is broken).
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasReference. with no trivial copy-assignment \\\[" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "clearing an object of type .struct HasReference. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (bzero, (p, n));                // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 0, n));            // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, n));            // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, n));            // { dg-warning "memset" }
+
+  // Copying is also diagnosed.
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning "writing to an object of type .struct HasReference. with no trivial copy-assignment; use copy-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "writing to an object of type .struct HasReference. with no trivial copy-assignment" "c++ 98" { target { c++98_only } } .-1 }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  T (memmove, (p, &x, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+
+  T (mempcpy, (p, &x, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+
+  // Reallocating is not diagnosed because a type with a reference
+  // is (perhaps surprisingly) trivially copyable.  It is diagnosed
+  // in C++ 98 because of a bug, but it seems like it should be
+  // diagnosed in all modes.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_MEM_DATA_PTR
+
+/* HasMemDataPtr should only be initialized using aggregate initializatoon
+   and not cleared by bzero or written into using memset because its
+   representation is different from ordinary scalars (a null member data
+   pointer is all ones).  It can be copied into using memcpy from an object
+   of the same type or from a character buffer.  */
+struct HasMemDataPtr { int HasMemDataPtr::*p; };
+
+void test (HasMemDataPtr *p, const HasMemDataPtr &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is diagnosed because a null member data pointer has
+  // a representation that's all bits set.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of type .struct HasMemDataPtr. containing a pointer-to-member" }
+  T (bzero, (p, n));                // { dg-warning "bzero" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 0, n));            // { dg-warning "memset" }
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, 1, n));            // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, n));            // { dg-warning "memset" }
+
+  // Copying is not diagnosed.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, q, n));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, s, n));
+  T (memcpy, (p, ia, sizeof *p));
+  T (memcpy, (p, ia, n));
+
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));
+
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, ia, sizeof *p));
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PRIVATE_DATA
+
+/* HasSomePrivateData can be initialized using value initialization
+   and should not be written to using memset with a non-zero argument.
+   Doing otherwise would break encapsulation.  */
+struct HasSomePrivateData { char a[2]; private: char b[2]; };
+
+void test (HasSomePrivateData *p, const HasSomePrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_SOME_PROTECTED_DATA
+
+/* Similarly to HasSomePrivateData, HasSomeProtectedData can be
+   initialized using value initialization and should not be written
+   to using memset with a non-zero argument.  Doing otherwise would
+   break encapsulation.  */
+struct HasSomeProtectedData { char a[2]; protected: char b[2]; };
+
+void test (HasSomeProtectedData *p, const HasSomeProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PRIVATE_DATA
+
+/* Similarly to HasSomePrivateData, HasAllPrivateData should only be
+   initialized using value initializatoon and should not be written
+   to using memset with non-zero argument.  They are tested separately
+   because unlike the former classes, these are standard layout.  */
+struct HasAllPrivateData { private: char a[4]; };
+
+void test (HasAllPrivateData *p, const HasAllPrivateData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_HAS_ALL_PROTECTED_DATA
+
+/* Similarly to HasSomeProtectedData, HasAllProtectedData should only
+   be initialized using value initializatoon and should not be written
+   to using memset with non-zero argument.  They are tested separately
+   because unlike the former classes, these are standard layout.  */
+struct HasAllProtectedData { protected: char a[4]; };
+
+void test (HasAllProtectedData *p, const HasAllProtectedData &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // Zeroing out is not diagnosed because it's equivalent to value
+  // initialization.
+  T (bzero, (p, sizeof *p));
+  T (memset, (p, 0, sizeof *p));
+  // Calling memset with a (possibly) non-zero argument is diagnosed
+  // because it breaks encapsulation.
+  T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
+  T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
+
+  // Calling memcpy to copy from an object of the same type or from
+  // a character or void buffer is not diagnosed because that's what
+  // copy construction and copy assignment do.
+  T (memcpy, (p, &x, sizeof *p));
+  T (memcpy, (p, &x, n));
+  T (memcpy, (p, q, sizeof *p));
+  T (memcpy, (p, s, sizeof *p));
+  T (memcpy, (p, ia, sizeof *p));   // { dg-warning "memcpy" }
+  T (memcpy, (p, ia, n));           // { dg-warning "memcpy" }
+
+  // Same as memcpy above.
+  T (memmove, (p, &x, sizeof *p));
+  T (memmove, (p, &x, n));
+  T (memmove, (p, q, sizeof *p));
+  T (memmove, (p, s, sizeof *p));
+  T (memmove, (p, ia, sizeof *p));  // { dg-warning "memmove" }
+  T (memmove, (p, ia, n));          // { dg-warning "memmove" }
+
+  // Same as memcpy above.
+  T (mempcpy, (p, &x, sizeof *p));
+  T (mempcpy, (p, &x, n));
+  T (mempcpy, (p, q, sizeof *p));
+  T (mempcpy, (p, q, n));
+  T (mempcpy, (p, s, sizeof *p));
+  T (mempcpy, (p, s, n));
+  T (mempcpy, (p, ia, sizeof *p));  // { dg-warning "mempcpy" }
+  T (mempcpy, (p, ia, n));          // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy except that partial
+  // copies are not diagnosed.
+  T (q = realloc, (p, 1));
+  T (q = realloc, (p, n));
+  T (q = realloc, (p, sizeof *p));
+  T (q = realloc, (p, sizeof *p + 1));
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_DEFAULT_CTOR_PRIVATE_ASSIGN
+
+/* Used to verify suggested alternatives.  */
+struct HasDefaultPrivateAssign
+{
+  char a[4];
+  HasDefaultPrivateAssign ();
+private:
+  void operator= (HasDefaultPrivateAssign&);
+};
+
+void test (HasDefaultPrivateAssign *p, const HasDefaultPrivateAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefaultPrivateAssign isn't trivial or assignable.  Verify
+  // that the alternative suggested in the warning is to use copy or
+  // default but not assignment.
+  T (bzero, (p, sizeof *p));   // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 98" { target { c++98_only } } .-1 }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment; use value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment" }
+
+  // Copying from another object of the same type is diagnosed because
+  // the copy assignment is inaccessible.  Verify that the suggested
+  // alternative is not copy assignment (C++ 98 is busted).
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment" "c++98" { target c++98_only } .-1 }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+
+  // Similarly for copying from a void* or character buffer.
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultPrivateAssign. with no trivial copy-assignment" "c++98" { target c++98_only } ,-1 }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, q, n));           // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, n));           // { dg-warning "memmove" }
+
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, n));           // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, n));           // { dg-warning "mempcpy" }
+
+  // Same for partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment" } */
+  T (memmove, (p, q, 2));   // { dg-warning "memmove" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "mempcpy" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning "writing to an object of type .struct HasDefaultPrivateAssign. with (deleted|no trivial) copy-assignment." }
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning "memmove" }
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_DEFAULT_CTOR_DELETED_ASSIGN
+
+/* Used to verify suggested alternatives.  */
+struct HasDefaultDeletedAssign
+{
+  char a[4];
+  HasDefaultDeletedAssign ();
+private:
+  void operator= (HasDefaultDeletedAssign&);
+};
+
+void test (HasDefaultDeletedAssign *p, const HasDefaultDeletedAssign &x,
+	   const void *q, const unsigned char *s, const int ia[])
+{
+  const int i = *ia;
+  const size_t n = *ia;
+
+  // HasDefaultDeletedAssign isn't trivial or assignable.  Verify
+  // that the alternative suggested in the warning is to use copy or
+  // default but not assignment.
+  T (bzero, (p, sizeof *p));   // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 11 and later" { target { c++11 } } }
+  // { dg-warning "bzero(\[^\n\r\]*). clearing an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use value-initialization instead" "c++ 98" { target { c++98_only } } .-1 }
+
+  T (memset, (p, 0, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment; use value-initialization instead" }
+
+  T (memset, (p, 1, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment" }
+
+  T (memset, (p, i, sizeof *p));   // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment" }
+
+  // Copying from another object of the same type is diagnosed because
+  // the copy assignment is inaccessible.  Verify that the suggested
+  // alternative is not copy assignment (C++ 98 is busted).
+  T (memcpy, (p, &x, sizeof *p));   // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment" "c++98" { target c++98_only } .-1 }
+  T (memcpy, (p, &x, n));           // { dg-warning "memcpy" }
+
+  // Similarly for copying from a void* or character buffer.
+  T (memcpy, (p, q, sizeof *p));    // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment; use copy-initialization instead" "c++11 and later" { target c++11 } }
+  // { dg-warning ".void\\* memcpy(\[^\n\r\]*). writing to an object of type .struct HasDefaultDeletedAssign. with no trivial copy-assignment" "c++98" { target c++98_only } ,-1 }
+  T (memcpy, (p, q, n));            // { dg-warning "memcpy" }
+  T (memcpy, (p, s, sizeof *p));    // { dg-warning "memcpy" }
+  T (memcpy, (p, s, n));            // { dg-warning "memcpy" }
+
+  T (memmove, (p, q, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, q, n));           // { dg-warning "memmove" }
+  T (memmove, (p, s, sizeof *p));   // { dg-warning "memmove" }
+  T (memmove, (p, s, n));           // { dg-warning "memmove" }
+
+  T (mempcpy, (p, q, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, q, n));           // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, sizeof *p));   // { dg-warning "mempcpy" }
+  T (mempcpy, (p, s, n));           // { dg-warning "mempcpy" }
+
+  // Same for partial copies are diagnosed.
+  T (memcpy, (p, &x, 1));   // { dg-warning "writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment" } */
+  T (memmove, (p, q, 2));   // { dg-warning "memmove" } */
+  T (mempcpy, (p, q, 3));   // { dg-warning "mempcpy" } */
+
+  // Otherwise, copying from an object of an unrelated type is diagnosed.
+  T (memcpy, (p, ia, sizeof *p));  // { dg-warning "writing to an object of type .struct HasDefaultDeletedAssign. with (deleted|no trivial) copy-assignment." }
+  T (memmove, (p, ia, sizeof *p)); // { dg-warning "memmove" }
+  T (mempcpy, (p, ia, sizeof *p)); // { dg-warning "mempcpy" }
+
+  // Reallocating is the same as calling memcpy.
+  T (q = realloc, (p, 1));          // { dg-warning "realloc" }
+  T (q = realloc, (p, n));          // { dg-warning "realloc" }
+  T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_EXPRESSION
+
+void test_expr (int i)
+{
+  struct TestClass { TestClass () { } };
+  TestClass a, b;
+
+  static void *p;
+
+  T (bzero, (i < 0 ? &a : &b, 1));  // { dg-warning "bzero" }
+}
+
+#endif
+
+#if !defined TEST || TEST == TEST_CTOR
+
+void test_ctor ()
+{
+#undef T
+#define T(fn, arglist) (fn arglist, sink (this))
+
+  static void *p;
+
+  struct TestBase
+  {
+    TestBase ()
+    {
+      /* A ctor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+
+    ~TestBase ()
+    {
+      /* A dtor of a base class with no virtual function can do whatever
+	 it wants.  */
+      T (bzero, (this, sizeof *this));
+      T (memset, (this, 0, sizeof *this));
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestBaseVtable
+  {
+    TestBaseVtable ()
+    {
+      /* A ctor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    ~TestBaseVtable ()
+    {
+      /* A dtor of a base class with virtual function is treated
+	 as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+
+    virtual void foo ();
+  };
+
+  struct TestDerived: HasDefault
+  {
+    TestDerived ()
+    {
+      /* A derived class ctor is treated as an ordinary function.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));
+      T (memmove, (this, p, sizeof *this));
+      T (mempcpy, (this, p, sizeof *this));
+    }
+  };
+
+  struct TestDerivedDtor: HasDefault
+  {
+    ~TestDerivedDtor ()
+    {
+      /* A derived class dtor is treated as an ordinary function though
+	 it probably shouldn't be unless the base dtor is trivial.  But
+	 it doesn't seem worth the trouble.  */
+      T (bzero, (this, sizeof *this));      // { dg-warning "bzero" }
+      T (memset, (this, 0, sizeof *this));  // { dg-warning "memset" }
+      T (memcpy, (this, p, sizeof *this));  // { dg-warning "memcpy" }
+      T (memmove, (this, p, sizeof *this)); // { dg-warning "memmove" }
+      T (mempcpy, (this, p, sizeof *this)); // { dg-warning "mempcpy" }
+    }
+  };
+}
+
+#endif
+
+// { dg-prune-output "defaulted and deleted functions" }
diff --git a/gcc/tree-switch-conversion.c b/gcc/tree-switch-conversion.c
index 66db20f..72927bf 100644
--- a/gcc/tree-switch-conversion.c
+++ b/gcc/tree-switch-conversion.c
@@ -268,7 +268,7 @@ static void
 emit_case_bit_tests (gswitch *swtch, tree index_expr,
 		     tree minval, tree range, tree maxval)
 {
-  struct case_bit_test test[MAX_CASE_BIT_TESTS];
+  struct case_bit_test test[MAX_CASE_BIT_TESTS] = { };
   unsigned int i, j, k;
   unsigned int count;
 
@@ -293,8 +293,6 @@ emit_case_bit_tests (gswitch *swtch, tree index_expr,
   int prec = TYPE_PRECISION (word_type_node);
   wide_int wone = wi::one (prec);
 
-  memset (&test, 0, sizeof (test));
-
   /* Get the edge for the default case.  */
   tmp = gimple_switch_default_label (swtch);
   default_bb = label_to_block (CASE_LABEL (tmp));
diff --git a/gcc/vec.h b/gcc/vec.h
index 755a1f8..cbdd439 100644
--- a/gcc/vec.h
+++ b/gcc/vec.h
@@ -407,6 +407,26 @@ struct GTY((user)) vec
 {
 };
 
+/* Default-construct N elements in DST.  */
+
+template <typename T>
+inline void
+vec_default_construct (T *dst, unsigned n)
+{
+  for ( ; n; ++dst, --n)
+    ::new (static_cast<void*>(dst)) T ();
+}
+
+/* Copy-construct N elements in DST from *SRC.  */
+
+template <typename T>
+inline void
+vec_copy_construct (T *dst, const T *src, unsigned n)
+{
+  for ( ; n; ++dst, ++src, --n)
+    ::new (static_cast<void*>(dst)) T (*src);
+}
+
 /* Type to provide NULL values for vec<T, A, L>.  This is used to
    provide nil initializers for vec instances.  Since vec must be
    a POD, we cannot have proper ctor/dtor for it.  To initialize
@@ -612,7 +632,7 @@ vec_safe_grow_cleared (vec<T, A, vl_embed> *&v, unsigned len CXX_MEM_STAT_INFO)
 {
   unsigned oldlen = vec_safe_length (v);
   vec_safe_grow (v, len PASS_MEM_STAT);
-  memset (&(v->address ()[oldlen]), 0, sizeof (T) * (len - oldlen));
+  vec_default_construct (v->address () + oldlen, len - oldlen);
 }
 
 
@@ -818,7 +838,7 @@ vec<T, A, vl_embed>::copy (ALONE_MEM_STAT_DECL) const
     {
       vec_alloc (new_vec, len PASS_MEM_STAT);
       new_vec->embedded_init (len, len);
-      memcpy (new_vec->address (), m_vecdata, sizeof (T) * len);
+      vec_copy_construct (new_vec->address (), m_vecdata, len);
     }
   return new_vec;
 }
@@ -835,7 +855,7 @@ vec<T, A, vl_embed>::splice (const vec<T, A, vl_embed> &src)
   if (len)
     {
       gcc_checking_assert (space (len));
-      memcpy (address () + length (), src.address (), len * sizeof (T));
+      vec_copy_construct (end (), src.address (), len);
       m_vecpfx.m_num += len;
     }
 }
@@ -1089,13 +1109,12 @@ inline void
 vec<T, A, vl_embed>::quick_grow_cleared (unsigned len)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   quick_grow (len);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
-
 /* Garbage collection support for vec<T, A, vl_embed>.  */
 
 template<typename T>
@@ -1454,7 +1473,7 @@ vec<T, va_heap, vl_ptr>::reserve (unsigned nelems, bool exact MEM_STAT_DECL)
   va_heap::reserve (m_vec, nelems, exact PASS_MEM_STAT);
   if (handle_auto_vec)
     {
-      memcpy (m_vec->address (), oldvec->address (), sizeof (T) * oldsize);
+      vec_copy_construct (m_vec->address (), oldvec->address (), oldsize);
       m_vec->m_vecpfx.m_num = oldsize;
     }
 
@@ -1616,10 +1635,10 @@ inline void
 vec<T, va_heap, vl_ptr>::safe_grow_cleared (unsigned len MEM_STAT_DECL)
 {
   unsigned oldlen = length ();
-  size_t sz = sizeof (T) * (len - oldlen);
+  size_t growby = len - oldlen;
   safe_grow (len PASS_MEM_STAT);
-  if (sz != 0)
-    memset (&(address ()[oldlen]), 0, sz);
+  if (growby != 0)
+    vec_default_construct (address () + oldlen, growby);
 }
 
 
diff --git a/libcpp/line-map.c b/libcpp/line-map.c
index c4b7cb2..56b7913 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -62,7 +62,8 @@ extern unsigned num_macro_tokens_counter;
 
 line_maps::~line_maps ()
 {
-  htab_delete (location_adhoc_data_map.htab);
+  if (location_adhoc_data_map.htab)
+    htab_delete (location_adhoc_data_map.htab);
 }
 
 /* Hash function for location_adhoc_data hashtable.  */
@@ -347,7 +348,7 @@ void
 linemap_init (struct line_maps *set,
 	      source_location builtin_location)
 {
-  memset (set, 0, sizeof (struct line_maps));
+  *set = line_maps ();
   set->highest_location = RESERVED_LOCATION_COUNT - 1;
   set->highest_line = RESERVED_LOCATION_COUNT - 1;
   set->location_adhoc_data_map.htab =
diff --git a/libitm/beginend.cc b/libitm/beginend.cc
index d04f3e9..c6550a3 100644
--- a/libitm/beginend.cc
+++ b/libitm/beginend.cc
@@ -431,7 +431,7 @@ GTM::gtm_transaction_cp::save(gtm_thread* tx)
   // Save everything that we might have to restore on restarts or aborts.
   jb = tx->jb;
   undolog_size = tx->undolog.size();
-  memcpy(&alloc_actions, &tx->alloc_actions, sizeof(alloc_actions));
+  alloc_actions = tx->alloc_actions;
   user_actions_size = tx->user_actions.size();
   id = tx->id;
   prop = tx->prop;
@@ -449,7 +449,7 @@ GTM::gtm_transaction_cp::commit(gtm_thread* tx)
   // commits of nested transactions. Allocation actions must be committed
   // before committing the snapshot.
   tx->jb = jb;
-  memcpy(&tx->alloc_actions, &alloc_actions, sizeof(alloc_actions));
+  tx->alloc_actions = alloc_actions;
   tx->id = id;
   tx->prop = prop;
 }
@@ -485,7 +485,7 @@ GTM::gtm_thread::rollback (gtm_transaction_cp *cp, bool aborting)
       prop = cp->prop;
       if (cp->disp != abi_disp())
 	set_abi_disp(cp->disp);
-      memcpy(&alloc_actions, &cp->alloc_actions, sizeof(alloc_actions));
+      alloc_actions = cp->alloc_actions;
       nesting = cp->nesting;
     }
   else
diff --git a/libitm/method-ml.cc b/libitm/method-ml.cc
index fcae334..b857bff 100644
--- a/libitm/method-ml.cc
+++ b/libitm/method-ml.cc
@@ -138,7 +138,11 @@ struct ml_mg : public method_group
     // This store is only executed while holding the serial lock, so relaxed
     // memory order is sufficient here.  Same holds for the memset.
     time.store(0, memory_order_relaxed);
-    memset(orecs, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
+    // The memset below isn't strictly kosher because it bypasses
+    // the non-trivial assignment operator defined by std::atomic.  Using
+    // a local void* is enough to prevent GCC from warning for this.
+    void *p = orecs;
+    memset(p, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
   }
 };
 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-08 20:25                                             ` Martin Sebor
@ 2017-06-12 21:36                                               ` Jason Merrill
  2017-06-15 16:26                                                 ` Martin Sebor
  0 siblings, 1 reply; 46+ messages in thread
From: Jason Merrill @ 2017-06-12 21:36 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Pedro Alves, Gcc Patch List

On 06/08/2017 01:25 PM, Martin Sebor wrote:
> +      if (TREE_CHAIN (DECL_ARGUMENTS (f)))
> +	{
> +	  /* Skip constructors that aren't copy or move ctors.  */
> +	  if (!copy_fn_p (f))
> +	    continue;
> +
> +	  cpy_or_move_ctor_p = true;
> +	}
> +      else
> +	{
> +	  /* Constructor is a default ctor.  */
> +	  cpy_or_move_ctor_p = false;
> +	}

A default constructor can have parameters, so long as they have default 
arguments.  You can use default_ctor_p to test for a default constructor.

Jason

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-12 21:36                                               ` Jason Merrill
@ 2017-06-15 16:26                                                 ` Martin Sebor
  2017-06-15 21:31                                                   ` Jason Merrill
  0 siblings, 1 reply; 46+ messages in thread
From: Martin Sebor @ 2017-06-15 16:26 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Pedro Alves, Gcc Patch List

[-- Attachment #1: Type: text/plain, Size: 878 bytes --]

On 06/12/2017 03:36 PM, Jason Merrill wrote:
> On 06/08/2017 01:25 PM, Martin Sebor wrote:
>> +      if (TREE_CHAIN (DECL_ARGUMENTS (f)))
>> +    {
>> +      /* Skip constructors that aren't copy or move ctors.  */
>> +      if (!copy_fn_p (f))
>> +        continue;
>> +
>> +      cpy_or_move_ctor_p = true;
>> +    }
>> +      else
>> +    {
>> +      /* Constructor is a default ctor.  */
>> +      cpy_or_move_ctor_p = false;
>> +    }
>
> A default constructor can have parameters, so long as they have default
> arguments.  You can use default_ctor_p to test for a default constructor.

Thank you for the suggestion.  Attached is an incremental diff
with this tweak plus a test for it.

The code above has been there in the last three revisions of
the patch, so just to streamline the review/redo/retest process:
are there any other changes you'd like me to make?

Martin

[-- Attachment #2: gcc-80560.diff.diff --]
[-- Type: text/x-patch, Size: 3401 bytes --]

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 6e0951e..50c26bf 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8251,20 +8251,11 @@ has_trivial_copy_p (tree type, bool access, bool hasctor[2])
       if (TREE_CODE (f) != FUNCTION_DECL)
 	continue;
 
-      bool cpy_or_move_ctor_p;
-      if (TREE_CHAIN (DECL_ARGUMENTS (f)))
-	{
-	  /* Skip constructors that aren't copy or move ctors.  */
-	  if (!copy_fn_p (f))
-	    continue;
+      bool cpy_or_move_ctor_p = copy_fn_p (f);
 
-	  cpy_or_move_ctor_p = true;
-	}
-      else
-	{
-	  /* Constructor is a default ctor.  */
-	  cpy_or_move_ctor_p = false;
-	}
+      /* Skip ctors other than default, copy, and move.  */
+      if (!cpy_or_move_ctor_p && !default_ctor_p (f))
+	continue;
 
       if (DECL_DELETED_FN (f))
 	continue;
@@ -8346,7 +8337,7 @@ maybe_warn_class_memaccess (location_t loc, tree fndecl, tree *args)
      accessible.  */
   bool trivassign = has_trivial_copy_assign_p (desttype, true, &hasassign);
 
-  /* Set to true if DESTTYPE has an accessible defqault and copy ctor,
+  /* Set to true if DESTTYPE has an accessible default and copy ctor,
      respectively.  */
   bool hasctors[2] = { false, false };
 
diff --git a/gcc/testsuite/g++.dg/Wclass-memaccess.C b/gcc/testsuite/g++.dg/Wclass-memaccess.C
index d7ba44f..4783438 100644
--- a/gcc/testsuite/g++.dg/Wclass-memaccess.C
+++ b/gcc/testsuite/g++.dg/Wclass-memaccess.C
@@ -314,9 +314,11 @@ void test (HasCopy *p, const HasCopy &x,
   const int i = *ia;
   const size_t n = *ia;
 
-  // Zeroing out is diagnosed because value initialization is
-  // invalid (the copy ctor makes no default ctor unavailable).
-  T (bzero, (p, sizeof *p));        // { dg-warning "bzero" }
+  // Zeroing out is diagnosed because value initialization is invalid
+  // (the copy ctor makes no default ctor unavailable).  Since the type
+  // has no default ctor verify that the suggested alternative does not
+  // include value-initialization.
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of non-trivial type .struct HasCopy.; use assignment instead" }
   T (memset, (p, 0, sizeof *p));    // { dg-warning "memset" }
   T (memset, (p, 1, sizeof *p));    // { dg-warning "memset" }
   T (memset, (p, i, sizeof *p));    // { dg-warning "memset" }
@@ -350,6 +352,27 @@ void test (HasCopy *p, const HasCopy &x,
 
 #endif
 
+#if !defined TEST || TEST == TEST_HAS_DEFAULT_AND_COPY
+
+/* HasDefaultAndCopy is like HasCopy above but its default ctor takes
+   a default argument to verify that the suggested alternative offered
+   by the warning includes the default ctor (i.e., the test verifies
+   that the default ctor is recognized as such despite taking an argument.  */
+
+struct HasDefaultAndCopy
+{
+  HasDefaultAndCopy (int = 0);   // default ctor
+  HasDefaultAndCopy (const HasDefaultAndCopy&);
+};
+
+void test (HasDefaultAndCopy *p, const HasDefaultAndCopy &x)
+{
+  T (bzero, (p, sizeof *p));        // { dg-warning "clearing an object of non-trivial type .struct HasDefaultAndCopy.; use assignment or value-initialization instead" }
+  T (memset, (p, 0, sizeof *p));    // { dg-warning "clearing an object of non-trivial type .struct HasDefaultAndCopy.; use assignment or value-initialization instead" }
+}
+
+#endif
+
 #if !defined TEST || TEST == TEST_HAS_PRIVATE_COPY
 
 /* HasPrivateCopy cannot be copied using memcpy or memmove.  Since it's

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-15 16:26                                                 ` Martin Sebor
@ 2017-06-15 21:31                                                   ` Jason Merrill
  2017-06-16  7:38                                                     ` Richard Biener
  0 siblings, 1 reply; 46+ messages in thread
From: Jason Merrill @ 2017-06-15 21:31 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Pedro Alves, Gcc Patch List

On Thu, Jun 15, 2017 at 12:26 PM, Martin Sebor <msebor@gmail.com> wrote:
> On 06/12/2017 03:36 PM, Jason Merrill wrote:
>>
>> On 06/08/2017 01:25 PM, Martin Sebor wrote:
>>>
>>> +      if (TREE_CHAIN (DECL_ARGUMENTS (f)))
>>> +    {
>>> +      /* Skip constructors that aren't copy or move ctors.  */
>>> +      if (!copy_fn_p (f))
>>> +        continue;
>>> +
>>> +      cpy_or_move_ctor_p = true;
>>> +    }
>>> +      else
>>> +    {
>>> +      /* Constructor is a default ctor.  */
>>> +      cpy_or_move_ctor_p = false;
>>> +    }
>>
>> A default constructor can have parameters, so long as they have default
>> arguments.  You can use default_ctor_p to test for a default constructor.
>
> Thank you for the suggestion.  Attached is an incremental diff
> with this tweak plus a test for it.
>
> The code above has been there in the last three revisions of
> the patch

Yeah, I don't always notice everything :)

> are there any other changes you'd like me to make?

No, the patch is OK with this change.

Jason

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-15 21:31                                                   ` Jason Merrill
@ 2017-06-16  7:38                                                     ` Richard Biener
  2017-06-16  7:40                                                       ` Richard Biener
  0 siblings, 1 reply; 46+ messages in thread
From: Richard Biener @ 2017-06-16  7:38 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Martin Sebor, Pedro Alves, Gcc Patch List

On Thu, Jun 15, 2017 at 11:31 PM, Jason Merrill <jason@redhat.com> wrote:
> On Thu, Jun 15, 2017 at 12:26 PM, Martin Sebor <msebor@gmail.com> wrote:
>> On 06/12/2017 03:36 PM, Jason Merrill wrote:
>>>
>>> On 06/08/2017 01:25 PM, Martin Sebor wrote:
>>>>
>>>> +      if (TREE_CHAIN (DECL_ARGUMENTS (f)))
>>>> +    {
>>>> +      /* Skip constructors that aren't copy or move ctors.  */
>>>> +      if (!copy_fn_p (f))
>>>> +        continue;
>>>> +
>>>> +      cpy_or_move_ctor_p = true;
>>>> +    }
>>>> +      else
>>>> +    {
>>>> +      /* Constructor is a default ctor.  */
>>>> +      cpy_or_move_ctor_p = false;
>>>> +    }
>>>
>>> A default constructor can have parameters, so long as they have default
>>> arguments.  You can use default_ctor_p to test for a default constructor.
>>
>> Thank you for the suggestion.  Attached is an incremental diff
>> with this tweak plus a test for it.
>>
>> The code above has been there in the last three revisions of
>> the patch
>
> Yeah, I don't always notice everything :)
>
>> are there any other changes you'd like me to make?
>
> No, the patch is OK with this change.

This broke build with GCC 4.8 as host compiler:

g++ -fno-PIE -c   -g  -DIN_GCC     -fno-exceptions -fno-rtti
-fasynchronous-unwind-tables -W -Wall -Wno-narrowing -Wwrite-strings
-Wcast-qual -Wmissing-format-attribute -Woverloaded-virtual -pedantic
-Wno-long-long -Wno-variadic-macros -Wno-overlength-strings
-fno-common  -DHAVE_CONFIG_H -I. -I.
-I/space/rguenther/src/svn/early-lto-debug/gcc
-I/space/rguenther/src/svn/early-lto-debug/gcc/.
-I/space/rguenther/src/svn/early-lto-debug/gcc/../include
-I/space/rguenther/src/svn/early-lto-debug/gcc/../libcpp/include
-I/space/rguenther/src/svn/early-lto-debug/gcc/../libdecnumber
-I/space/rguenther/src/svn/early-lto-debug/gcc/../libdecnumber/bid
-I../libdecnumber
-I/space/rguenther/src/svn/early-lto-debug/gcc/../libbacktrace   -o
tree-switch-conversion.o -MT tree-switch-conversion.o -MMD -MP -MF
./.deps/tree-switch-conversion.TPo
/space/rguenther/src/svn/early-lto-debug/gcc/tree-switch-conversion.c
/space/rguenther/src/svn/early-lto-debug/gcc/tree-switch-conversion.c:
In function ‘void emit_case_bit_tests(gswitch*, tree, tree, tree,
tree)’:
/space/rguenther/src/svn/early-lto-debug/gcc/tree-switch-conversion.c:271:53:
warning: missing initializer for member ‘case_bit_test::mask’
[-Wmissing-field-initializers]
   struct case_bit_test test[MAX_CASE_BIT_TESTS] = { };
                                                     ^
...
/space/rguenther/src/svn/early-lto-debug/gcc/tree-switch-conversion.c:271:53:
internal compiler error: in gimplify_init_constructor, at
gimplify.c:4271
Please submit a full bug report,
with preprocessed source if appropriate.
See <http://bugs.opensuse.org/> for instructions.
Makefile:1102: recipe for target 'tree-switch-conversion.o' failed
make: *** [tree-switch-conversion.o] Error 1

Please fix.

Richard.

> Jason

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-16  7:38                                                     ` Richard Biener
@ 2017-06-16  7:40                                                       ` Richard Biener
  0 siblings, 0 replies; 46+ messages in thread
From: Richard Biener @ 2017-06-16  7:40 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Martin Sebor, Pedro Alves, Gcc Patch List

On Fri, Jun 16, 2017 at 9:38 AM, Richard Biener
<richard.guenther@gmail.com> wrote:
> On Thu, Jun 15, 2017 at 11:31 PM, Jason Merrill <jason@redhat.com> wrote:
>> On Thu, Jun 15, 2017 at 12:26 PM, Martin Sebor <msebor@gmail.com> wrote:
>>> On 06/12/2017 03:36 PM, Jason Merrill wrote:
>>>>
>>>> On 06/08/2017 01:25 PM, Martin Sebor wrote:
>>>>>
>>>>> +      if (TREE_CHAIN (DECL_ARGUMENTS (f)))
>>>>> +    {
>>>>> +      /* Skip constructors that aren't copy or move ctors.  */
>>>>> +      if (!copy_fn_p (f))
>>>>> +        continue;
>>>>> +
>>>>> +      cpy_or_move_ctor_p = true;
>>>>> +    }
>>>>> +      else
>>>>> +    {
>>>>> +      /* Constructor is a default ctor.  */
>>>>> +      cpy_or_move_ctor_p = false;
>>>>> +    }
>>>>
>>>> A default constructor can have parameters, so long as they have default
>>>> arguments.  You can use default_ctor_p to test for a default constructor.
>>>
>>> Thank you for the suggestion.  Attached is an incremental diff
>>> with this tweak plus a test for it.
>>>
>>> The code above has been there in the last three revisions of
>>> the patch
>>
>> Yeah, I don't always notice everything :)
>>
>>> are there any other changes you'd like me to make?
>>
>> No, the patch is OK with this change.
>
> This broke build with GCC 4.8 as host compiler:
>
> g++ -fno-PIE -c   -g  -DIN_GCC     -fno-exceptions -fno-rtti
> -fasynchronous-unwind-tables -W -Wall -Wno-narrowing -Wwrite-strings
> -Wcast-qual -Wmissing-format-attribute -Woverloaded-virtual -pedantic
> -Wno-long-long -Wno-variadic-macros -Wno-overlength-strings
> -fno-common  -DHAVE_CONFIG_H -I. -I.
> -I/space/rguenther/src/svn/early-lto-debug/gcc
> -I/space/rguenther/src/svn/early-lto-debug/gcc/.
> -I/space/rguenther/src/svn/early-lto-debug/gcc/../include
> -I/space/rguenther/src/svn/early-lto-debug/gcc/../libcpp/include
> -I/space/rguenther/src/svn/early-lto-debug/gcc/../libdecnumber
> -I/space/rguenther/src/svn/early-lto-debug/gcc/../libdecnumber/bid
> -I../libdecnumber
> -I/space/rguenther/src/svn/early-lto-debug/gcc/../libbacktrace   -o
> tree-switch-conversion.o -MT tree-switch-conversion.o -MMD -MP -MF
> ./.deps/tree-switch-conversion.TPo
> /space/rguenther/src/svn/early-lto-debug/gcc/tree-switch-conversion.c
> /space/rguenther/src/svn/early-lto-debug/gcc/tree-switch-conversion.c:
> In function ‘void emit_case_bit_tests(gswitch*, tree, tree, tree,
> tree)’:
> /space/rguenther/src/svn/early-lto-debug/gcc/tree-switch-conversion.c:271:53:
> warning: missing initializer for member ‘case_bit_test::mask’
> [-Wmissing-field-initializers]
>    struct case_bit_test test[MAX_CASE_BIT_TESTS] = { };
>                                                      ^
> ...
> /space/rguenther/src/svn/early-lto-debug/gcc/tree-switch-conversion.c:271:53:
> internal compiler error: in gimplify_init_constructor, at
> gimplify.c:4271
> Please submit a full bug report,
> with preprocessed source if appropriate.
> See <http://bugs.opensuse.org/> for instructions.
> Makefile:1102: recipe for target 'tree-switch-conversion.o' failed
> make: *** [tree-switch-conversion.o] Error 1
>
> Please fix.

Using

  struct case_bit_test test[MAX_CASE_BIT_TESTS] = { {} };

avoids the ICE, the warning persists (not sure if correctly or not).
I'll commit this
workaround if it survives stage2/3 -Werror.

Richard.

> Richard.
>
>> Jason

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-05-11 20:03     ` Martin Sebor
  2017-05-12  2:43       ` Martin Sebor
@ 2017-06-29 16:15       ` Jan Hubicka
  2017-06-29 20:23         ` Martin Sebor
  2017-07-04  9:33         ` Richard Earnshaw (lists)
  1 sibling, 2 replies; 46+ messages in thread
From: Jan Hubicka @ 2017-06-29 16:15 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Jason Merrill, Pedro Alves, Gcc Patch List, hubicka

Hello,
> diff --git a/gcc/hash-table.h b/gcc/hash-table.h
> index 0f7e21a..443d16c 100644
> --- a/gcc/hash-table.h
> +++ b/gcc/hash-table.h
> @@ -803,7 +803,10 @@ hash_table<Descriptor, Allocator>::empty_slow ()
>        m_size_prime_index = nindex;
>      }
>    else
> -    memset (entries, 0, size * sizeof (value_type));
> +    {
> +      for ( ; size; ++entries, --size)
> +	*entries = value_type ();
> +    }
>    m_n_deleted = 0;
>    m_n_elements = 0;
>  }

This change sends our periodic testers into an infinite loop.  It is fault of gcc 4.2 being used
as bootstrap compiler, but perhaps that can be worked around?

Honza

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-29 16:15       ` Jan Hubicka
@ 2017-06-29 20:23         ` Martin Sebor
  2017-06-29 22:34           ` Jan Hubicka
  2017-06-30  8:34           ` Richard Biener
  2017-07-04  9:33         ` Richard Earnshaw (lists)
  1 sibling, 2 replies; 46+ messages in thread
From: Martin Sebor @ 2017-06-29 20:23 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Jason Merrill, Pedro Alves, Gcc Patch List

On 06/29/2017 10:15 AM, Jan Hubicka wrote:
> Hello,
>> diff --git a/gcc/hash-table.h b/gcc/hash-table.h
>> index 0f7e21a..443d16c 100644
>> --- a/gcc/hash-table.h
>> +++ b/gcc/hash-table.h
>> @@ -803,7 +803,10 @@ hash_table<Descriptor, Allocator>::empty_slow ()
>>        m_size_prime_index = nindex;
>>      }
>>    else
>> -    memset (entries, 0, size * sizeof (value_type));
>> +    {
>> +      for ( ; size; ++entries, --size)
>> +	*entries = value_type ();
>> +    }
>>    m_n_deleted = 0;
>>    m_n_elements = 0;
>>  }
>
> This change sends our periodic testers into an infinite loop.  It is fault of gcc 4.2 being used
> as bootstrap compiler, but perhaps that can be worked around?

The warning in the original code could have been suppressed (by
casting the pointer to char*), but it was valid so I opted not
to.  I'd expect it to be possible to work around the bug but
I don't have easy access to GCC 4.2 to reproduce it or verify
the fix.

FWIW, after looking at the function again, I wondered if zeroing
out the elements (either way) was the right thing to do and if
they shouldn't be cleared by calling Descriptor::mark_empty()
instead, like in alloc_entries(), but making that change broke
a bunch of ipa/ipa-pta-*.c tests.  It's not really clear to me
what this code is supposed to do.

Martin

PS Does this help at all?

@@ -804,8 +804,8 @@ hash_table<Descriptor, Allocator>::empty_slow ()
      }
    else
      {
-      for ( ; size; ++entries, --size)
-       *entries = value_type ();
+      for (size_t i = 0; i != size; ++i)
+       entries[i] = value_type ();
      }
    m_n_deleted = 0;
    m_n_elements = 0;

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-29 20:23         ` Martin Sebor
@ 2017-06-29 22:34           ` Jan Hubicka
  2017-06-30  0:16             ` Martin Sebor
  2017-06-30  8:34           ` Richard Biener
  1 sibling, 1 reply; 46+ messages in thread
From: Jan Hubicka @ 2017-06-29 22:34 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Jan Hubicka, Jason Merrill, Pedro Alves, Gcc Patch List

> 
> The warning in the original code could have been suppressed (by
> casting the pointer to char*), but it was valid so I opted not
> to.  I'd expect it to be possible to work around the bug but
> I don't have easy access to GCC 4.2 to reproduce it or verify
> the fix.
> 
> FWIW, after looking at the function again, I wondered if zeroing
> out the elements (either way) was the right thing to do and if
> they shouldn't be cleared by calling Descriptor::mark_empty()
> instead, like in alloc_entries(), but making that change broke
> a bunch of ipa/ipa-pta-*.c tests.  It's not really clear to me
> what this code is supposed to do.

Well, it is a standard hash table.  The problem I hit was
lookup_with_hash walking infinitly around the hash table because
all elements seemed used.
is_empty is defined as:
template <typename Type>
inline bool
pointer_hash <Type>::is_empty (Type *e)
{
  return e == NULL;
}

and mark_empty as
template <typename Type>                                                        
inline void
pointer_hash <Type>::mark_empty (Type *&e)
{
  e = NULL;
}

I guess they are supposed to be definable to other implementations
but then the former memset code would break.
> Martin
> 
> PS Does this help at all?
> 
> @@ -804,8 +804,8 @@ hash_table<Descriptor, Allocator>::empty_slow ()
>      }
>    else
>      {
> -      for ( ; size; ++entries, --size)
> -       *entries = value_type ();
> +      for (size_t i = 0; i != size; ++i)
> +       entries[i] = value_type ();

I can give it a try tomorrow.  Still wonder what goes wrong with ctors with 4.2 (and 4.3 as well apparently)

Honza
>      }
>    m_n_deleted = 0;
>    m_n_elements = 0;

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-29 22:34           ` Jan Hubicka
@ 2017-06-30  0:16             ` Martin Sebor
  0 siblings, 0 replies; 46+ messages in thread
From: Martin Sebor @ 2017-06-30  0:16 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Jason Merrill, Pedro Alves, Gcc Patch List

On 06/29/2017 04:34 PM, Jan Hubicka wrote:
>>
>> The warning in the original code could have been suppressed (by
>> casting the pointer to char*), but it was valid so I opted not
>> to.  I'd expect it to be possible to work around the bug but
>> I don't have easy access to GCC 4.2 to reproduce it or verify
>> the fix.
>>
>> FWIW, after looking at the function again, I wondered if zeroing
>> out the elements (either way) was the right thing to do and if
>> they shouldn't be cleared by calling Descriptor::mark_empty()
>> instead, like in alloc_entries(), but making that change broke
>> a bunch of ipa/ipa-pta-*.c tests.  It's not really clear to me
>> what this code is supposed to do.
>
> Well, it is a standard hash table.

What I mean is that while the true branch of the if (nsize != size)
statement in hash_table::empty_slow () frees the table and then
allocates a new one which marks each element in the newly allocated
storage as empty by calling mark_empty() (i.e., Descriptor::
mark_empty()), while the false branch bypasses mark_empty() and
calls memset(entries, 0, ...) instead.  The two are equivalent
only when Descriptor::mark_empty(value_type &e) sets e to all
zeros.  They are not equivalent when Descriptor::mark_empty()
does something else.  When value_type is a pointer they should
be the equivalent (so long as a null pointer is all zeros), but
it's not obvious to me what value_type is in your case (tree?)

In any case, calling memset bypasses the (undocumented)
customization point (Descriptor::mark_empty).  Calling it
conditionally depending on the size of the hash table seems
suspicious (as does setting *entry = value_type();).  If there
is a hash table instance where mark_empty is not equivalent to
memset(entries, 0, ...) it could mean a bug.

Looking at some other definitions of mark_empty I see int_hash
defines it this way:

   template <typename Type, Type Empty, Type Deleted>
   inline void
   int_hash <Type, Empty, Deleted>::mark_empty (Type &x)
   {
     x = Empty;
   }

So for hash_tables with int value_types the memset is only
equivalent to calling mark_empty when Empty is zero.  There
are instances of hash tables in GCC like alias_set_hash and
uid_hash where Empty is non-zero.  I wonder what happens when
hash_table::empty_slow() is called on one of these instances
with a small number of elements and that instance is used
again.

> The problem I hit was
> lookup_with_hash walking infinitly around the hash table because
> all elements seemed used.
> is_empty is defined as:
> template <typename Type>
> inline bool
> pointer_hash <Type>::is_empty (Type *e)
> {
>   return e == NULL;
> }
>
> and mark_empty as
> template <typename Type>
> inline void
> pointer_hash <Type>::mark_empty (Type *&e)
> {
>   e = NULL;
> }

It sounds like value_type is a pointer in your case and
the assignment '*entry = value_type();' isn't clearing the *entry.
I vaguely remember coming across a bug like that years ago but I
don't know if that was GCC or some other compiler.  I tried
compiling a small test case with a few GCC revisions close to
4.2 (r117926 and some others) but couldn't reproduce anything
unexpected.

> I guess they are supposed to be definable to other implementations
> but then the former memset code would break.

Right, that's my concern.

Martin

PS the below is just a shot in the dark. I'd be surprised if it
actually did something for you, but with little else to go on
it's worth a try.

>> Martin
>>
>> PS Does this help at all?
>>
>> @@ -804,8 +804,8 @@ hash_table<Descriptor, Allocator>::empty_slow ()
>>      }
>>    else
>>      {
>> -      for ( ; size; ++entries, --size)
>> -       *entries = value_type ();
>> +      for (size_t i = 0; i != size; ++i)
>> +       entries[i] = value_type ();
>
> I can give it a try tomorrow.  Still wonder what goes wrong with ctors with 4.2 (and 4.3 as well apparently)
>
> Honza
>>      }
>>    m_n_deleted = 0;
>>    m_n_elements = 0;

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-29 20:23         ` Martin Sebor
  2017-06-29 22:34           ` Jan Hubicka
@ 2017-06-30  8:34           ` Richard Biener
  2017-06-30 14:29             ` Martin Sebor
  1 sibling, 1 reply; 46+ messages in thread
From: Richard Biener @ 2017-06-30  8:34 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Jan Hubicka, Jason Merrill, Pedro Alves, Gcc Patch List

On Thu, Jun 29, 2017 at 10:23 PM, Martin Sebor <msebor@gmail.com> wrote:
> On 06/29/2017 10:15 AM, Jan Hubicka wrote:
>>
>> Hello,
>>>
>>> diff --git a/gcc/hash-table.h b/gcc/hash-table.h
>>> index 0f7e21a..443d16c 100644
>>> --- a/gcc/hash-table.h
>>> +++ b/gcc/hash-table.h
>>> @@ -803,7 +803,10 @@ hash_table<Descriptor, Allocator>::empty_slow ()
>>>        m_size_prime_index = nindex;
>>>      }
>>>    else
>>> -    memset (entries, 0, size * sizeof (value_type));
>>> +    {
>>> +      for ( ; size; ++entries, --size)
>>> +       *entries = value_type ();
>>> +    }
>>>    m_n_deleted = 0;
>>>    m_n_elements = 0;
>>>  }
>>
>>
>> This change sends our periodic testers into an infinite loop.  It is fault
>> of gcc 4.2 being used
>> as bootstrap compiler, but perhaps that can be worked around?
>
>
> The warning in the original code could have been suppressed (by
> casting the pointer to char*), but it was valid so I opted not
> to.  I'd expect it to be possible to work around the bug but
> I don't have easy access to GCC 4.2 to reproduce it or verify
> the fix.
>
> FWIW, after looking at the function again, I wondered if zeroing
> out the elements (either way) was the right thing to do and if
> they shouldn't be cleared by calling Descriptor::mark_empty()
> instead, like in alloc_entries(), but making that change broke
> a bunch of ipa/ipa-pta-*.c tests.  It's not really clear to me
> what this code is supposed to do.
>
> Martin
>
> PS Does this help at all?
>
> @@ -804,8 +804,8 @@ hash_table<Descriptor, Allocator>::empty_slow ()
>      }
>    else
>      {
> -      for ( ; size; ++entries, --size)
> -       *entries = value_type ();
> +      for (size_t i = 0; i != size; ++i)
> +       entries[i] = value_type ();
>
>      }
>    m_n_deleted = 0;
>    m_n_elements = 0;

alloc_entries uses mark_empty.  untested:

Index: gcc/hash-table.h
===================================================================
--- gcc/hash-table.h    (revision 249780)
+++ gcc/hash-table.h    (working copy)
@@ -804,8 +804,8 @@ hash_table<Descriptor, Allocator>::empty
     }
   else
     {
-      for ( ; size; ++entries, --size)
-       *entries = value_type ();
+      for (size_t i = 0; i < size; ++i)
+       mark_empty (entries[i]);
     }
   m_n_deleted = 0;
   m_n_elements = 0;

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-30  8:34           ` Richard Biener
@ 2017-06-30 14:29             ` Martin Sebor
  0 siblings, 0 replies; 46+ messages in thread
From: Martin Sebor @ 2017-06-30 14:29 UTC (permalink / raw)
  To: Richard Biener; +Cc: Jan Hubicka, Jason Merrill, Pedro Alves, Gcc Patch List

On 06/30/2017 02:34 AM, Richard Biener wrote:
> On Thu, Jun 29, 2017 at 10:23 PM, Martin Sebor <msebor@gmail.com> wrote:
>> On 06/29/2017 10:15 AM, Jan Hubicka wrote:
>>>
>>> Hello,
>>>>
>>>> diff --git a/gcc/hash-table.h b/gcc/hash-table.h
>>>> index 0f7e21a..443d16c 100644
>>>> --- a/gcc/hash-table.h
>>>> +++ b/gcc/hash-table.h
>>>> @@ -803,7 +803,10 @@ hash_table<Descriptor, Allocator>::empty_slow ()
>>>>        m_size_prime_index = nindex;
>>>>      }
>>>>    else
>>>> -    memset (entries, 0, size * sizeof (value_type));
>>>> +    {
>>>> +      for ( ; size; ++entries, --size)
>>>> +       *entries = value_type ();
>>>> +    }
>>>>    m_n_deleted = 0;
>>>>    m_n_elements = 0;
>>>>  }
>>>
>>>
>>> This change sends our periodic testers into an infinite loop.  It is fault
>>> of gcc 4.2 being used
>>> as bootstrap compiler, but perhaps that can be worked around?
>>
>>
>> The warning in the original code could have been suppressed (by
>> casting the pointer to char*), but it was valid so I opted not
>> to.  I'd expect it to be possible to work around the bug but
>> I don't have easy access to GCC 4.2 to reproduce it or verify
>> the fix.
>>
>> FWIW, after looking at the function again, I wondered if zeroing
>> out the elements (either way) was the right thing to do and if
>> they shouldn't be cleared by calling Descriptor::mark_empty()
>> instead, like in alloc_entries(), but making that change broke
>> a bunch of ipa/ipa-pta-*.c tests.  It's not really clear to me
>> what this code is supposed to do.
>>
>> Martin
>>
>> PS Does this help at all?
>>
>> @@ -804,8 +804,8 @@ hash_table<Descriptor, Allocator>::empty_slow ()
>>      }
>>    else
>>      {
>> -      for ( ; size; ++entries, --size)
>> -       *entries = value_type ();
>> +      for (size_t i = 0; i != size; ++i)
>> +       entries[i] = value_type ();
>>
>>      }
>>    m_n_deleted = 0;
>>    m_n_elements = 0;
>
> alloc_entries uses mark_empty.  untested:

Right.  That was my initial thought as well but it broke a number
of ipa/ipa-pta-*.c tests.  I didn't have time to investigate why.

Martin

>
> Index: gcc/hash-table.h
> ===================================================================
> --- gcc/hash-table.h    (revision 249780)
> +++ gcc/hash-table.h    (working copy)
> @@ -804,8 +804,8 @@ hash_table<Descriptor, Allocator>::empty
>      }
>    else
>      {
> -      for ( ; size; ++entries, --size)
> -       *entries = value_type ();
> +      for (size_t i = 0; i < size; ++i)
> +       mark_empty (entries[i]);
>      }
>    m_n_deleted = 0;
>    m_n_elements = 0;
>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-06-29 16:15       ` Jan Hubicka
  2017-06-29 20:23         ` Martin Sebor
@ 2017-07-04  9:33         ` Richard Earnshaw (lists)
  1 sibling, 0 replies; 46+ messages in thread
From: Richard Earnshaw (lists) @ 2017-07-04  9:33 UTC (permalink / raw)
  To: Jan Hubicka, Martin Sebor; +Cc: Jason Merrill, Pedro Alves, Gcc Patch List

On 29/06/17 17:15, Jan Hubicka wrote:
> Hello,
>> diff --git a/gcc/hash-table.h b/gcc/hash-table.h
>> index 0f7e21a..443d16c 100644
>> --- a/gcc/hash-table.h
>> +++ b/gcc/hash-table.h
>> @@ -803,7 +803,10 @@ hash_table<Descriptor, Allocator>::empty_slow ()
>>        m_size_prime_index = nindex;
>>      }
>>    else
>> -    memset (entries, 0, size * sizeof (value_type));
>> +    {
>> +      for ( ; size; ++entries, --size)
>> +	*entries = value_type ();
>> +    }
>>    m_n_deleted = 0;
>>    m_n_elements = 0;
>>  }
> 
> This change sends our periodic testers into an infinite loop.  It is fault of gcc 4.2 being used
> as bootstrap compiler, but perhaps that can be worked around?
> 
> Honza
> 

Same is also true on RHE5 (gcc-4.1).

R.

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
       [not found] ` <656ca1db-1082-b1ed-a911-ba7bf48f09c0@redhat.com>
  2017-05-01 15:49   ` Jason Merrill
  2017-05-11 16:34   ` Martin Sebor
@ 2017-07-05 20:58   ` Andrew Pinski
  2017-07-05 22:33     ` Martin Sebor
  2 siblings, 1 reply; 46+ messages in thread
From: Andrew Pinski @ 2017-07-05 20:58 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Martin Sebor, Gcc Patch List, Jason Merrill

On Sun, Apr 30, 2017 at 1:02 PM, Pedro Alves <palves@redhat.com> wrote:
> Hi Martin,
>
> Thanks much for doing this.  A few comments below, in light of my
> experience doing the equivalent checks in the gdb patch linked below,
> using standard C++11.
>
> On 04/29/2017 09:09 PM, Martin Sebor wrote:
>> Calling memset, memcpy, or similar to write to an object of
>> a non-trivial type (such as one that defines a ctor or dtor,
>> or has such a member) can break the invariants otherwise
>> maintained by the class and cause undefined behavior.
>>
>> The motivating example that prompted this work was a review of
>> a change that added to a plain old struct a new member with a ctor
>> and dtor (in this instance the member was of type std::vector).
>>
>> To help catch problems of this sort some projects (such as GDB)
>> have apparently even devised their own clever solutions to detect
>> them: https://sourceware.org/ml/gdb-patches/2017-04/msg00378.html.
>>
>> The attached patch adds a new warning, -Wnon-trivial-memaccess,
>> that has GCC detect these mistakes.  The patch also fixes up
>> a handful of instances of the problem in GCC.  These instances
>> correspond to the two patterns below:
>>
>>   struct A
>>   {
>>     void *p;
>>     void foo (int n) { p = malloc (n); }
>>     ~A () { free (p); }
>>   };
>>
>>   void init (A *a)
>>   {
>>     memset (a, 0, sizeof *a);
>>   }
>>
>> and
>>
>>   struct B
>>   {
>>     int i;
>>     ~A ();
>>   };
>
> (typo: "~B ();")
>
>>
>>   void copy (B *p, const B *q)
>>   {
>>     memcpy (p, q, sizeof *p);
>>     ...
>>    }
>>
>
> IMO the check should be relaxed from "type is trivial" to "type is
> trivially copyable" (which is what the gdb detection at
> https://sourceware.org/ml/gdb-patches/2017-04/msg00378.html
> uses for memcpy/memmove).  Checking that the destination is trivial is
> going to generate false positives -- specifically, [basic-types]/3
> specifies that it's fine to memcpy trivially _copyable_ types, not
> trivial types.  A type can be both non-trivial and trivially copyable
> at the same time.  For example, this compiles, but triggers
> your new warning:
>
> #include <stdlib.h>
> #include <string.h>
> #include <type_traits>
>
> struct NonTrivialButTriviallyCopyable
> {
>   NonTrivialButTriviallyCopyable () : i (0) {}
>   int i;
> };
>
> static_assert (!std::is_trivial<NonTrivialButTriviallyCopyable>::value, "");
> static_assert (std::is_trivially_copyable<NonTrivialButTriviallyCopyable>::value, "");
>
> void copy (NonTrivialButTriviallyCopyable *dst, NonTrivialButTriviallyCopyable *src)
> {
>   memcpy (dst, src, sizeof (*src));
> }
>
> $ /opt/gcc/bin/g++ -std=gnu++11 trivial-warn.cc -o trivial-warn -g3 -O0 -Wall -Wextra -c
> trivial-warn.cc: In function ‘void copy(NonTrivialButTriviallyCopyable*, NonTrivialButTriviallyCopyable*)’:
> trivial-warn.cc:16:34: warning: calling ‘void* memcpy(void*, const void*, size_t)’ with a pointer to a non-trivial type ‘struct NonTrivialButTriviallyCopyable’ [-Wnon-trivial-memaccess]
>    memcpy (dst, src, sizeof (*src));
>                                   ^
> $
>
> Implementations of vector-like classes can very well (and are
> encouraged) to make use of std::is_trivially_copyable to know whether
> they can copy a range of elements to new storage
> using memcpy/memmove/mempcpy.
>
> Running your patch against GDB trips on such a case:
>
> src/gdb/btrace.h: In function ‘btrace_insn_s* VEC_btrace_insn_s_quick_insert(VEC_btrace_insn_s*, unsigned int, const btrace_insn_s*, const char*, unsigned int)’:
> src/gdb/common/vec.h:948:62: error: calling ‘void* memmove(void*, const void*, size_t)’ with a pointer to a non-trivial type ‘btrace_insn_s {aka struct btrace_insn}’ [-Werror=non-trivial-memaccess]
>    memmove (slot_ + 1, slot_, (vec_->num++ - ix_) * sizeof (T));    \
>                                                               ^
>
> There is nothing wrong with the code being warned here.
> While "struct btrace_insn" is trivial (has a user-provided default
> ctor), it is still trivially copyable.


Any news on getting a "fix" for this issue.  Right now it blocks my
testing of GCC/gdb because I am building the trunk of both in a CI
loop and my build is broken due to this warning.  Should I just add
--disable-werror to my gdb build instead?

Thanks,
Andrew Pinski

>
> Now, this gdb code is using the old VEC (originated from
> gcc's C days, it's not the current C++fied VEC implementation),
> but the point is that any other random vector-like container out there
> is free to optimize copy of a range of non-trivial but trivially
> copyable types using memcpy/memmove.
>
> Note that libstdc++ does not actually do that optimization, but
> that's just a missed optimization, see PR libstdc++/68350 [1]
> "std::uninitialized_copy overly restrictive for
> trivially_copyable types".  (libstdc++'s std::vector defers
> copy to std::unitialized_copy.)
>
> [1] - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68350
>
>> These aren't undefined and the patch could be tweaked to allow
>> them.
>
> I think they're undefined because the types are not trivially
> copyable (non-trivial destructor with side effects).
>
>> I decided not to invest effort into it because, although
>> not strictly erroneous, I think they represent poor practice.
>
> I think that for my suggestion, all you really need is use
> call trivially_copyable_p instead of trivial_type_p, for
> memcpy/memmove/mempcpy at least.
>
> For memset, I'd suggest to go the other direction actually, and
> instead of relaxing the trivial check, to tighten it, by warning about
> memset'ting objects of non-standard-layout type too.  I.e., warn for
> memset of all non-POD (non-trivial + non-standard-layout) types.  That's
> what I did in the gdb patch.  That would also produce warnings for memset
> of trivial types that via refactoring end up with references, like:
>
> struct Trivial
> {
>   Trivial () = default;
>   Trivial (int &i) : m_i (i) {}
>   void add (int howmuch) { m_i += howmuch; }
>
> private:
>   int &m_i;
> };
>
> void reset (Trivial *triv)
> {
>   memset (triv, 0, sizeof (Trivial));
> }
>
> void recompute (Trivial *triv)
> {
>   reset (triv); // start over
>   triv->add (10); // whoops, null reference.
> }
>
> It's also warn for memset of trivial types that aren't standard
> layout due to having a mix of public/protected/private fields,
> which is likely not a real problem in practice, but I'd call
> those a code smell that warrants a warning too:
>
>  struct S
>  {
>  private:
>    int a;
>  public:
>    int b;
>  };
>
>  S s;
>  memset (&s, 0, sizeof (S));
>
>
> Playing with the patch, I noticed that you can't silence
> it by casting the pointer to void, but you can with
> casting to char, like:
>
> void copy (B *dst, B *src)
> {
>     memcpy (dst, src, sizeof (*src)); // warns
>     memcpy ((void*) dst, (void *) src, sizeof (*src)); // still warns
>     memcpy ((char*) dst, (char *) src, sizeof (*src)); // doesn't warn
>     memcpy ((unsigned char*) dst, (unsigned char *) src, sizeof (*src)); // doesn't warn
> }
>
> I can understand how we end up like that, given char's magic properties, but still
> I think many will reach for "void" first if they (really really) need to add a cast to
> silence the warning.  In any case, I think it'd be very nice to add cases with such
> casts to the new tests, and maybe mention it in the documentation too.
>
> Thanks,
> Pedro Alves
>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)
  2017-07-05 20:58   ` Andrew Pinski
@ 2017-07-05 22:33     ` Martin Sebor
  0 siblings, 0 replies; 46+ messages in thread
From: Martin Sebor @ 2017-07-05 22:33 UTC (permalink / raw)
  To: Andrew Pinski, Pedro Alves; +Cc: Gcc Patch List, Jason Merrill

On 07/05/2017 02:58 PM, Andrew Pinski wrote:
> On Sun, Apr 30, 2017 at 1:02 PM, Pedro Alves <palves@redhat.com> wrote:
>> Hi Martin,
>>
>> Thanks much for doing this.  A few comments below, in light of my
>> experience doing the equivalent checks in the gdb patch linked below,
>> using standard C++11.
>>
>> On 04/29/2017 09:09 PM, Martin Sebor wrote:
>>> Calling memset, memcpy, or similar to write to an object of
>>> a non-trivial type (such as one that defines a ctor or dtor,
>>> or has such a member) can break the invariants otherwise
>>> maintained by the class and cause undefined behavior.
>>>
>>> The motivating example that prompted this work was a review of
>>> a change that added to a plain old struct a new member with a ctor
>>> and dtor (in this instance the member was of type std::vector).
>>>
>>> To help catch problems of this sort some projects (such as GDB)
>>> have apparently even devised their own clever solutions to detect
>>> them: https://sourceware.org/ml/gdb-patches/2017-04/msg00378.html.
>>>
>>> The attached patch adds a new warning, -Wnon-trivial-memaccess,
>>> that has GCC detect these mistakes.  The patch also fixes up
>>> a handful of instances of the problem in GCC.  These instances
>>> correspond to the two patterns below:
>>>
>>>   struct A
>>>   {
>>>     void *p;
>>>     void foo (int n) { p = malloc (n); }
>>>     ~A () { free (p); }
>>>   };
>>>
>>>   void init (A *a)
>>>   {
>>>     memset (a, 0, sizeof *a);
>>>   }
>>>
>>> and
>>>
>>>   struct B
>>>   {
>>>     int i;
>>>     ~A ();
>>>   };
>>
>> (typo: "~B ();")
>>
>>>
>>>   void copy (B *p, const B *q)
>>>   {
>>>     memcpy (p, q, sizeof *p);
>>>     ...
>>>    }
>>>
>>
>> IMO the check should be relaxed from "type is trivial" to "type is
>> trivially copyable" (which is what the gdb detection at
>> https://sourceware.org/ml/gdb-patches/2017-04/msg00378.html
>> uses for memcpy/memmove).  Checking that the destination is trivial is
>> going to generate false positives -- specifically, [basic-types]/3
>> specifies that it's fine to memcpy trivially _copyable_ types, not
>> trivial types.  A type can be both non-trivial and trivially copyable
>> at the same time.  For example, this compiles, but triggers
>> your new warning:
>>
>> #include <stdlib.h>
>> #include <string.h>
>> #include <type_traits>
>>
>> struct NonTrivialButTriviallyCopyable
>> {
>>   NonTrivialButTriviallyCopyable () : i (0) {}
>>   int i;
>> };
>>
>> static_assert (!std::is_trivial<NonTrivialButTriviallyCopyable>::value, "");
>> static_assert (std::is_trivially_copyable<NonTrivialButTriviallyCopyable>::value, "");
>>
>> void copy (NonTrivialButTriviallyCopyable *dst, NonTrivialButTriviallyCopyable *src)
>> {
>>   memcpy (dst, src, sizeof (*src));
>> }
>>
>> $ /opt/gcc/bin/g++ -std=gnu++11 trivial-warn.cc -o trivial-warn -g3 -O0 -Wall -Wextra -c
>> trivial-warn.cc: In function ‘void copy(NonTrivialButTriviallyCopyable*, NonTrivialButTriviallyCopyable*)’:
>> trivial-warn.cc:16:34: warning: calling ‘void* memcpy(void*, const void*, size_t)’ with a pointer to a non-trivial type ‘struct NonTrivialButTriviallyCopyable’ [-Wnon-trivial-memaccess]
>>    memcpy (dst, src, sizeof (*src));
>>                                   ^
>> $
>>
>> Implementations of vector-like classes can very well (and are
>> encouraged) to make use of std::is_trivially_copyable to know whether
>> they can copy a range of elements to new storage
>> using memcpy/memmove/mempcpy.
>>
>> Running your patch against GDB trips on such a case:
>>
>> src/gdb/btrace.h: In function ‘btrace_insn_s* VEC_btrace_insn_s_quick_insert(VEC_btrace_insn_s*, unsigned int, const btrace_insn_s*, const char*, unsigned int)’:
>> src/gdb/common/vec.h:948:62: error: calling ‘void* memmove(void*, const void*, size_t)’ with a pointer to a non-trivial type ‘btrace_insn_s {aka struct btrace_insn}’ [-Werror=non-trivial-memaccess]
>>    memmove (slot_ + 1, slot_, (vec_->num++ - ix_) * sizeof (T));    \
>>                                                               ^
>>
>> There is nothing wrong with the code being warned here.
>> While "struct btrace_insn" is trivial (has a user-provided default
>> ctor), it is still trivially copyable.
>
>
> Any news on getting a "fix" for this issue.  Right now it blocks my
> testing of GCC/gdb because I am building the trunk of both in a CI
> loop and my build is broken due to this warning.  Should I just add
> --disable-werror to my gdb build instead?

I'm not aware of any serious bugs in the warning that need fixing.
The warning points out raw memory accesses to objects of non-trivial
types (among other things), or those with user-defined default or
copy ctors, dtor, or copy assignment operator.  Objects of such
types should be manipulated using these special member functions
rather than by raw memory functions.  In many (though not all(*))
cases the raw memory calls can put.leave such objects in an invalid
state and make using them undefined.

In the instance of the warning above, btrace_insn_s is a non-trivial
type because it has a user-defined default ctor, as a result of
defining a member of such a type (flags, which is of type
enum_flags<enum btrace_insn_flag>).  To avoid the warning either
the memcpy/memmove calls should be replaced with a loop that makes
use of the special function(s), or in C++ 11 and later, the class
made trivial by defaulting the ctors and copy assignment operators.
In the GDB case, this can be done by replacing the defintion of
the enum_flags default ctor like so:

--- a/gdb/common/enum-flags.h
+++ b/gdb/common/enum-flags.h
@@ -116,9 +116,7 @@ private:

  public:
    /* Allow default construction.  */
-  enum_flags ()
-    : m_enum_value ((enum_type) 0)
-  {}
+  enum_flags () = default;

    /* If you get an error saying these two overloads are ambiguous,
       then you tried to mix values of different enum types.  */

In cases where copying such objects using memcpy/memmove is safe
and changing the code to use the special member functions is not
practical the warning can be suppressed by casting the pointer
to char*.

Martin

[*] Whether a call to memcpy or memove to copy an object of
a non-trivial type like btrace_insn_s (i.e., one with a trivial
copy ctor and copy assignment but with a non-trivial default
ctor) is safe depends on whether the destination of the copy
is an already constructed object of the type or one that has
just been allocated but whose lifetime hasn't yet begun (i.e.,
whose ctor has been called yet).  Because there is no way to
tell which of these two it is, the warning makes the conservative
assumption that it's the latter.  This was done deliberately to
help non-expert users, with the expectation that advanced users
will easily either adjust their code or suppress the warning.

^ permalink raw reply	[flat|nested] 46+ messages in thread

end of thread, other threads:[~2017-07-05 22:33 UTC | newest]

Thread overview: 46+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-04-29 22:10 [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560) Martin Sebor
     [not found] ` <alpine.DEB.2.20.1704302338540.1461@digraph.polyomino.org.uk>
2017-05-03 16:18   ` Martin Sebor
     [not found] ` <656ca1db-1082-b1ed-a911-ba7bf48f09c0@redhat.com>
2017-05-01 15:49   ` Jason Merrill
2017-05-11 20:03     ` Martin Sebor
2017-05-12  2:43       ` Martin Sebor
2017-05-17 11:53         ` Pedro Alves
2017-06-29 16:15       ` Jan Hubicka
2017-06-29 20:23         ` Martin Sebor
2017-06-29 22:34           ` Jan Hubicka
2017-06-30  0:16             ` Martin Sebor
2017-06-30  8:34           ` Richard Biener
2017-06-30 14:29             ` Martin Sebor
2017-07-04  9:33         ` Richard Earnshaw (lists)
2017-05-11 16:34   ` Martin Sebor
2017-05-11 16:57     ` Jakub Jelinek
2017-05-11 17:17       ` Martin Sebor
2017-05-16 19:46     ` Jason Merrill
2017-05-16 22:28       ` Martin Sebor
2017-05-19 19:14         ` Jason Merrill
2017-05-19 21:11           ` Martin Sebor
2017-05-19 21:56             ` Jason Merrill
2017-05-22  2:07               ` Martin Sebor
2017-05-22  6:07                 ` Jason Merrill
2017-05-24 20:28                   ` Martin Sebor
2017-05-24 20:48                     ` Martin Sebor
2017-05-24 21:36                       ` Jason Merrill
2017-05-28  5:02                         ` Martin Sebor
     [not found]                           ` <cc62e93c-3b49-8e2f-70b9-acdd013fe760@redhat.com>
2017-06-02 21:28                             ` Martin Sebor
2017-06-05  2:02                               ` Jason Merrill
2017-06-05  7:53                                 ` Jason Merrill
2017-06-05 16:07                                   ` Martin Sebor
2017-06-05 19:13                                     ` Martin Sebor
2017-06-06  1:53                                       ` Martin Sebor
2017-06-06 22:24                                         ` Martin Sebor
2017-06-08  1:09                                           ` Jason Merrill
2017-06-08 20:25                                             ` Martin Sebor
2017-06-12 21:36                                               ` Jason Merrill
2017-06-15 16:26                                                 ` Martin Sebor
2017-06-15 21:31                                                   ` Jason Merrill
2017-06-16  7:38                                                     ` Richard Biener
2017-06-16  7:40                                                       ` Richard Biener
2017-05-17  1:01       ` Pedro Alves
2017-05-17  1:57         ` Martin Sebor
2017-05-17 11:23           ` Pedro Alves
2017-07-05 20:58   ` Andrew Pinski
2017-07-05 22:33     ` Martin Sebor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).