PR c++/80560 - warn on undefined memory operations involving non-trivial types
authorMartin Sebor <msebor@redhat.com>
Fri, 16 Jun 2017 03:48:59 +0000 (03:48 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Fri, 16 Jun 2017 03:48:59 +0000 (21:48 -0600)
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

21 files changed:
gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c.opt
gcc/cp/ChangeLog
gcc/cp/call.c
gcc/doc/invoke.texi
gcc/dumpfile.c
gcc/gcc.c
gcc/genattrtab.c
gcc/hash-table.h
gcc/ipa-cp.c
gcc/ipa-prop.c
gcc/omp-low.c
gcc/params.h
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/Wclass-memaccess.C [new file with mode: 0644]
gcc/tree-switch-conversion.c
gcc/vec.h
libcpp/line-map.c
libitm/beginend.cc
libitm/method-ml.cc

index b8c2f055b474e9f5b42c6c6301f8fef326cc6aa3..df4da476a6c7cfa35a919f39484fe46cc65f7c2b 100644 (file)
@@ -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
index 1eba1c6533a6268c59f11d0d717df0c2bfb96093..e8d1d57ae91d0365b2484b3da71739977897919c 100644 (file)
@@ -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.
index 37bb236eb9757d7d5403ab084025e9931a8e2cde..363d10433971c204acb0e1ca13e96428d8a8e478 100644 (file)
@@ -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.
index b933392d58391e5afe18be6ac1b905a828c8cb03..ed307b6278e0426257829b35f3cbb971ad36a440 100644 (file)
@@ -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
index ef9968340c6ea42e4d901f46c96c49dfbc6ecc05..9c3f1eb2b2bc93d78cda3b510169c3d2a74b462f 100644 (file)
@@ -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
index 653bc076759203c6b6399f5f54d4153824fdc294..d7027aa1b651609a978b073b372ae205a6c0180a 100644 (file)
@@ -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
index c746d0b24fffd57fcb005637e8bb350c27f2f5d3..6c55f05b677350e209f4fa531df932c98a1bdafb 100644 (file)
@@ -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;
index 3292532996b88810ca1858554d316d52f55835bd..6d724b25423fa0fba18e65a798293e1216e8a635 100644 (file)
--- 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.  */
 };
 
index 3629b5fa4862c4c63dd4da192315e6075d9c5a76..51dfe77401e8eac474b60935ad22063e281ff3b7 100644 (file)
@@ -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;
 
index 0f7e21a2cc5e4c22e0f608c8f2ba932d69e5fae2..443d16c1837a9b0885a3a209d5be15be34d0717b 100644 (file)
@@ -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;
 }
index 3c9c3f29ee0eb8895e295a1371448bf3a184e028..c7e3c7107ca9d3a0c3aeed263ea7f3087651e926 100644 (file)
@@ -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;
 }
index c73ffd7b5866015d1e233c7aef1311376c53b526..292f3e214def342edcdfa4ba5f3f0f858cf3b8e3 100644 (file)
@@ -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.  */
index 9a1624851c3762411cca91f1d1e35fc09f5b1cc0..dd4a0926c448a729bce204811137df2ec3382f30 100644 (file)
@@ -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;
index b61cff94487b4f6d91e0ccc608a57c7f336c0ada..8b916607f163e3701a167405c8babb55c7ad2fb2 100644 (file)
@@ -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;
index 34b6a9d9f1f096bfcc31480a86b7e33fe2a39ac3..7e48992478b1b397d1d68f26456b1cdbde191469 100644 (file)
@@ -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 (file)
index 0000000..4783438
--- /dev/null
@@ -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" }
index 66db20fe7bc6fa4afe25e094de522fc232faae86..72927bf0c340b691bd126c7fddfb3cc1c10e30ac 100644 (file)
@@ -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));
index 755a1f8356bc1b24c0e9c282ab63a6304f7881f3..cbdd439571b97fe920d121332514557c72e4852c 100644 (file)
--- 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);
 }
 
 
index 5caaf6b2d68472c29a680ee1dfe7d3455a44910c..694137a736043b4029dd3be8fcb2d47beb0035cd 100644 (file)
@@ -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 =
index d04f3e9333efcf315007e7737e75bacd2482ed79..c6550a38627b4c3ed1e8620a2ccd71f9920943e3 100644 (file)
@@ -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
index fcae334c385a2a03ea501caac6c1dc102737ba4b..b857bff59fe593d276981b722d95f196a34bc84c 100644 (file)
@@ -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);
   }
 };