This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] warn on mem calls modifying objects of non-trivial types (PR 80560)


On 06/05/2017 01:13 PM, Martin Sebor wrote:
On 06/05/2017 10:07 AM, Martin Sebor wrote:
Maybe I should use a different approach and instead of trying
to see if a function is deleted use trivially_xible to see if
it's usable.  That will mean changing the diagnostics from
"with a deleted special function" to "without trivial special
function" but it will avoid calling synthesized_method_walk
while still avoiding giving bogus suggestions.

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

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

E.g., given:

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

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

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

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

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

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

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

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

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

Attached is an update that implements this simplified approach.

Martin

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

gcc/c-family/ChangeLog:

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

gcc/cp/ChangeLog:

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

gcc/ChangeLog:

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

libcpp/ChangeLog:

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

libitm/ChangeLog:

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

gcc/testsuite/ChangeLog:

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

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

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]