mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-11-23 19:03:59 +08:00
PR c++/80560 - warn on undefined memory operations involving non-trivial types
gcc/c-family/ChangeLog: PR c++/80560 * c.opt (-Wclass-memaccess): New option. gcc/cp/ChangeLog: PR c++/80560 * call.c (first_non_public_field, maybe_warn_class_memaccess): New functions. (has_trivial_copy_assign_p, has_trivial_copy_p): Ditto. (build_cxx_call): Call maybe_warn_class_memaccess. gcc/ChangeLog: PR c++/80560 * dumpfile.c (dump_register): Avoid calling memset to initialize a class with a default ctor. * gcc.c (struct compiler): Remove const qualification. * genattrtab.c (gen_insn_reserv): Replace memset with initialization. * hash-table.h: Ditto. * ipa-cp.c (allocate_and_init_ipcp_value): Replace memset with assignment. * ipa-prop.c (ipa_free_edge_args_substructures): Ditto. * omp-low.c (lower_omp_ordered_clauses): Replace memset with default ctor. * params.h (struct param_info): Make struct members non-const. * tree-switch-conversion.c (emit_case_bit_tests): Replace memset with default initialization. * vec.h (vec_copy_construct, vec_default_construct): New helper functions. (vec<T>::copy, vec<T>::splice, vec<T>::reserve): Replace memcpy with vec_copy_construct. (vect<T>::quick_grow_cleared): Replace memset with default ctor. (vect<T>::vec_safe_grow_cleared, vec_safe_grow_cleared): Same. * doc/invoke.texi (-Wclass-memaccess): Document. libcpp/ChangeLog: PR c++/80560 * line-map.c (line_maps::~line_maps): Avoid calling htab_delete with a null pointer. (linemap_init): Avoid calling memset on an object of a non-trivial type. libitm/ChangeLog: PR c++/80560 * beginend.cc (GTM::gtm_thread::rollback): Avoid calling memset on an object of a non-trivial type. (GTM::gtm_transaction_cp::commit): Use assignment instead of memcpy to copy an object. * method-ml.cc (orec_iterator::reinit): Avoid -Wclass-memaccess. gcc/testsuite/ChangeLog: PR c++/80560 * g++.dg/Wclass-memaccess.C: New test. From-SVN: r249234
This commit is contained in:
parent
6a382041dd
commit
c3684b7b86
@ -1,3 +1,27 @@
|
||||
2017-06-15 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR c++/80560
|
||||
* dumpfile.c (dump_register): Avoid calling memset to initialize
|
||||
a class with a default ctor.
|
||||
* gcc.c (struct compiler): Remove const qualification.
|
||||
* genattrtab.c (gen_insn_reserv): Replace memset with initialization.
|
||||
* hash-table.h: Ditto.
|
||||
* ipa-cp.c (allocate_and_init_ipcp_value): Replace memset with
|
||||
assignment.
|
||||
* ipa-prop.c (ipa_free_edge_args_substructures): Ditto.
|
||||
* omp-low.c (lower_omp_ordered_clauses): Replace memset with
|
||||
default ctor.
|
||||
* params.h (struct param_info): Make struct members non-const.
|
||||
* tree-switch-conversion.c (emit_case_bit_tests): Replace memset
|
||||
with default initialization.
|
||||
* vec.h (vec_copy_construct, vec_default_construct): New helper
|
||||
functions.
|
||||
(vec<T>::copy, vec<T>::splice, vec<T>::reserve): Replace memcpy
|
||||
with vec_copy_construct.
|
||||
(vect<T>::quick_grow_cleared): Replace memset with default ctor.
|
||||
(vect<T>::vec_safe_grow_cleared, vec_safe_grow_cleared): Same.
|
||||
* doc/invoke.texi (-Wclass-memaccess): Document.
|
||||
|
||||
2017-06-15 Ramana Radhakrishnan <ramana.radhakrishnan@arm.com>
|
||||
|
||||
* emit-rtl.h (is_leaf): Update comment about local
|
||||
|
@ -1,3 +1,8 @@
|
||||
2017-06-15 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR c++/80560
|
||||
* c.opt (-Wclass-memaccess): New option.
|
||||
|
||||
2017-06-14 Boris Kolpackov <boris@codesynthesis.com>
|
||||
|
||||
* c-opts.c (c_common_finish): Handle '-' special value to -MF.
|
||||
|
@ -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.
|
||||
|
@ -1,3 +1,11 @@
|
||||
2017-06-15 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR c++/80560
|
||||
* call.c (first_non_public_field, maybe_warn_class_memaccess): New
|
||||
functions.
|
||||
(has_trivial_copy_assign_p, has_trivial_copy_p): Ditto.
|
||||
(build_cxx_call): Call maybe_warn_class_memaccess.
|
||||
|
||||
2017-06-14 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
* cp-gimplify.c (cp_genericize_r): Turn most of the function
|
||||
|
391
gcc/cp/call.c
391
gcc/cp/call.c
@ -8184,6 +8184,393 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
|
||||
return call;
|
||||
}
|
||||
|
||||
/* Return the DECL of the first non-public data member of class TYPE
|
||||
or null if none can be found. */
|
||||
|
||||
static tree
|
||||
first_non_public_field (tree type)
|
||||
{
|
||||
if (!CLASS_TYPE_P (type))
|
||||
return NULL_TREE;
|
||||
|
||||
for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
|
||||
{
|
||||
if (TREE_CODE (field) != FIELD_DECL)
|
||||
continue;
|
||||
if (TREE_STATIC (field))
|
||||
continue;
|
||||
if (TREE_PRIVATE (field) || TREE_PROTECTED (field))
|
||||
return field;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (tree base_binfo, binfo = TYPE_BINFO (type);
|
||||
BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
|
||||
{
|
||||
tree base = TREE_TYPE (base_binfo);
|
||||
|
||||
if (tree field = first_non_public_field (base))
|
||||
return field;
|
||||
}
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Return true if all copy and move assignment operator overloads for
|
||||
class TYPE are trivial and at least one of them is not deleted and,
|
||||
when ACCESS is set, accessible. Return false otherwise. Set
|
||||
HASASSIGN to true when the TYPE has a (not necessarily trivial)
|
||||
copy or move assignment. */
|
||||
|
||||
static bool
|
||||
has_trivial_copy_assign_p (tree type, bool access, bool *hasassign)
|
||||
{
|
||||
tree fns = cp_assignment_operator_id (NOP_EXPR);
|
||||
fns = lookup_fnfields_slot (type, fns);
|
||||
|
||||
bool all_trivial = true;
|
||||
|
||||
/* Iterate over overloads of the assignment operator, checking
|
||||
accessible copy assignments for triviality. */
|
||||
|
||||
for (ovl_iterator oi (fns); oi; ++oi)
|
||||
{
|
||||
tree f = *oi;
|
||||
|
||||
/* Skip operators that aren't copy assignments. */
|
||||
if (!copy_fn_p (f))
|
||||
continue;
|
||||
|
||||
bool accessible = (!access || !(TREE_PRIVATE (f) || TREE_PROTECTED (f))
|
||||
|| accessible_p (TYPE_BINFO (type), f, true));
|
||||
|
||||
/* Skip template assignment operators and deleted functions. */
|
||||
if (TREE_CODE (f) != FUNCTION_DECL || DECL_DELETED_FN (f))
|
||||
continue;
|
||||
|
||||
if (accessible)
|
||||
*hasassign = true;
|
||||
|
||||
if (!accessible || !trivial_fn_p (f))
|
||||
all_trivial = false;
|
||||
|
||||
/* Break early when both properties have been determined. */
|
||||
if (*hasassign && !all_trivial)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Return true if they're all trivial and one of the expressions
|
||||
TYPE() = TYPE() or TYPE() = (TYPE&)() is valid. */
|
||||
tree ref = cp_build_reference_type (type, false);
|
||||
return (all_trivial
|
||||
&& (is_trivially_xible (MODIFY_EXPR, type, type)
|
||||
|| is_trivially_xible (MODIFY_EXPR, type, ref)));
|
||||
}
|
||||
|
||||
/* Return true if all copy and move ctor overloads for class TYPE are
|
||||
trivial and at least one of them is not deleted and, when ACCESS is
|
||||
set, accessible. Return false otherwise. Set each element of HASCTOR[]
|
||||
to true when the TYPE has a (not necessarily trivial) default and copy
|
||||
(or move) ctor, respectively. */
|
||||
|
||||
static bool
|
||||
has_trivial_copy_p (tree type, bool access, bool hasctor[2])
|
||||
{
|
||||
tree fns = lookup_fnfields_slot (type, complete_ctor_identifier);
|
||||
|
||||
bool all_trivial = true;
|
||||
|
||||
for (ovl_iterator oi (fns); oi; ++oi)
|
||||
{
|
||||
tree f = *oi;
|
||||
|
||||
/* Skip template constructors. */
|
||||
if (TREE_CODE (f) != FUNCTION_DECL)
|
||||
continue;
|
||||
|
||||
bool cpy_or_move_ctor_p = copy_fn_p (f);
|
||||
|
||||
/* Skip ctors other than default, copy, and move. */
|
||||
if (!cpy_or_move_ctor_p && !default_ctor_p (f))
|
||||
continue;
|
||||
|
||||
if (DECL_DELETED_FN (f))
|
||||
continue;
|
||||
|
||||
bool accessible = (!access || !(TREE_PRIVATE (f) || TREE_PROTECTED (f))
|
||||
|| accessible_p (TYPE_BINFO (type), f, true));
|
||||
|
||||
if (accessible)
|
||||
hasctor[cpy_or_move_ctor_p] = true;
|
||||
|
||||
if (cpy_or_move_ctor_p && (!accessible || !trivial_fn_p (f)))
|
||||
all_trivial = false;
|
||||
|
||||
/* Break early when both properties have been determined. */
|
||||
if (hasctor[0] && hasctor[1] && !all_trivial)
|
||||
break;
|
||||
}
|
||||
|
||||
return all_trivial;
|
||||
}
|
||||
|
||||
/* Issue a warning on a call to the built-in function FNDECL if it is
|
||||
a raw memory write whose destination is not an object of (something
|
||||
like) trivial or standard layout type with a non-deleted assignment
|
||||
and copy ctor. Detects const correctness violations, corrupting
|
||||
references, virtual table pointers, and bypassing non-trivial
|
||||
assignments. */
|
||||
|
||||
static void
|
||||
maybe_warn_class_memaccess (location_t loc, tree fndecl, tree *args)
|
||||
{
|
||||
/* Except for bcopy where it's second, the destination pointer is
|
||||
the first argument for all functions handled here. Compute
|
||||
the index of the destination and source arguments. */
|
||||
unsigned dstidx = DECL_FUNCTION_CODE (fndecl) == BUILT_IN_BCOPY;
|
||||
unsigned srcidx = !dstidx;
|
||||
|
||||
tree dest = args[dstidx];
|
||||
if (!dest || !TREE_TYPE (dest) || !POINTER_TYPE_P (TREE_TYPE (dest)))
|
||||
return;
|
||||
|
||||
STRIP_NOPS (dest);
|
||||
|
||||
tree srctype = NULL_TREE;
|
||||
|
||||
/* Determine the type of the pointed-to object and whether it's
|
||||
a complete class type. */
|
||||
tree desttype = TREE_TYPE (TREE_TYPE (dest));
|
||||
|
||||
if (!desttype || !COMPLETE_TYPE_P (desttype) || !CLASS_TYPE_P (desttype))
|
||||
return;
|
||||
|
||||
/* Check to see if the raw memory call is made by a ctor or dtor
|
||||
with this as the destination argument for the destination type.
|
||||
If so, be more permissive. */
|
||||
if (current_function_decl
|
||||
&& (DECL_CONSTRUCTOR_P (current_function_decl)
|
||||
|| DECL_DESTRUCTOR_P (current_function_decl))
|
||||
&& is_this_parameter (dest))
|
||||
{
|
||||
tree ctx = DECL_CONTEXT (current_function_decl);
|
||||
bool special = same_type_ignoring_top_level_qualifiers_p (ctx, desttype);
|
||||
|
||||
tree binfo = TYPE_BINFO (ctx);
|
||||
|
||||
/* A ctor and dtor for a class with no bases and no virtual functions
|
||||
can do whatever they want. Bail early with no further checking. */
|
||||
if (special && !BINFO_VTABLE (binfo) && !BINFO_N_BASE_BINFOS (binfo))
|
||||
return;
|
||||
}
|
||||
|
||||
/* True if the class is trivial. */
|
||||
bool trivial = trivial_type_p (desttype);
|
||||
|
||||
/* Set to true if DESTYPE has an accessible copy assignment. */
|
||||
bool hasassign = false;
|
||||
/* True if all of the class' overloaded copy assignment operators
|
||||
are all trivial (and not deleted) and at least one of them is
|
||||
accessible. */
|
||||
bool trivassign = has_trivial_copy_assign_p (desttype, true, &hasassign);
|
||||
|
||||
/* Set to true if DESTTYPE has an accessible default and copy ctor,
|
||||
respectively. */
|
||||
bool hasctors[2] = { false, false };
|
||||
|
||||
/* True if all of the class' overloaded copy constructors are all
|
||||
trivial (and not deleted) and at least one of them is accessible. */
|
||||
bool trivcopy = has_trivial_copy_p (desttype, true, hasctors);
|
||||
|
||||
/* Set FLD to the first private/protected member of the class. */
|
||||
tree fld = trivial ? first_non_public_field (desttype) : NULL_TREE;
|
||||
|
||||
/* The warning format string. */
|
||||
const char *warnfmt = NULL;
|
||||
/* A suggested alternative to offer instead of the raw memory call.
|
||||
Empty string when none can be come up with. */
|
||||
const char *suggest = "";
|
||||
bool warned = false;
|
||||
|
||||
switch (DECL_FUNCTION_CODE (fndecl))
|
||||
{
|
||||
case BUILT_IN_MEMSET:
|
||||
if (!integer_zerop (args[1]))
|
||||
{
|
||||
/* Diagnose setting non-copy-assignable or non-trivial types,
|
||||
or types with a private member, to (potentially) non-zero
|
||||
bytes. Since the value of the bytes being written is unknown,
|
||||
suggest using assignment instead (if one exists). Also warn
|
||||
for writes into objects for which zero-initialization doesn't
|
||||
mean all bits clear (pointer-to-member data, where null is all
|
||||
bits set). Since the value being written is (most likely)
|
||||
non-zero, simply suggest assignment (but not copy assignment). */
|
||||
suggest = "; use assignment instead";
|
||||
if (!trivassign)
|
||||
warnfmt = G_("%qD writing to an object of type %#qT with "
|
||||
"no trivial copy-assignment");
|
||||
else if (!trivial)
|
||||
warnfmt = G_("%qD writing to an object of non-trivial type %#qT%s");
|
||||
else if (fld)
|
||||
{
|
||||
const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
|
||||
warned = warning_at (loc, OPT_Wclass_memaccess,
|
||||
"%qD writing to an object of type %#qT with "
|
||||
"%qs member %qD",
|
||||
fndecl, desttype, access, fld);
|
||||
}
|
||||
else if (!zero_init_p (desttype))
|
||||
warnfmt = G_("%qD writing to an object of type %#qT containing "
|
||||
"a pointer to data member%s");
|
||||
|
||||
break;
|
||||
}
|
||||
/* Fall through. */
|
||||
|
||||
case BUILT_IN_BZERO:
|
||||
/* Similarly to the above, diagnose clearing non-trivial or non-
|
||||
standard layout objects, or objects of types with no assignmenmt.
|
||||
Since the value being written is known to be zero, suggest either
|
||||
copy assignment, copy ctor, or default ctor as an alternative,
|
||||
depending on what's available. */
|
||||
|
||||
if (hasassign && hasctors[0])
|
||||
suggest = G_("; use assignment or value-initialization instead");
|
||||
else if (hasassign)
|
||||
suggest = G_("; use assignment instead");
|
||||
else if (hasctors[0])
|
||||
suggest = G_("; use value-initialization instead");
|
||||
|
||||
if (!trivassign)
|
||||
warnfmt = G_("%qD clearing an object of type %#qT with "
|
||||
"no trivial copy-assignment%s");
|
||||
else if (!trivial)
|
||||
warnfmt = G_("%qD clearing an object of non-trivial type %#qT%s");
|
||||
else if (!zero_init_p (desttype))
|
||||
warnfmt = G_("%qD clearing an object of type %#qT containing "
|
||||
"a pointer-to-member%s");
|
||||
break;
|
||||
|
||||
case BUILT_IN_BCOPY:
|
||||
case BUILT_IN_MEMCPY:
|
||||
case BUILT_IN_MEMMOVE:
|
||||
case BUILT_IN_MEMPCPY:
|
||||
/* Determine the type of the source object. */
|
||||
srctype = STRIP_NOPS (args[srcidx]);
|
||||
srctype = TREE_TYPE (TREE_TYPE (srctype));
|
||||
|
||||
/* Since it's impossible to determine wheter the byte copy is
|
||||
being used in place of assignment to an existing object or
|
||||
as a substitute for initialization, assume it's the former.
|
||||
Determine the best alternative to use instead depending on
|
||||
what's not deleted. */
|
||||
if (hasassign && hasctors[1])
|
||||
suggest = G_("; use copy-assignment or copy-initialization instead");
|
||||
else if (hasassign)
|
||||
suggest = G_("; use copy-assignment instead");
|
||||
else if (hasctors[1])
|
||||
suggest = G_("; use copy-initialization instead");
|
||||
|
||||
if (!trivassign)
|
||||
warnfmt = G_("%qD writing to an object of type %#qT with no trivial "
|
||||
"copy-assignment%s");
|
||||
else if (!trivially_copyable_p (desttype))
|
||||
warnfmt = G_("%qD writing to an object of non-trivially copyable "
|
||||
"type %#qT%s");
|
||||
else if (!trivcopy)
|
||||
warnfmt = G_("%qD writing to an object with a deleted copy constructor");
|
||||
|
||||
else if (!trivial
|
||||
&& !VOID_TYPE_P (srctype)
|
||||
&& !char_type_p (TYPE_MAIN_VARIANT (srctype))
|
||||
&& !same_type_ignoring_top_level_qualifiers_p (desttype,
|
||||
srctype))
|
||||
{
|
||||
/* Warn when copying into a non-trivial object from an object
|
||||
of a different type other than void or char. */
|
||||
warned = warning_at (loc, OPT_Wclass_memaccess,
|
||||
"%qD copying an object of non-trivial type "
|
||||
"%#qT from an array of %#qT",
|
||||
fndecl, desttype, srctype);
|
||||
}
|
||||
else if (fld
|
||||
&& !VOID_TYPE_P (srctype)
|
||||
&& !char_type_p (TYPE_MAIN_VARIANT (srctype))
|
||||
&& !same_type_ignoring_top_level_qualifiers_p (desttype,
|
||||
srctype))
|
||||
{
|
||||
const char *access = TREE_PRIVATE (fld) ? "private" : "protected";
|
||||
warned = warning_at (loc, OPT_Wclass_memaccess,
|
||||
"%qD copying an object of type %#qT with "
|
||||
"%qs member %qD from an array of %#qT; use "
|
||||
"assignment or copy-initialization instead",
|
||||
fndecl, desttype, access, fld, srctype);
|
||||
}
|
||||
else if (!trivial && TREE_CODE (args[2]) == INTEGER_CST)
|
||||
{
|
||||
/* Finally, warn on partial copies. */
|
||||
unsigned HOST_WIDE_INT typesize
|
||||
= tree_to_uhwi (TYPE_SIZE_UNIT (desttype));
|
||||
if (unsigned HOST_WIDE_INT partial
|
||||
= tree_to_uhwi (args[2]) % typesize)
|
||||
warned = warning_at (loc, OPT_Wclass_memaccess,
|
||||
(typesize - partial > 1
|
||||
? G_("%qD writing to an object of "
|
||||
"a non-trivial type %#qT leaves %wu "
|
||||
"bytes unchanged")
|
||||
: G_("%qD writing to an object of "
|
||||
"a non-trivial type %#qT leaves %wu "
|
||||
"byte unchanged")),
|
||||
fndecl, desttype, typesize - partial);
|
||||
}
|
||||
break;
|
||||
|
||||
case BUILT_IN_REALLOC:
|
||||
|
||||
if (!trivially_copyable_p (desttype))
|
||||
warnfmt = G_("%qD moving an object of non-trivially copyable type "
|
||||
"%#qT; use %<new%> and %<delete%> instead");
|
||||
else if (!trivcopy)
|
||||
warnfmt = G_("%qD moving an object of type %#qT with deleted copy "
|
||||
"constructor; use %<new%> and %<delete%> instead");
|
||||
else if (!get_dtor (desttype, tf_none))
|
||||
warnfmt = G_("%qD moving an object of type %#qT with deleted "
|
||||
"destructor");
|
||||
else if (!trivial
|
||||
&& TREE_CODE (args[1]) == INTEGER_CST
|
||||
&& tree_int_cst_lt (args[1], TYPE_SIZE_UNIT (desttype)))
|
||||
{
|
||||
/* Finally, warn on reallocation into insufficient space. */
|
||||
warned = warning_at (loc, OPT_Wclass_memaccess,
|
||||
"%qD moving an object of non-trivial type "
|
||||
"%#qT and size %E into a region of size %E",
|
||||
fndecl, desttype, TYPE_SIZE_UNIT (desttype),
|
||||
args[1]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (!warned && !warnfmt)
|
||||
return;
|
||||
|
||||
if (warnfmt)
|
||||
{
|
||||
if (suggest)
|
||||
warned = warning_at (loc, OPT_Wclass_memaccess,
|
||||
warnfmt, fndecl, desttype, suggest);
|
||||
else
|
||||
warned = warning_at (loc, OPT_Wclass_memaccess,
|
||||
warnfmt, fndecl, desttype);
|
||||
}
|
||||
|
||||
if (warned)
|
||||
inform (location_of (desttype), "%#qT declared here", desttype);
|
||||
}
|
||||
|
||||
/* Build and return a call to FN, using NARGS arguments in ARGARRAY.
|
||||
This function performs no overload resolution, conversion, or other
|
||||
high-level operations. */
|
||||
@ -8216,6 +8603,10 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
|
||||
if (!check_builtin_function_arguments (EXPR_LOCATION (fn), vNULL, fndecl,
|
||||
nargs, argarray))
|
||||
return error_mark_node;
|
||||
|
||||
/* Warn if the built-in writes to an object of a non-trivial type. */
|
||||
if (nargs)
|
||||
maybe_warn_class_memaccess (loc, fndecl, argarray);
|
||||
}
|
||||
|
||||
/* If it is a built-in array notation function, then the return type of
|
||||
|
@ -215,7 +215,8 @@ in the following sections.
|
||||
-Wabi=@var{n} -Wabi-tag -Wconversion-null -Wctor-dtor-privacy @gol
|
||||
-Wdelete-non-virtual-dtor -Wliteral-suffix -Wmultiple-inheritance @gol
|
||||
-Wnamespaces -Wnarrowing @gol
|
||||
-Wnoexcept -Wnoexcept-type -Wnon-virtual-dtor -Wreorder -Wregister @gol
|
||||
-Wnoexcept -Wnoexcept-type -Wclass-memaccess @gol
|
||||
-Wnon-virtual-dtor -Wreorder -Wregister @gol
|
||||
-Weffc++ -Wstrict-null-sentinel -Wtemplates @gol
|
||||
-Wno-non-template-friend -Wold-style-cast @gol
|
||||
-Woverloaded-virtual -Wno-pmf-conversions @gol
|
||||
@ -2920,6 +2921,23 @@ void g() noexcept;
|
||||
void h() @{ f(g); @} // in C++14 calls f<void(*)()>, in C++1z calls f<void(*)()noexcept>
|
||||
@end smallexample
|
||||
|
||||
@item -Wclass-memaccess @r{(C++ and Objective-C++ only)}
|
||||
@opindex Wclass-memaccess
|
||||
Warn when the destination of a call to a raw memory function such as
|
||||
@code{memset} or @code{memcpy} is an object of class type writing into which
|
||||
might bypass the class non-trivial or deleted constructor or copy assignment,
|
||||
violate const-correctness or encapsulation, or corrupt the virtual table.
|
||||
Modifying the representation of such objects may violate invariants maintained
|
||||
by member functions of the class. For example, the call to @code{memset}
|
||||
below is undefined becase it modifies a non-trivial class object and is,
|
||||
therefore, diagnosed. The safe way to either initialize or clear the storage
|
||||
of objects of such types is by using the appropriate constructor or assignment
|
||||
operator, if one is available.
|
||||
@smallexample
|
||||
std::string str = "abc";
|
||||
memset (&str, 0, 3);
|
||||
@end smallexample
|
||||
The @option{-Wclass-memaccess} option is enabled by @option{-Wall}.
|
||||
|
||||
@item -Wnon-virtual-dtor @r{(C++ and Objective-C++ only)}
|
||||
@opindex Wnon-virtual-dtor
|
||||
|
@ -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;
|
||||
|
@ -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. */
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -803,7 +803,10 @@ hash_table<Descriptor, Allocator>::empty_slow ()
|
||||
m_size_prime_index = nindex;
|
||||
}
|
||||
else
|
||||
memset (entries, 0, size * sizeof (value_type));
|
||||
{
|
||||
for ( ; size; ++entries, --size)
|
||||
*entries = value_type ();
|
||||
}
|
||||
m_n_deleted = 0;
|
||||
m_n_elements = 0;
|
||||
}
|
||||
|
@ -1471,8 +1471,7 @@ allocate_and_init_ipcp_value (tree source)
|
||||
{
|
||||
ipcp_value<tree> *val;
|
||||
|
||||
val = ipcp_cst_values_pool.allocate ();
|
||||
memset (val, 0, sizeof (*val));
|
||||
val = new (ipcp_cst_values_pool.allocate ()) ipcp_value<tree>();
|
||||
val->value = source;
|
||||
return val;
|
||||
}
|
||||
@ -1486,8 +1485,8 @@ allocate_and_init_ipcp_value (ipa_polymorphic_call_context source)
|
||||
ipcp_value<ipa_polymorphic_call_context> *val;
|
||||
|
||||
// TODO
|
||||
val = ipcp_poly_ctx_values_pool.allocate ();
|
||||
memset (val, 0, sizeof (*val));
|
||||
val = new (ipcp_poly_ctx_values_pool.allocate ())
|
||||
ipcp_value<ipa_polymorphic_call_context>();
|
||||
val->value = source;
|
||||
return val;
|
||||
}
|
||||
|
@ -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. */
|
||||
|
@ -6320,7 +6320,11 @@ lower_omp_ordered_clauses (gimple_stmt_iterator *gsi_p, gomp_ordered *ord_stmt,
|
||||
return;
|
||||
|
||||
wide_int *folded_deps = XALLOCAVEC (wide_int, 2 * len - 1);
|
||||
memset (folded_deps, 0, sizeof (*folded_deps) * (2 * len - 1));
|
||||
|
||||
/* wide_int is not a POD so it must be default-constructed. */
|
||||
for (unsigned i = 0; i != 2 * len - 1; ++i)
|
||||
new (static_cast<void*>(folded_deps + i)) wide_int ();
|
||||
|
||||
tree folded_dep = NULL_TREE;
|
||||
/* TRUE if the first dimension's offset is negative. */
|
||||
bool neg_offset_p = false;
|
||||
|
@ -42,7 +42,7 @@ struct param_info
|
||||
{
|
||||
/* The name used with the `--param <name>=<value>' switch to set this
|
||||
value. */
|
||||
const char *const option;
|
||||
const char *option;
|
||||
|
||||
/* The default value. */
|
||||
int default_value;
|
||||
@ -54,7 +54,7 @@ struct param_info
|
||||
int max_value;
|
||||
|
||||
/* A short description of the option. */
|
||||
const char *const help;
|
||||
const char *help;
|
||||
|
||||
/* The optional names corresponding to the values. */
|
||||
const char **value_names;
|
||||
|
@ -1,3 +1,8 @@
|
||||
2017-06-15 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR c++/80560
|
||||
* g++.dg/Wclass-memaccess.C: New test.
|
||||
|
||||
2017-06-15 Janus Weil <janus@gcc.gnu.org>
|
||||
|
||||
PR fortran/80983
|
||||
|
1671
gcc/testsuite/g++.dg/Wclass-memaccess.C
Normal file
1671
gcc/testsuite/g++.dg/Wclass-memaccess.C
Normal file
File diff suppressed because it is too large
Load Diff
@ -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));
|
||||
|
41
gcc/vec.h
41
gcc/vec.h
@ -407,6 +407,26 @@ struct GTY((user)) vec
|
||||
{
|
||||
};
|
||||
|
||||
/* Default-construct N elements in DST. */
|
||||
|
||||
template <typename T>
|
||||
inline void
|
||||
vec_default_construct (T *dst, unsigned n)
|
||||
{
|
||||
for ( ; n; ++dst, --n)
|
||||
::new (static_cast<void*>(dst)) T ();
|
||||
}
|
||||
|
||||
/* Copy-construct N elements in DST from *SRC. */
|
||||
|
||||
template <typename T>
|
||||
inline void
|
||||
vec_copy_construct (T *dst, const T *src, unsigned n)
|
||||
{
|
||||
for ( ; n; ++dst, ++src, --n)
|
||||
::new (static_cast<void*>(dst)) T (*src);
|
||||
}
|
||||
|
||||
/* Type to provide NULL values for vec<T, A, L>. This is used to
|
||||
provide nil initializers for vec instances. Since vec must be
|
||||
a POD, we cannot have proper ctor/dtor for it. To initialize
|
||||
@ -612,7 +632,7 @@ vec_safe_grow_cleared (vec<T, A, vl_embed> *&v, unsigned len CXX_MEM_STAT_INFO)
|
||||
{
|
||||
unsigned oldlen = vec_safe_length (v);
|
||||
vec_safe_grow (v, len PASS_MEM_STAT);
|
||||
memset (&(v->address ()[oldlen]), 0, sizeof (T) * (len - oldlen));
|
||||
vec_default_construct (v->address () + oldlen, len - oldlen);
|
||||
}
|
||||
|
||||
|
||||
@ -818,7 +838,7 @@ vec<T, A, vl_embed>::copy (ALONE_MEM_STAT_DECL) const
|
||||
{
|
||||
vec_alloc (new_vec, len PASS_MEM_STAT);
|
||||
new_vec->embedded_init (len, len);
|
||||
memcpy (new_vec->address (), m_vecdata, sizeof (T) * len);
|
||||
vec_copy_construct (new_vec->address (), m_vecdata, len);
|
||||
}
|
||||
return new_vec;
|
||||
}
|
||||
@ -835,7 +855,7 @@ vec<T, A, vl_embed>::splice (const vec<T, A, vl_embed> &src)
|
||||
if (len)
|
||||
{
|
||||
gcc_checking_assert (space (len));
|
||||
memcpy (address () + length (), src.address (), len * sizeof (T));
|
||||
vec_copy_construct (end (), src.address (), len);
|
||||
m_vecpfx.m_num += len;
|
||||
}
|
||||
}
|
||||
@ -1089,13 +1109,12 @@ inline void
|
||||
vec<T, A, vl_embed>::quick_grow_cleared (unsigned len)
|
||||
{
|
||||
unsigned oldlen = length ();
|
||||
size_t sz = sizeof (T) * (len - oldlen);
|
||||
size_t growby = len - oldlen;
|
||||
quick_grow (len);
|
||||
if (sz != 0)
|
||||
memset (&(address ()[oldlen]), 0, sz);
|
||||
if (growby != 0)
|
||||
vec_default_construct (address () + oldlen, growby);
|
||||
}
|
||||
|
||||
|
||||
/* Garbage collection support for vec<T, A, vl_embed>. */
|
||||
|
||||
template<typename T>
|
||||
@ -1454,7 +1473,7 @@ vec<T, va_heap, vl_ptr>::reserve (unsigned nelems, bool exact MEM_STAT_DECL)
|
||||
va_heap::reserve (m_vec, nelems, exact PASS_MEM_STAT);
|
||||
if (handle_auto_vec)
|
||||
{
|
||||
memcpy (m_vec->address (), oldvec->address (), sizeof (T) * oldsize);
|
||||
vec_copy_construct (m_vec->address (), oldvec->address (), oldsize);
|
||||
m_vec->m_vecpfx.m_num = oldsize;
|
||||
}
|
||||
|
||||
@ -1616,10 +1635,10 @@ inline void
|
||||
vec<T, va_heap, vl_ptr>::safe_grow_cleared (unsigned len MEM_STAT_DECL)
|
||||
{
|
||||
unsigned oldlen = length ();
|
||||
size_t sz = sizeof (T) * (len - oldlen);
|
||||
size_t growby = len - oldlen;
|
||||
safe_grow (len PASS_MEM_STAT);
|
||||
if (sz != 0)
|
||||
memset (&(address ()[oldlen]), 0, sz);
|
||||
if (growby != 0)
|
||||
vec_default_construct (address () + oldlen, growby);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
|
@ -138,7 +138,11 @@ struct ml_mg : public method_group
|
||||
// This store is only executed while holding the serial lock, so relaxed
|
||||
// memory order is sufficient here. Same holds for the memset.
|
||||
time.store(0, memory_order_relaxed);
|
||||
memset(orecs, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
|
||||
// The memset below isn't strictly kosher because it bypasses
|
||||
// the non-trivial assignment operator defined by std::atomic. Using
|
||||
// a local void* is enough to prevent GCC from warning for this.
|
||||
void *p = orecs;
|
||||
memset(p, 0, sizeof(atomic<gtm_word>) * L2O_ORECS);
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user