PR c++/80560 - warn on undefined memory operations involving non-trivial types gcc/c-family/ChangeLog: PR c++/80560 * c.opt (-Wnon-trivial-memaccess): New option. gcc/cp/ChangeLog: PR c++/80560 * call.c (first_non_public_field): New function. (maybe_warn_nontrivial_memaccess): Same. (build_cxx_call): Call it. * cp-tree.h (has_trivial_assign, has_trivial_copy): Declare. * method.c (has_trivial_assign, has_trivial_copy): Define. gcc/ChangeLog: PR c++/80560 * 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 (-Wnon-trivial-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 -Wnon-trivial-memaccess. gcc/testsuite/ChangeLog: PR c++/80560 * g++.dg/Wnon-trivial-memaccess.C: New test. diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 6ecbfca..c32f8a9 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -792,6 +792,10 @@ Wnon-template-friend C++ ObjC++ Var(warn_nontemplate_friend) Init(1) Warning Warn when non-templatized friend functions are declared within a template. +Wnon-trivial-memaccess +C++ ObjC++ Var(warn_nontrival_memaccess) Warning LangEnabledBy(C++ ObjC++, Wall) +Warn for raw memory writes to objects of non-trivial types. + Wnon-virtual-dtor C++ ObjC++ Var(warn_nonvdtor) Warning LangEnabledBy(C++ ObjC++,Weffc++) Warn about non-virtual destructors. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index c15b8e4..4bf984d 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -8152,6 +8152,245 @@ 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; + } + + return NULL_TREE; +} + +/* Return true if class TYPE meets a relaxed definition of standard-layout. + In particular, the requirement that it "has all non-static data members + nd bit-fields in the class and its base classes first declared in the + same class" is not considered. */ + +static bool +almost_std_layout_p (tree type, tree *field) +{ + if (!CLASS_TYPE_P (type)) + return true; + + if (!trivial_type_p (type)) + return false; + + if (tree fld = first_non_public_field (type)) + if (!*field) + *field = fld; + + tree binfo, base_binfo; + + int i; + + for (binfo = TYPE_BINFO (type), i = 0; + BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) + { + tree base = TREE_TYPE (base_binfo); + + if (!almost_std_layout_p (base, field)) + return false; + } + + return true; +} + +/* Issue a warning on a call to the built-in function FNDECL if it is + a memory write whose destination is not an object of (something like) + trivial or standard layout type. Detects const correctness violations, + corrupting references, virtual table pointers, and bypassing non- + trivial assignments. */ + +static void +maybe_warn_nontrivial_memaccess (location_t loc, tree fndecl, tree *args) +{ + /* The destination pointer is always first argument for all functions + handled here. */ + tree dest = args[0]; + 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 class. */ + tree desttype = TREE_TYPE (TREE_TYPE (dest)); + + if (!desttype || !COMPLETE_TYPE_P (desttype)) + return; + + /* Check to see if the memcpy/memset 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; + } + + tree fld = NULL_TREE; + bool stdlayout = almost_std_layout_p (desttype, &fld); + bool trivial = trivial_type_p (desttype); + bool trivassign = has_trivial_assign (desttype); + bool trivcopy = has_trivial_copy (desttype); + + /* Warn about raw memory operations whose destination is an object + of a non-trivial type because they are undefined. */ + + const char *warnfmt = NULL; + bool warned = false; + + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_MEMSET: + if (!integer_zerop (args[1])) + { + /* Diagnose setting non-copy-assignable, non-trivial or non- + standard layout objects (in that order, since the latter + two are subsets of one another), to (potentially) non-zero + bytes. Since the value of the bytes being written is unknown + suggest using assignment instead (if one exists). */ + if (!trivassign) + warnfmt = G_("%qD writing to an object of type %#qT without " + "trivial copy assignment"); + else if (!trivial) + warnfmt = G_("%qD writing to an object of non-trivial " + "type %#qT; use assignment instead"); + else if (!stdlayout) + warnfmt = G_("%qD writing to an object of non-standard-layout " + "type %#qT; use assignment instead"); + else if (fld) + { + const char *access = TREE_PRIVATE (fld) ? "private" : "protected"; + warned = warning_at (loc, OPT_Wnon_trivial_memaccess, + "%qD writing to an object of type %#qT with " + "%qs member %qD; use assignment instead", + fndecl, desttype, access, fld); + } + 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. */ + if (!trivassign) + warnfmt = (trivcopy + ? G_("%qD clearing an object of type %#qT without trivial " + "copy assignment; use copy constructor instead") + : G_("%qD clearing an object of type %#qT without trivial " + "copy assignment")); + else if (!trivial) + warnfmt = G_("%qD clearing an object of non-trivial type " + "%#qT; use assignment or default initialization instead"); + else if (!stdlayout) + warnfmt = G_("%qD clearing an object of non-standard-layout type " + "%#qT; use assignment or default initialization instead"); + else if (fld) + { + const char *access = TREE_PRIVATE (fld) ? "private" : "protected"; + warned = warning_at (loc, OPT_Wnon_trivial_memaccess, + "%qD clearing an object of type %#qT with " + "%qs member %qD; use assignment or default " + "initialization instead", + fndecl, desttype, access, fld); + } + break; + + case BUILT_IN_MEMCPY: + case BUILT_IN_MEMMOVE: + case BUILT_IN_MEMPCPY: + /* Determine the type of the source object. */ + srctype = (TREE_CODE (args[1]) == NOP_EXPR + ? TREE_OPERAND (args[1], 0) : args[1]); + + srctype = TREE_TYPE (TREE_TYPE (srctype)); + + if (!trivassign) + warnfmt = (trivcopy + ? G_("%qD copying to an object of type %#qT without trivial " + "copy assignment; use copy constructor instead") + : G_("%qD copying to an object of type %#qT without trivial " + "copy assignment")); + else if (!trivially_copyable_p (desttype)) + warnfmt = G_("%qD copying to an object of non-trivially-copyable type " + "%#qT; use assignment or copy initialization instead"); + 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_Wnon_trivial_memaccess, + "%qD copying an object of non-trivial type " + "%#qT from an array of %#qT", + fndecl, desttype, srctype); + } + else if (fld) + { + const char *access = TREE_PRIVATE (fld) ? "private" : "protected"; + warned = warning_at (loc, OPT_Wnon_trivial_memaccess, + "%qD clearing an object of type %#qT with " + "%qs member %qD; use assignment or default " + "initialization instead", + fndecl, desttype, access, fld); + } + 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 nbytes = tree_to_uhwi (args[2]) % typesize) + warned = warning_at (loc, OPT_Wnon_trivial_memaccess, + (nbytes > 1 + ? G_("%qD copying to an object of " + "a non-trivial type %#qT leaves %wu " + "byte unchanged") + : G_("%qD copying to an object of " + "a non-trivial type %#qT leaves %wu " + "bytes unchanged")), + fndecl, desttype, nbytes); + } + break; + + default: + return; + } + + if (!warned && !warnfmt) + return; + + if (warned + || warning_at (loc, OPT_Wnon_trivial_memaccess, warnfmt, fndecl, desttype)) + 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. */ @@ -8184,6 +8423,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_nontrivial_memaccess (loc, fndecl, argarray); } /* If it is a built-in array notation function, then the return type of diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 3bbc0bf..b7ca930 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6119,6 +6119,9 @@ extern bool ctor_omit_inherited_parms (tree); extern tree locate_ctor (tree); extern tree implicitly_declare_fn (special_function_kind, tree, bool, tree, tree); +extern bool has_trivial_assign (tree); +extern bool has_trivial_copy (tree); + /* In optimize.c */ extern bool maybe_clone_body (tree); diff --git a/gcc/cp/method.c b/gcc/cp/method.c index b4c1f60..810119e 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -1716,6 +1716,44 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, --c_inhibit_evaluation_warnings; } +/* Return true if the class type CTYPE has non-deleted trivial copy + assignment. */ + +bool +has_trivial_assign (tree ctype) +{ + if (!CLASS_TYPE_P (ctype)) + return true; + + bool trivial_p = false; + bool deleted_p = false; + + synthesized_method_walk (ctype, sfk_copy_assignment, false, NULL, + &trivial_p, &deleted_p, + NULL, false, NULL_TREE, NULL_TREE); + + return trivial_p && !deleted_p; +} + +/* Return true if the class type CTYPE has non-deleted trivial copy + constructor. */ + +bool +has_trivial_copy (tree ctype) +{ + if (!CLASS_TYPE_P (ctype)) + return true; + + bool trivial_p = false; + bool deleted_p = false; + + synthesized_method_walk (ctype, sfk_copy_constructor, false, NULL, + &trivial_p, &deleted_p, + NULL, false, NULL_TREE, NULL_TREE); + + return trivial_p && !deleted_p; +} + /* DECL is a defaulted function whose exception specification is now needed. Return what it should be. */ diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 0eeea7b..9ec53a3 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 -Wnon-trivial-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 @@ -2911,6 +2912,24 @@ void g() noexcept; void h() @{ f(g); @} // in C++14 calls f, in C++1z calls f @end smallexample +@item -Wnon-trivial-memaccess @r{(C++ and Objective-C++ only)} +@opindex Wnon-trivial-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 where doing +so might bypass the class non-trivial 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 +non-trivial types is by using the appropriate constructor. +@smallexample +std::string str = "abc"; +memset (&str, 0, 3); +@end smallexample +The option does not warn on safe calls to such functions +The @option{-Wnon-trivial-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/gcc.c b/gcc/gcc.c index 120c5c0..2872b2a 100644 --- a/gcc/gcc.c +++ b/gcc/gcc.c @@ -1259,9 +1259,9 @@ struct compiler const char *cpp_spec; /* If non-NULL, substitute this spec for `%C', rather than the usual cpp_spec. */ - const int combinable; /* If nonzero, compiler can deal with + int combinable; /* If nonzero, compiler can deal with multiple source files at once (IMA). */ - const int needs_preprocessing; /* If nonzero, source files need to + int needs_preprocessing; /* If nonzero, source files need to be run through a preprocessor. */ }; diff --git a/gcc/genattrtab.c b/gcc/genattrtab.c index 3629b5f..51dfe77 100644 --- a/gcc/genattrtab.c +++ b/gcc/genattrtab.c @@ -4703,8 +4703,8 @@ gen_insn_reserv (md_rtx_info *info) struct insn_reserv *decl = oballoc (struct insn_reserv); rtx def = info->def; - struct attr_desc attr; - memset (&attr, 0, sizeof (attr)); + struct attr_desc attr = { }; + attr.name = DEF_ATTR_STRING (XSTR (def, 0)); attr.loc = info->loc; diff --git a/gcc/hash-table.h b/gcc/hash-table.h index 0f7e21a..443d16c 100644 --- a/gcc/hash-table.h +++ b/gcc/hash-table.h @@ -803,7 +803,10 @@ hash_table::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 26ae8fc..f232d57 100644 --- a/gcc/ipa-cp.c +++ b/gcc/ipa-cp.c @@ -1472,8 +1472,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; } @@ -1487,8 +1486,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 b3d5159..8c912b4 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -3731,7 +3731,7 @@ void ipa_free_edge_args_substructures (struct ipa_edge_args *args) { vec_free (args->jump_functions); - memset (args, 0, sizeof (*args)); + *args = ipa_edge_args (); } /* Free all ipa_edge structures. */ diff --git a/gcc/omp-low.c b/gcc/omp-low.c index 9cc2996..dc93dce 100644 --- a/gcc/omp-low.c +++ b/gcc/omp-low.c @@ -6292,7 +6292,11 @@ lower_omp_ordered_clauses (gimple_stmt_iterator *gsi_p, gomp_ordered *ord_stmt, return; wide_int *folded_deps = XALLOCAVEC (wide_int, 2 * len - 1); - memset (folded_deps, 0, sizeof (*folded_deps) * (2 * len - 1)); + + /* wide_int is not a POD so it must be default-constructed. */ + for (unsigned i = 0; i != 2 * len - 1; ++i) + new (static_cast(folded_deps + i)) wide_int (); + tree folded_dep = NULL_TREE; /* TRUE if the first dimension's offset is negative. */ bool neg_offset_p = false; diff --git a/gcc/params.h b/gcc/params.h index b61cff9..8b91660 100644 --- a/gcc/params.h +++ b/gcc/params.h @@ -42,7 +42,7 @@ struct param_info { /* The name used with the `--param =' switch to set this value. */ - const char *const option; + const char *option; /* The default value. */ int default_value; @@ -54,7 +54,7 @@ struct param_info int max_value; /* A short description of the option. */ - const char *const help; + const char *help; /* The optional names corresponding to the values. */ const char **value_names; diff --git a/gcc/testsuite/g++.dg/Wnon-trivial-memaccess.C b/gcc/testsuite/g++.dg/Wnon-trivial-memaccess.C new file mode 100644 index 0000000..dc93c9b --- /dev/null +++ b/gcc/testsuite/g++.dg/Wnon-trivial-memaccess.C @@ -0,0 +1,571 @@ +/* PR c++/80560 - warn on undefined memory operations involving non-trivial + types + { dg-do compile } + { dg-options "-Wnon-trivial-memaccess -ftrack-macro-expansion=0" } */ + +typedef __SIZE_TYPE__ size_t; + +extern "C" +{ +void bzero (void*, size_t); +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); +} + +/* Trivial can be manipulated by raw memory functions. */ +struct Trivial { int i; char *s; char a[4]; }; + +/* HasDefaultCtor is trivially copyable but should be initialized by + the ctor, not bzero or memset. */ +struct HasDefaultCtor { HasDefaultCtor (); }; + +/* HasCopyCtor should be copied using the copy ctor or assignment, not + by memcpy or memmove. Since it's non-trivial, i should not be zeroed + out by bzero/memset either and should instead use assignment and/or + default initialization. */ +struct HasCopyCtor { HasCopyCtor (const HasCopyCtor&); }; + +/* HasDtor should be initialized using aggregate or memberwise intialization, + not bzero or memset. */ +struct HasDtor { ~HasDtor (); }; + +/* HasAssign should be copied using the copy ctor or assignment, not + by memcpy or memmove. */ +struct HasAssign { void operator= (HasAssign&); }; + +/* 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 { virtual void foo (); }; + +/* 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 int ci; }; + +/* 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; }; + +/* HasSomePrivateDate and HasSomeProtectedData should only be initialized + using aggregate initializatoon and not cleared by bzero, or copied into + using memcpy. Doing otherwise would break encapsulation. */ +struct HasSomePrivateData { int i; private: int j; }; +struct HasSomeProtectedData { int i; protected: int j; }; + +/* Similarly to the above, HasAllPrivateDate and HasAllProtectedData should + only be initialized using aggregate initializatoon and not cleared by + bzero, or copied into using memcpy. They are tested separately because + unlike the former classes, these are standard layout. */ +struct HasAllPrivateData { private: int i; }; +struct HasAllProtectedData { protected: int i; }; + +void sink (void*); + +#define T(fn, arglist) (fn arglist, sink (p)) + +#if !defined TEST || TEST == TEST_TRIVIAL + +void test (Trivial *p, void *q, int x) +{ + T (bzero, (p, sizeof *p)); + T (bzero, (q, sizeof *p)); + + T (memcpy, (p, q, sizeof *p)); + T (memcpy, (q, p, sizeof *p)); + + T (memset, (p, 0, sizeof *p)); + T (memset, (q, 0, sizeof *p)); + + T (memset, (p, x, sizeof *p)); + T (memset, (q, x, sizeof *p)); + + T (memmove, (p, q, sizeof *p)); + T (memmove, (q, p, sizeof *p)); +} + +#endif + +#if !defined TEST || TEST == TEST_HAS_DEFAULT_CTOR + +void test (HasDefaultCtor *p, const HasDefaultCtor &x, + const void *q, const unsigned char *s, const int ia[]) +{ + const int i = *ia; + + // HasDefaultCtor 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 ".void bzero(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefaultCtor.; use assignment or default initialization instead" } + + T (memset, (p, 0, sizeof *p)); // { dg-warning ".void\\* memset(\[^\n\r\]*). clearing an object of non-trivial type .struct HasDefaultCtor.; use assignment or default initialization instead" } + + T (memset, (p, i, sizeof *p)); // { dg-warning ".void\\* memset(\[^\n\r\]*). writing to an object of non-trivial type .struct HasDefaultCtor.; use assignment instead" } + + + // Copying from another object of the same type is fine. + T (memcpy, (p, &x, sizeof *p)); + T (memcpy, (p, &x, sizeof *p * i)); + + // Copying from a void* or character buffer is also fine. + T (memcpy, (p, q, sizeof *p)); + T (memcpy, (p, q, sizeof *p * i)); + T (memcpy, (p, s, sizeof *p)); + T (memcpy, (p, s, sizeof *p * i)); + + T (memmove, (p, q, sizeof *p)); + T (memmove, (p, q, sizeof *p * i)); + T (memmove, (p, s, sizeof *p)); + T (memmove, (p, s, sizeof *p * i)); + + T (mempcpy, (p, q, sizeof *p)); + T (mempcpy, (p, q, sizeof *p * i)); + T (mempcpy, (p, s, sizeof *p)); + T (mempcpy, (p, s, sizeof *p * i)); + + // 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 HasDefaultCtor. from an array of .const int." } + + T (memmove, (p, ia, sizeof *p)); // { dg-warning ".void\\* memmove(\[^\n\r\]*). copying an object of non-trivial type .struct HasDefaultCtor. 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 HasDefaultCtor. from an array of .const int." } +} + +#endif + +#if !defined TEST || TEST == TEST_HAS_COPY_CTOR + +void test (HasCopyCtor *p, const HasCopyCtor &x, + const void *q, const unsigned char *s, const int ia[]) +{ + const int i = *ia; + + // Zeroing out is diagnosed because default initialization is + // invalid (the copy ctor makes no default ctor unavailable). + T (bzero, (p, sizeof *p)); // { dg-warning "bzero" } + T (memset, (p, 0, sizeof *p)); // { dg-warning "memset" } + T (memset, (p, 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" } +} + +#endif + +#if !defined TEST || TEST == TEST_HAS_DTOR + +void test (HasDtor *p, const HasDtor &x, + const void *q, const unsigned char *s, const int ia[]) +{ + const int i = *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 + // default-initializing it does). + T (bzero, (p, sizeof *p)); // { dg-warning "bzero" } + T (memset, (p, 0, 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" } +} + +#endif + +#if !defined TEST || TEST == TEST_HAS_ASSIGN + +void test (HasAssign *p, const HasAssign &x, + const void *q, const unsigned char *s, const int ia[]) +{ + const int i = *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, 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" } +} + +#endif + +#if !defined TEST || TEST == TEST_HAS_VIRTUALS + +void test (HasVirtuals *p, const HasVirtuals &x, + const void *q, const unsigned char *s, const int ia[]) +{ + const int i = *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, 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" } +} + +#endif + +#if !defined TEST || TEST == TEST_HAS_CONST_DATA + +void test (HasConstData *p, const HasConstData &x, + const void *q, const unsigned char *s, const int ia[]) +{ + const int i = *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. + T (bzero, (p, sizeof *p)); // { dg-warning "clearing an object of type .struct HasConstData. without trivial copy assignment" } + T (memset, (p, 0, 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 "copying to an object of type .struct HasConstData. without trivial copy assignment" } + 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" } +} + +#endif + +#if !defined TEST || TEST == TEST_HAS_REFERENCE + +void test (HasReference *p, const HasReference &x, + const void *q, const unsigned char *s, const int ia[]) +{ + const int i = *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. + T (bzero, (p, sizeof *p)); // { dg-warning "clearing an object of type .struct HasReference. without trivial copy assignment" } + T (memset, (p, 0, 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 "copying to an object of type .struct HasReference. without trivial copy assignment" } + 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" } +} + +#endif + +#if !defined TEST || TEST == TEST_HAS_SOME_PRIVATE_DATA + +void test (HasSomePrivateData *p, const HasSomePrivateData &x, + const void *q, const unsigned char *s, const int ia[]) +{ + const int i = *ia; + + // All operations are diagnosed becauce they break encapsulation. + T (bzero, (p, sizeof *p)); // { dg-warning "bzero" } + T (memset, (p, 0, sizeof *p)); // { dg-warning "memset" } + T (memset, (p, i, sizeof *p)); // { dg-warning "memset" } + + 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" } +} + +#endif + +#if !defined TEST || TEST == TEST_HAS_SOME_PROTECTED_DATA + +void test (HasSomeProtectedData *p, const HasSomeProtectedData &x, + const void *q, const unsigned char *s, const int ia[]) +{ + const int i = *ia; + + // All operations are diagnosed becauce they break encapsulation. + T (bzero, (p, sizeof *p)); // { dg-warning "bzero" } + T (memset, (p, 0, sizeof *p)); // { dg-warning "memset" } + T (memset, (p, i, sizeof *p)); // { dg-warning "memset" } + + 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" } +} + +#endif + +#if !defined TEST || TEST == TEST_HAS_ALL_PRIVATE_DATA + +void test (HasAllPrivateData *p, const HasAllPrivateData &x, + const void *q, const unsigned char *s, const int ia[]) +{ + const int i = *ia; + + // All operations are diagnosed becauce they break encapsulation. + T (bzero, (p, sizeof *p)); // { dg-warning "bzero" } + T (memset, (p, 0, sizeof *p)); // { dg-warning "memset" } + T (memset, (p, i, sizeof *p)); // { dg-warning "memset" } + + 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" } +} + +#endif + +#if !defined TEST || TEST == TEST_HAS_ALL_PROTECTED_DATA + +void test (HasAllProtectedData *p, const HasAllProtectedData &x, + const void *q, const unsigned char *s, const int ia[]) +{ + const int i = *ia; + + // All operations are diagnosed becauce they break encapsulation. + T (bzero, (p, sizeof *p)); // { dg-warning "bzero" } + T (memset, (p, 0, sizeof *p)); // { dg-warning "memset" } + T (memset, (p, i, sizeof *p)); // { dg-warning "memset" } + + 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" } +} + +#endif + +#if !defined TEST || TEST == TEST_EXPRESSION + +void test_expr (int i) +{ + struct TestClass: HasDefaultCtor { }; + TestClass a, b; + + static void *p; + + T (bzero, (i < 0 ? &a : &b, 1)); // { dg-warning ".void 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: HasDefaultCtor + { + 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: HasDefaultCtor + { + ~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 diff --git a/gcc/tree-switch-conversion.c b/gcc/tree-switch-conversion.c index 1ccb4bd..d8b1c67 100644 --- a/gcc/tree-switch-conversion.c +++ b/gcc/tree-switch-conversion.c @@ -266,7 +266,7 @@ static void emit_case_bit_tests (gswitch *swtch, tree index_expr, tree minval, tree range, tree maxval) { - struct case_bit_test test[MAX_CASE_BIT_TESTS]; + struct case_bit_test test[MAX_CASE_BIT_TESTS] = { }; unsigned int i, j, k; unsigned int count; @@ -291,8 +291,6 @@ emit_case_bit_tests (gswitch *swtch, tree index_expr, int prec = TYPE_PRECISION (word_type_node); wide_int wone = wi::one (prec); - memset (&test, 0, sizeof (test)); - /* Get the edge for the default case. */ tmp = gimple_switch_default_label (swtch); default_bb = label_to_block (CASE_LABEL (tmp)); diff --git a/gcc/vec.h b/gcc/vec.h index fee4616..0e9a13b 100644 --- a/gcc/vec.h +++ b/gcc/vec.h @@ -407,6 +407,26 @@ struct GTY((user)) vec { }; +/* Default-construct N elements in DST. */ + +template +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 @@ -615,7 +635,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); } @@ -821,7 +841,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; } @@ -838,7 +858,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; } } @@ -1092,13 +1112,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 @@ -1445,7 +1464,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; } @@ -1607,10 +1626,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 949489e..4e36e38 100644 --- a/libcpp/line-map.c +++ b/libcpp/line-map.c @@ -62,7 +62,8 @@ extern unsigned num_macro_tokens_counter; line_maps::~line_maps () { - htab_delete (location_adhoc_data_map.htab); + if (location_adhoc_data_map.htab) + htab_delete (location_adhoc_data_map.htab); } /* Hash function for location_adhoc_data hashtable. */ @@ -347,7 +348,7 @@ void linemap_init (struct line_maps *set, source_location builtin_location) { - memset (set, 0, sizeof (struct line_maps)); + *set = line_maps (); set->highest_location = RESERVED_LOCATION_COUNT - 1; set->highest_line = RESERVED_LOCATION_COUNT - 1; set->location_adhoc_data_map.htab = diff --git a/libitm/beginend.cc b/libitm/beginend.cc index d04f3e9..c6550a3 100644 --- a/libitm/beginend.cc +++ b/libitm/beginend.cc @@ -431,7 +431,7 @@ GTM::gtm_transaction_cp::save(gtm_thread* tx) // Save everything that we might have to restore on restarts or aborts. jb = tx->jb; undolog_size = tx->undolog.size(); - memcpy(&alloc_actions, &tx->alloc_actions, sizeof(alloc_actions)); + alloc_actions = tx->alloc_actions; user_actions_size = tx->user_actions.size(); id = tx->id; prop = tx->prop; @@ -449,7 +449,7 @@ GTM::gtm_transaction_cp::commit(gtm_thread* tx) // commits of nested transactions. Allocation actions must be committed // before committing the snapshot. tx->jb = jb; - memcpy(&tx->alloc_actions, &alloc_actions, sizeof(alloc_actions)); + tx->alloc_actions = alloc_actions; tx->id = id; tx->prop = prop; } @@ -485,7 +485,7 @@ GTM::gtm_thread::rollback (gtm_transaction_cp *cp, bool aborting) prop = cp->prop; if (cp->disp != abi_disp()) set_abi_disp(cp->disp); - memcpy(&alloc_actions, &cp->alloc_actions, sizeof(alloc_actions)); + alloc_actions = cp->alloc_actions; nesting = cp->nesting; } else diff --git a/libitm/method-ml.cc b/libitm/method-ml.cc index fcae334..b857bff 100644 --- a/libitm/method-ml.cc +++ b/libitm/method-ml.cc @@ -138,7 +138,11 @@ struct ml_mg : public method_group // This store is only executed while holding the serial lock, so relaxed // memory order is sufficient here. Same holds for the memset. time.store(0, memory_order_relaxed); - memset(orecs, 0, sizeof(atomic) * 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); } };