PR c++/84850 - -Wclass-memaccess on a memcpy in a copy assignment operator with no nontrivial bases or members
gcc/cp/ChangeLog: PR c++/84850 * call.c (first_non_public_field): New template and function. (first_non_trivial_field): New function. (maybe_warn_class_memaccess): Call them. gcc/testsuite/ChangeLog: PR c++/84850 * g++.dg/Wclass-memaccess-3.C: New test. * g++.dg/Wclass-memaccess-4.C: New test. From-SVN: r258719
This commit is contained in:
parent
2a80d3ae14
commit
e65fc7c572
5 changed files with 406 additions and 18 deletions
|
@ -1,3 +1,10 @@
|
|||
2018-03-21 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR c++/84850
|
||||
* call.c (first_non_public_field): New template and function.
|
||||
(first_non_trivial_field): New function.
|
||||
(maybe_warn_class_memaccess): Call them.
|
||||
|
||||
2018-03-21 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c++/84892
|
||||
|
|
|
@ -8261,13 +8261,17 @@ 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)
|
||||
namespace
|
||||
{
|
||||
if (!CLASS_TYPE_P (type))
|
||||
|
||||
/* Return the DECL of the first non-static subobject of class TYPE
|
||||
that satisfies the predicate PRED or null if none can be found. */
|
||||
|
||||
template <class Predicate>
|
||||
tree
|
||||
first_non_static_field (tree type, Predicate pred)
|
||||
{
|
||||
if (!type || !CLASS_TYPE_P (type))
|
||||
return NULL_TREE;
|
||||
|
||||
for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
|
||||
|
@ -8276,7 +8280,7 @@ first_non_public_field (tree type)
|
|||
continue;
|
||||
if (TREE_STATIC (field))
|
||||
continue;
|
||||
if (TREE_PRIVATE (field) || TREE_PROTECTED (field))
|
||||
if (pred (field))
|
||||
return field;
|
||||
}
|
||||
|
||||
|
@ -8286,14 +8290,51 @@ first_non_public_field (tree type)
|
|||
BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
|
||||
{
|
||||
tree base = TREE_TYPE (base_binfo);
|
||||
|
||||
if (tree field = first_non_public_field (base))
|
||||
if (pred (base))
|
||||
return base;
|
||||
if (tree field = first_non_static_field (base, pred))
|
||||
return field;
|
||||
}
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
struct NonPublicField
|
||||
{
|
||||
bool operator() (const_tree t)
|
||||
{
|
||||
return DECL_P (t) && (TREE_PRIVATE (t) || TREE_PROTECTED (t));
|
||||
}
|
||||
};
|
||||
|
||||
/* Return the DECL of the first non-public subobject of class TYPE
|
||||
or null if none can be found. */
|
||||
|
||||
static inline tree
|
||||
first_non_public_field (tree type)
|
||||
{
|
||||
return first_non_static_field (type, NonPublicField ());
|
||||
}
|
||||
|
||||
struct NonTrivialField
|
||||
{
|
||||
bool operator() (const_tree t)
|
||||
{
|
||||
return !trivial_type_p (DECL_P (t) ? TREE_TYPE (t) : t);
|
||||
}
|
||||
};
|
||||
|
||||
/* Return the DECL of the first non-trivial subobject of class TYPE
|
||||
or null if none can be found. */
|
||||
|
||||
static inline tree
|
||||
first_non_trivial_field (tree type)
|
||||
{
|
||||
return first_non_static_field (type, NonTrivialField ());
|
||||
}
|
||||
|
||||
} /* unnamed namespace */
|
||||
|
||||
/* 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
|
||||
|
@ -8419,22 +8460,30 @@ maybe_warn_class_memaccess (location_t loc, tree fndecl,
|
|||
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. */
|
||||
/* Check to see if the raw memory call is made by a non-static member
|
||||
function with THIS as the destination argument for the destination
|
||||
type. If so, and if the class has no non-trivial bases or members,
|
||||
be more permissive. */
|
||||
if (current_function_decl
|
||||
&& (DECL_CONSTRUCTOR_P (current_function_decl)
|
||||
|| DECL_DESTRUCTOR_P (current_function_decl))
|
||||
&& DECL_NONSTATIC_MEMBER_FUNCTION_P (current_function_decl)
|
||||
&& is_this_parameter (tree_strip_nop_conversions (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))
|
||||
/* FIXME: The following if statement is overly permissive (see
|
||||
bug 84851). Remove it in GCC 9. */
|
||||
if (special
|
||||
&& !BINFO_VTABLE (binfo)
|
||||
&& !BINFO_N_BASE_BINFOS (binfo)
|
||||
&& (DECL_CONSTRUCTOR_P (current_function_decl)
|
||||
|| DECL_DESTRUCTOR_P (current_function_decl)))
|
||||
return;
|
||||
|
||||
if (special
|
||||
&& !BINFO_VTABLE (binfo)
|
||||
&& !first_non_trivial_field (desttype))
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
2018-03-21 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR c++/84850
|
||||
* g++.dg/Wclass-memaccess-3.C: New test.
|
||||
* g++.dg/Wclass-memaccess-4.C: New test.
|
||||
|
||||
2018-03-21 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c++/84892
|
||||
|
|
287
gcc/testsuite/g++.dg/Wclass-memaccess-3.C
Normal file
287
gcc/testsuite/g++.dg/Wclass-memaccess-3.C
Normal file
|
@ -0,0 +1,287 @@
|
|||
/* PR c++/84850 - -Wclass-memaccess on a memcpy in a copy assignment
|
||||
operator with no nontrivial bases or members
|
||||
{ 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);
|
||||
extern "C" void* memset (void*, int, size_t);
|
||||
|
||||
template <int>
|
||||
struct EmptyClass { };
|
||||
|
||||
template <int>
|
||||
struct TrivialClass
|
||||
{
|
||||
bool a;
|
||||
int b;
|
||||
void *c;
|
||||
double d[2];
|
||||
void (*e)();
|
||||
};
|
||||
|
||||
template <int>
|
||||
struct HasDefault
|
||||
{
|
||||
HasDefault ();
|
||||
};
|
||||
|
||||
/* Verify that raw memory accesses from non-static members of a class with
|
||||
an empty base is not diagnosed. */
|
||||
|
||||
struct EmptyWithBase: EmptyClass<0>, EmptyClass<1>, EmptyClass<2>
|
||||
{
|
||||
EmptyWithBase ()
|
||||
{
|
||||
memset (this, 0, sizeof *this);
|
||||
}
|
||||
|
||||
EmptyWithBase (const EmptyWithBase &x)
|
||||
{
|
||||
memcpy (this, &x, sizeof *this);
|
||||
}
|
||||
|
||||
~EmptyWithBase ()
|
||||
{
|
||||
memset (this, 0, sizeof *this);
|
||||
}
|
||||
|
||||
void operator= (const EmptyWithBase &x)
|
||||
{
|
||||
memcpy (this, &x, sizeof *this);
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
memset (this, 0, sizeof *this); // { dg-bogus "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
void copy (const void *p)
|
||||
{
|
||||
memcpy (this, p, sizeof *this); // { dg-bogus "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
static void bad_clear (EmptyWithBase &x)
|
||||
{
|
||||
memset (&x, 0, sizeof x); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
static void bad_copy (EmptyWithBase &x, const void *p)
|
||||
{
|
||||
memcpy (&x, p, sizeof x); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
};
|
||||
|
||||
/* Verify that raw memory accesses from non-static members of a class with
|
||||
all trivial members is not diagnosed. */
|
||||
|
||||
struct HasTrivialMembers
|
||||
{
|
||||
bool a;
|
||||
int b;
|
||||
void *c;
|
||||
double d[2];
|
||||
void (*e)();
|
||||
|
||||
TrivialClass<1> trivial;
|
||||
|
||||
HasTrivialMembers ()
|
||||
{
|
||||
memset (this, 0, sizeof *this);
|
||||
}
|
||||
|
||||
HasTrivialMembers (const HasTrivialMembers &x)
|
||||
{
|
||||
memcpy (this, &x, sizeof *this);
|
||||
}
|
||||
|
||||
~HasTrivialMembers ()
|
||||
{
|
||||
memset (this, 0, sizeof *this);
|
||||
}
|
||||
|
||||
void operator= (const HasTrivialMembers &x)
|
||||
{
|
||||
memcpy (this, &x, sizeof *this);
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
memset (this, 0, sizeof *this); // { dg-bogus "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
void copy (const void *p)
|
||||
{
|
||||
memcpy (this, p, sizeof *this); // { dg-bogus "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
static void bad_clear (HasTrivialMembers &x)
|
||||
{
|
||||
memset (&x, 0, sizeof x); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
static void bad_copy (HasTrivialMembers &x, const void *p)
|
||||
{
|
||||
memcpy (&x, p, sizeof x); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
};
|
||||
|
||||
/* Verify that raw memory accesses from non-static members of a class with
|
||||
a trivial base class and no non-trivial members is not diagnosed. */
|
||||
|
||||
struct HasTrivialBase: TrivialClass<1>
|
||||
{
|
||||
TrivialClass<2> a[2];
|
||||
|
||||
HasTrivialBase ()
|
||||
{
|
||||
memset (this, 0, sizeof *this); // { dg-bogus "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
HasTrivialBase (const HasTrivialBase &x)
|
||||
{
|
||||
memcpy (this, &x, sizeof *this); // { dg-bogus "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
~HasTrivialBase ()
|
||||
{
|
||||
memset (this, 0, sizeof *this); // { dg-bogus "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
void operator= (const HasTrivialBase &x)
|
||||
{
|
||||
memcpy (this, &x, sizeof *this);
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
memset (this, 0, sizeof *this); // { dg-bogus "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
void copy (void *p)
|
||||
{
|
||||
memcpy (this, p, sizeof *this); // { dg-bogus "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct HasTrivialBases: TrivialClass<1>, TrivialClass<2>
|
||||
{
|
||||
TrivialClass<3> a[2];
|
||||
|
||||
HasTrivialBases ()
|
||||
{
|
||||
memset (this, 0, sizeof *this); // { dg-bogus "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
HasTrivialBases (const HasTrivialBases &x)
|
||||
{
|
||||
memcpy (this, &x, sizeof *this); // { dg-bogus "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
~HasTrivialBases ()
|
||||
{
|
||||
memset (this, 0, sizeof *this); // { dg-bogus "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
void operator= (const HasTrivialBases &x)
|
||||
{
|
||||
memcpy (this, &x, sizeof *this);
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
memset (this, 0, sizeof *this); // { dg-bogus "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
void copy (void *p)
|
||||
{
|
||||
memcpy (this, p, sizeof *this); // { dg-bogus "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
};
|
||||
|
||||
struct DerivesFromNontrivialClass: HasDefault<1> { };
|
||||
|
||||
/* Verify that raw memory accesses from members of a class with a non-trivial
|
||||
base class is diagnosed. */
|
||||
|
||||
struct HasNonTrivialBase: TrivialClass<1>, TrivialClass<2>,
|
||||
DerivesFromNontrivialClass,
|
||||
TrivialClass<3>, TrivialClass<4>
|
||||
{
|
||||
HasNonTrivialBase ()
|
||||
{
|
||||
memset (this, 0, sizeof *this); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
HasNonTrivialBase (const HasNonTrivialBase &x)
|
||||
{
|
||||
memcpy (this, &x, sizeof *this); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
~HasNonTrivialBase ()
|
||||
{
|
||||
memset (this, 0, sizeof *this); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
HasNonTrivialBase& operator= (const HasNonTrivialBase &x)
|
||||
{
|
||||
memcpy (this, &x, sizeof *this); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
return *this;
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
memset (this, 0, sizeof *this); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
void copy (void *p)
|
||||
{
|
||||
memcpy (this, p, sizeof *this); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
};
|
||||
|
||||
struct DerivesIndidirectlyFromNontrivialClass:
|
||||
TrivialClass<1>, TrivialClass<2>,
|
||||
DerivesFromNontrivialClass,
|
||||
TrivialClass<3>, TrivialClass<4> { };
|
||||
|
||||
/* Verify that raw memory accesses from members of a class with a non-trivial
|
||||
indirect base class is diagnosed. */
|
||||
|
||||
struct HasIndirectNonTrivialBase: TrivialClass<5>, TrivialClass<6>,
|
||||
TrivialClass<7>, TrivialClass<8>,
|
||||
DerivesIndidirectlyFromNontrivialClass
|
||||
{
|
||||
HasIndirectNonTrivialBase ()
|
||||
{
|
||||
memset (this, 0, sizeof *this); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
HasIndirectNonTrivialBase (const HasIndirectNonTrivialBase &x)
|
||||
{
|
||||
memcpy (this, &x, sizeof *this); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
~HasIndirectNonTrivialBase ()
|
||||
{
|
||||
memset (this, 0, sizeof *this); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
HasIndirectNonTrivialBase& operator= (const HasIndirectNonTrivialBase &x)
|
||||
{
|
||||
memcpy (this, &x, sizeof *this); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
return *this;
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
memset (this, 0, sizeof *this); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
|
||||
void copy (void *p)
|
||||
{
|
||||
memcpy (this, p, sizeof *this); // { dg-warning "\\\[-Wclass-memaccess" }
|
||||
}
|
||||
};
|
39
gcc/testsuite/g++.dg/Wclass-memaccess-4.C
Normal file
39
gcc/testsuite/g++.dg/Wclass-memaccess-4.C
Normal file
|
@ -0,0 +1,39 @@
|
|||
/* PR c++/84850 - missing -Wclass-memaccess for a memcpy in a copy ctor
|
||||
with a non-trivial member
|
||||
{ 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);
|
||||
|
||||
struct A
|
||||
{
|
||||
const int &r;
|
||||
|
||||
A ();
|
||||
|
||||
A (const A&);
|
||||
|
||||
virtual ~A ();
|
||||
};
|
||||
|
||||
struct C
|
||||
{
|
||||
A a;
|
||||
|
||||
C (const C&);
|
||||
|
||||
C& operator= (const C&);
|
||||
};
|
||||
|
||||
C::C (const C &c)
|
||||
{
|
||||
memcpy (this, &c, sizeof c); // { dg-warning "\\\[-Wclass-memaccess]" "pr84851" { xfail *-*-*} }
|
||||
}
|
||||
|
||||
C& C::operator= (const C &c)
|
||||
{
|
||||
memcpy (this, &c, sizeof c); // { dg-warning "\\\[-Wclass-memaccess]" }
|
||||
return *this;
|
||||
}
|
Loading…
Add table
Reference in a new issue