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