From c3684b7b86da9b6b01f6fb274227fc6401df053e Mon Sep 17 00:00:00 2001
From: Martin Sebor <msebor@redhat.com>
Date: Fri, 16 Jun 2017 03:48:59 +0000
Subject: [PATCH] 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.

From-SVN: r249234
---
 gcc/ChangeLog                           |   24 +
 gcc/c-family/ChangeLog                  |    5 +
 gcc/c-family/c.opt                      |    4 +
 gcc/cp/ChangeLog                        |    8 +
 gcc/cp/call.c                           |  391 ++++++
 gcc/doc/invoke.texi                     |   20 +-
 gcc/dumpfile.c                          |    9 +-
 gcc/gcc.c                               |    4 +-
 gcc/genattrtab.c                        |    4 +-
 gcc/hash-table.h                        |    5 +-
 gcc/ipa-cp.c                            |    7 +-
 gcc/ipa-prop.c                          |    2 +-
 gcc/omp-low.c                           |    6 +-
 gcc/params.h                            |    4 +-
 gcc/testsuite/ChangeLog                 |    5 +
 gcc/testsuite/g++.dg/Wclass-memaccess.C | 1671 +++++++++++++++++++++++
 gcc/tree-switch-conversion.c            |    4 +-
 gcc/vec.h                               |   41 +-
 libcpp/line-map.c                       |    5 +-
 libitm/beginend.cc                      |    6 +-
 libitm/method-ml.cc                     |    6 +-
 21 files changed, 2196 insertions(+), 35 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/Wclass-memaccess.C

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index b8c2f055b474..df4da476a6c7 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,27 @@
+2017-06-15  Martin Sebor  <msebor@redhat.com>
+
+	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.
+
 2017-06-15  Ramana Radhakrishnan  <ramana.radhakrishnan@arm.com>
 
 	* emit-rtl.h (is_leaf): Update comment about local
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index 1eba1c6533a6..e8d1d57ae91d 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,8 @@
+2017-06-15  Martin Sebor  <msebor@redhat.com>
+
+	PR c++/80560
+	* c.opt (-Wclass-memaccess): New option.
+
 2017-06-14  Boris Kolpackov  <boris@codesynthesis.com>
 
 	* c-opts.c (c_common_finish): Handle '-' special value to -MF.
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 37bb236eb975..363d10433971 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/ChangeLog b/gcc/cp/ChangeLog
index b933392d5839..ed307b6278e0 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,11 @@
+2017-06-15  Martin Sebor  <msebor@redhat.com>
+
+	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.
+
 2017-06-14  Jakub Jelinek  <jakub@redhat.com>
 
 	* cp-gimplify.c (cp_genericize_r): Turn most of the function
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index ef9968340c6e..9c3f1eb2b2bc 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8184,6 +8184,393 @@ 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 = copy_fn_p (f);
+
+      /* 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;
+
+      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 default 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.  */
@@ -8216,6 +8603,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 653bc0767592..d7027aa1b651 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 c746d0b24fff..6c55f05b6773 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -187,9 +187,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 3292532996b8..6d724b25423f 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 3629b5fa4862..51dfe77401e8 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 0f7e21a2cc5e..443d16c1837a 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 3c9c3f29ee0e..c7e3c7107ca9 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 c73ffd7b5866..292f3e214def 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 9a1624851c37..dd4a0926c448 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 b61cff94487b..8b916607f163 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/ChangeLog b/gcc/testsuite/ChangeLog
index 34b6a9d9f1f0..7e48992478b1 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2017-06-15  Martin Sebor  <msebor@redhat.com>
+
+	PR c++/80560
+	* g++.dg/Wclass-memaccess.C: New test.
+
 2017-06-15  Janus Weil  <janus@gcc.gnu.org>
 
 	PR fortran/80983
diff --git a/gcc/testsuite/g++.dg/Wclass-memaccess.C b/gcc/testsuite/g++.dg/Wclass-memaccess.C
new file mode 100644
index 000000000000..4783438888e2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/Wclass-memaccess.C
@@ -0,0 +1,1671 @@
+/* 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).  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" }
+
+  // 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_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
+   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 66db20fe7bc6..72927bf0c340 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 755a1f8356bc..cbdd439571b9 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 5caaf6b2d684..694137a73604 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 d04f3e9333ef..c6550a38627b 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 fcae334c385a..b857bff59fe5 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);
   }
 };
 
-- 
GitLab