From a324786b4ded9047d05463b4bce9d238b6c6b3ef Mon Sep 17 00:00:00 2001 From: msebor Date: Fri, 16 Jun 2017 03:48:59 +0000 Subject: [PATCH] PR c++/80560 - warn on undefined memory operations involving non-trivial types gcc/c-family/ChangeLog: PR c++/80560 * c.opt (-Wclass-memaccess): New option. gcc/cp/ChangeLog: PR c++/80560 * call.c (first_non_public_field, maybe_warn_class_memaccess): New functions. (has_trivial_copy_assign_p, has_trivial_copy_p): Ditto. (build_cxx_call): Call maybe_warn_class_memaccess. gcc/ChangeLog: PR c++/80560 * dumpfile.c (dump_register): Avoid calling memset to initialize a class with a default ctor. * gcc.c (struct compiler): Remove const qualification. * genattrtab.c (gen_insn_reserv): Replace memset with initialization. * hash-table.h: Ditto. * ipa-cp.c (allocate_and_init_ipcp_value): Replace memset with assignment. * ipa-prop.c (ipa_free_edge_args_substructures): Ditto. * omp-low.c (lower_omp_ordered_clauses): Replace memset with default ctor. * params.h (struct param_info): Make struct members non-const. * tree-switch-conversion.c (emit_case_bit_tests): Replace memset with default initialization. * vec.h (vec_copy_construct, vec_default_construct): New helper functions. (vec::copy, vec::splice, vec::reserve): Replace memcpy with vec_copy_construct. (vect::quick_grow_cleared): Replace memset with default ctor. (vect::vec_safe_grow_cleared, vec_safe_grow_cleared): Same. * doc/invoke.texi (-Wclass-memaccess): Document. libcpp/ChangeLog: PR c++/80560 * line-map.c (line_maps::~line_maps): Avoid calling htab_delete with a null pointer. (linemap_init): Avoid calling memset on an object of a non-trivial type. libitm/ChangeLog: PR c++/80560 * beginend.cc (GTM::gtm_thread::rollback): Avoid calling memset on an object of a non-trivial type. (GTM::gtm_transaction_cp::commit): Use assignment instead of memcpy to copy an object. * method-ml.cc (orec_iterator::reinit): Avoid -Wclass-memaccess. gcc/testsuite/ChangeLog: PR c++/80560 * g++.dg/Wclass-memaccess.C: New test. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@249234 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 24 + gcc/c-family/ChangeLog | 5 + gcc/c-family/c.opt | 4 + gcc/cp/ChangeLog | 8 + gcc/cp/call.c | 391 ++++++++ gcc/doc/invoke.texi | 20 +- gcc/dumpfile.c | 9 +- gcc/gcc.c | 4 +- gcc/genattrtab.c | 4 +- gcc/hash-table.h | 5 +- gcc/ipa-cp.c | 7 +- gcc/ipa-prop.c | 2 +- gcc/omp-low.c | 6 +- gcc/params.h | 4 +- gcc/testsuite/ChangeLog | 5 + gcc/testsuite/g++.dg/Wclass-memaccess.C | 1671 +++++++++++++++++++++++++++++++ gcc/tree-switch-conversion.c | 4 +- gcc/vec.h | 41 +- libcpp/line-map.c | 5 +- libitm/beginend.cc | 6 +- libitm/method-ml.cc | 6 +- 21 files changed, 2196 insertions(+), 35 deletions(-) create mode 100644 gcc/testsuite/g++.dg/Wclass-memaccess.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b8c2f055b47..df4da476a6c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,27 @@ +2017-06-15 Martin Sebor + + 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::copy, vec::splice, vec::reserve): Replace memcpy + with vec_copy_construct. + (vect::quick_grow_cleared): Replace memset with default ctor. + (vect::vec_safe_grow_cleared, vec_safe_grow_cleared): Same. + * doc/invoke.texi (-Wclass-memaccess): Document. + 2017-06-15 Ramana Radhakrishnan * 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 + + PR c++/80560 + * c.opt (-Wclass-memaccess): New option. + 2017-06-14 Boris Kolpackov * 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 + + 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 * 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 % and % instead"); + else if (!trivcopy) + warnfmt = G_("%qD moving an object of type %#qT with deleted copy " + "constructor; use % and % 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, in C++1z calls f @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::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 *val; - val = ipcp_cst_values_pool.allocate (); - memset (val, 0, sizeof (*val)); + val = new (ipcp_cst_values_pool.allocate ()) ipcp_value(); val->value = source; return val; } @@ -1486,8 +1485,8 @@ allocate_and_init_ipcp_value (ipa_polymorphic_call_context source) ipcp_value *val; // TODO - val = ipcp_poly_ctx_values_pool.allocate (); - memset (val, 0, sizeof (*val)); + val = new (ipcp_poly_ctx_values_pool.allocate ()) + ipcp_value(); 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(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 =' 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 + + PR c++/80560 + * g++.dg/Wclass-memaccess.C: New test. + 2017-06-15 Janus Weil 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 + 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 + 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 +inline void +vec_default_construct (T *dst, unsigned n) +{ + for ( ; n; ++dst, --n) + ::new (static_cast(dst)) T (); +} + +/* Copy-construct N elements in DST from *SRC. */ + +template +inline void +vec_copy_construct (T *dst, const T *src, unsigned n) +{ + for ( ; n; ++dst, ++src, --n) + ::new (static_cast(dst)) T (*src); +} + /* Type to provide NULL values for vec. 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 *&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::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::splice (const vec &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::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. */ template @@ -1454,7 +1473,7 @@ vec::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::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) * 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) * L2O_ORECS); } }; -- 2.11.4.GIT