Correct/improve maybe_emit_free_warning (PR middle-end/98166, PR c++/57111, PR middle-end/98160).

Resolves:
PR middle-end/98166 - bogus -Wmismatched-dealloc on user-defined allocator and inlining
PR c++/57111 - 57111 - Generalize -Wfree-nonheap-object to delete
PR middle-end/98160 - ICE in default_tree_printer at gcc/tree-diagnostic.c:270

gcc/ChangeLog:

	PR middle-end/98166
	PR c++/57111
	PR middle-end/98160
	* builtins.c (check_access): Call tree_inlined_location
	fndecl_alloc_p): Handle BUILT_IN_ALIGNED_ALLOC and
	BUILT_IN_GOMP_ALLOC.
	call_dealloc_p): Remove unused function.
	(new_delete_mismatch_p): Call valid_new_delete_pair_p and rework.
	(matching_alloc_calls_p): Handle built-in deallocation functions.
	(warn_dealloc_offset): Corrct the handling of user-defined operators
	delete.
	(maybe_emit_free_warning): Avoid assuming expression is a decl.
	Simplify.
	* doc/extend.texi (attribute malloc): Update.
	* tree-ssa-dce.c (valid_new_delete_pair_p): Factor code out into
	valid_new_delete_pair_p in tree.c.
	* tree.c (tree_inlined_location): Define new function.
	(valid_new_delete_pair_p): Define.
	* tree.h (tree_inlined_location): Declare.
	(valid_new_delete_pair_p): Declare.

gcc/c-family/ChangeLog:

	PR middle-end/98166
	PR c++/57111
	PR middle-end/98160
	* c-attribs.c (maybe_add_noinline): New function.
	(handle_malloc_attribute): Call it.  Use ATTR_FLAG_INTERNAL.
	Implicitly add attribute noinline to functions not declared inline
	and warn on those.

libstdc++-v3/ChangeLog:
	* testsuite/ext/vstring/requirements/exception/basic.cc: Suppress
	a false positive warning.
	* testsuite/ext/vstring/requirements/exception/propagation_consistent.cc:
	  Same.

gcc/testsuite/ChangeLog:

	PR middle-end/98166
	PR c++/57111
	PR middle-end/98160
	* g++.dg/warn/Wmismatched-dealloc-2.C: Adjust test of expected warning.
	* g++.dg/warn/Wmismatched-new-delete.C: Same.
	* gcc.dg/Wmismatched-dealloc.c: Same.
	* c-c++-common/Wfree-nonheap-object-2.c: New test.
	* c-c++-common/Wfree-nonheap-object-3.c: New test.
	* c-c++-common/Wfree-nonheap-object.c: New test.
	* c-c++-common/Wmismatched-dealloc.c: New test.
	* g++.dg/warn/Wfree-nonheap-object-3.C: New test.
	* g++.dg/warn/Wfree-nonheap-object-4.C: New test.
	* g++.dg/warn/Wmismatched-dealloc-2.C: New test.
	* g++.dg/warn/Wmismatched-new-delete-2.C: New test.
	* g++.dg/warn/Wmismatched-new-delete.C: New test.
	* gcc.dg/Wmismatched-dealloc-2.c: New test.
	* gcc.dg/Wmismatched-dealloc-3.c: New test.
	* gcc.dg/Wmismatched-dealloc.c: New test.
This commit is contained in:
Martin Sebor 2020-12-14 13:30:00 -07:00
parent e63ae8c083
commit fe7f75cf16
20 changed files with 1661 additions and 254 deletions

View File

@ -4738,9 +4738,7 @@ check_access (tree exp, tree dstwrite,
&& TREE_CODE (range[0]) == INTEGER_CST
&& tree_int_cst_lt (maxobjsize, range[0]))
{
location_t loc = tree_nonartificial_location (exp);
loc = expansion_point_location_if_in_system_header (loc);
location_t loc = tree_inlined_location (exp);
maybe_warn_for_bound (OPT_Wstringop_overflow_, loc, exp, func, range,
NULL_TREE, pad);
return false;
@ -4766,9 +4764,7 @@ check_access (tree exp, tree dstwrite,
|| (pad && pad->dst.ref && TREE_NO_WARNING (pad->dst.ref)))
return false;
location_t loc = tree_nonartificial_location (exp);
loc = expansion_point_location_if_in_system_header (loc);
location_t loc = tree_inlined_location (exp);
bool warned = false;
if (dstwrite == slen && at_least_one)
{
@ -4821,9 +4817,7 @@ check_access (tree exp, tree dstwrite,
PAD is nonnull and BNDRNG is valid. */
get_size_range (maxread, range, pad ? pad->src.bndrng : NULL);
location_t loc = tree_nonartificial_location (exp);
loc = expansion_point_location_if_in_system_header (loc);
location_t loc = tree_inlined_location (exp);
tree size = dstsize;
if (pad && pad->mode == access_read_only)
size = wide_int_to_tree (sizetype, pad->src.sizrng[1]);
@ -4882,9 +4876,7 @@ check_access (tree exp, tree dstwrite,
|| (pad && pad->src.ref && TREE_NO_WARNING (pad->src.ref)))
return false;
location_t loc = tree_nonartificial_location (exp);
loc = expansion_point_location_if_in_system_header (loc);
location_t loc = tree_inlined_location (exp);
const bool read
= mode == access_read_only || mode == access_read_write;
const bool maybe = pad && pad->dst.parmarray;
@ -6381,9 +6373,7 @@ check_strncat_sizes (tree exp, tree objsize)
if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (objsize)
&& tree_int_cst_equal (objsize, maxread))
{
location_t loc = tree_nonartificial_location (exp);
loc = expansion_point_location_if_in_system_header (loc);
location_t loc = tree_inlined_location (exp);
warning_at (loc, OPT_Wstringop_overflow_,
"%K%qD specified bound %E equals destination size",
exp, get_callee_fndecl (exp), maxread);
@ -6456,9 +6446,7 @@ expand_builtin_strncat (tree exp, rtx)
if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (destsize)
&& tree_int_cst_equal (destsize, maxread))
{
location_t loc = tree_nonartificial_location (exp);
loc = expansion_point_location_if_in_system_header (loc);
location_t loc = tree_inlined_location (exp);
warning_at (loc, OPT_Wstringop_overflow_,
"%K%qD specified bound %E equals destination size",
exp, get_callee_fndecl (exp), maxread);
@ -7040,9 +7028,7 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
|| !check_nul_terminated_array (exp, arg2, arg3))
return NULL_RTX;
location_t loc = tree_nonartificial_location (exp);
loc = expansion_point_location_if_in_system_header (loc);
location_t loc = tree_inlined_location (exp);
tree len1 = c_strlen (arg1, 1);
tree len2 = c_strlen (arg2, 1);
@ -12970,7 +12956,9 @@ fndecl_alloc_p (tree fndecl, bool all_alloc)
case BUILT_IN_ALLOCA:
case BUILT_IN_ALLOCA_WITH_ALIGN:
return all_alloc;
case BUILT_IN_ALIGNED_ALLOC:
case BUILT_IN_CALLOC:
case BUILT_IN_GOMP_ALLOC:
case BUILT_IN_MALLOC:
case BUILT_IN_REALLOC:
case BUILT_IN_STRDUP:
@ -13065,12 +13053,119 @@ call_dealloc_argno (tree exp)
return UINT_MAX;
}
/* Return true if STMT is a call to a deallocation function. */
/* Return true if DELETE_DECL is an operator delete that's not suitable
to call with a pointer returned fron NEW_DECL. */
static inline bool
call_dealloc_p (tree exp)
static bool
new_delete_mismatch_p (tree new_decl, tree delete_decl)
{
return call_dealloc_argno (exp) != UINT_MAX;
tree new_name = DECL_ASSEMBLER_NAME (new_decl);
tree delete_name = DECL_ASSEMBLER_NAME (delete_decl);
/* valid_new_delete_pair_p() returns a conservative result. A true
result is reliable but a false result doesn't necessarily mean
the operators don't match. */
if (valid_new_delete_pair_p (new_name, delete_name))
return false;
const char *new_str = IDENTIFIER_POINTER (new_name);
const char *del_str = IDENTIFIER_POINTER (delete_name);
if (*new_str != '_')
return *new_str != *del_str;
++del_str;
if (*++new_str != 'Z')
return *new_str != *del_str;
++del_str;
if (*++new_str == 'n')
return *del_str != 'd';
if (*new_str != 'N')
return *del_str != 'N';
/* Handle user-defined member operators below. */
++new_str;
++del_str;
do
{
/* Determine if both operators are members of the same type.
If not, they don't match. */
char *new_end, *del_end;
unsigned long nlen = strtoul (new_str, &new_end, 10);
unsigned long dlen = strtoul (del_str, &del_end, 10);
if (nlen != dlen)
return true;
/* Skip past the name length. */
new_str = new_end;
del_str = del_end;
/* Skip past the names making sure each has the expected length
(it would suggest some sort of a corruption if they didn't). */
while (nlen--)
if (!*++new_end)
return true;
for (nlen = dlen; nlen--; )
if (!*++del_end)
return true;
/* The names have the expected length. Compare them. */
if (memcmp (new_str, del_str, dlen))
return true;
new_str = new_end;
del_str = del_end;
if (*new_str == 'I')
{
/* Template instantiation. */
do
{
++new_str;
++del_str;
if (*new_str == 'n')
break;
if (*new_str != *del_str)
return true;
}
while (*new_str);
}
if (*new_str == 'n')
{
if (*del_str != 'd')
return true;
++del_str;
if (*++new_str == 'w' && *del_str != 'l')
return true;
if (*new_str == 'a' && *del_str != 'a')
return true;
++new_str;
++del_str;
break;
}
} while (true);
if (*new_str != 'E')
return *del_str != *new_str;
++new_str;
++del_str;
if (*new_str != 'j' && *new_str != 'm' && *new_str != 'y')
return true;
if (*del_str != 'P' || *++del_str != 'v')
return true;
/* Ignore any remaining arguments. Since both operators are members
of the same class, mismatches in those should be detectable and
diagnosed by the front end. */
return false;
}
/* ALLOC_DECL and DEALLOC_DECL are pair of allocation and deallocation
@ -13080,18 +13175,17 @@ call_dealloc_p (tree exp)
static bool
matching_alloc_calls_p (tree alloc_decl, tree dealloc_decl)
{
/* Set to alloc_kind_t::builtin if ALLOC_DECL is associated with
a built-in deallocator. */
enum class alloc_kind_t { none, builtin, user }
alloc_dealloc_kind = alloc_kind_t::none;
if (DECL_IS_OPERATOR_NEW_P (alloc_decl))
{
if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl))
{
/* Return true iff both functions are of the same array or
singleton form and false otherwise. */
tree alloc_id = DECL_NAME (alloc_decl);
tree dealloc_id = DECL_NAME (dealloc_decl);
const char *alloc_fname = IDENTIFIER_POINTER (alloc_id);
const char *dealloc_fname = IDENTIFIER_POINTER (dealloc_id);
return !strchr (alloc_fname, '[') == !strchr (dealloc_fname, '[');
}
/* Return true iff both functions are of the same array or
singleton form and false otherwise. */
return !new_delete_mismatch_p (alloc_decl, dealloc_decl);
/* Return false for deallocation functions that are known not
to match. */
@ -13110,7 +13204,9 @@ matching_alloc_calls_p (tree alloc_decl, tree dealloc_decl)
case BUILT_IN_ALLOCA_WITH_ALIGN:
return false;
case BUILT_IN_ALIGNED_ALLOC:
case BUILT_IN_CALLOC:
case BUILT_IN_GOMP_ALLOC:
case BUILT_IN_MALLOC:
case BUILT_IN_REALLOC:
case BUILT_IN_STRDUP:
@ -13121,6 +13217,8 @@ matching_alloc_calls_p (tree alloc_decl, tree dealloc_decl)
if (fndecl_built_in_p (dealloc_decl, BUILT_IN_FREE)
|| fndecl_built_in_p (dealloc_decl, BUILT_IN_REALLOC))
return true;
alloc_dealloc_kind = alloc_kind_t::builtin;
break;
default:
@ -13128,30 +13226,151 @@ matching_alloc_calls_p (tree alloc_decl, tree dealloc_decl)
}
}
/* If DEALLOC_DECL has internal "*dealloc" attribute scan the list of
its associated allocation functions for ALLOC_DECL. If it's found
they are a matching pair, otherwise they're not. */
tree attrs = DECL_ATTRIBUTES (dealloc_decl);
if (!attrs)
return false;
/* Set if DEALLOC_DECL both allocates and deallocates. */
alloc_kind_t realloc_kind = alloc_kind_t::none;
for (tree funs = attrs;
(funs = lookup_attribute ("*dealloc", funs));
funs = TREE_CHAIN (funs))
if (fndecl_built_in_p (dealloc_decl, BUILT_IN_NORMAL))
{
tree args = TREE_VALUE (funs);
built_in_function dealloc_code = DECL_FUNCTION_CODE (dealloc_decl);
if (dealloc_code == BUILT_IN_REALLOC)
realloc_kind = alloc_kind_t::builtin;
for (tree amats = DECL_ATTRIBUTES (alloc_decl);
(amats = lookup_attribute ("malloc", amats));
amats = TREE_CHAIN (amats))
{
tree args = TREE_VALUE (amats);
if (!args)
continue;
tree fndecl = TREE_VALUE (args);
if (!fndecl || !DECL_P (fndecl))
continue;
if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)
&& dealloc_code == DECL_FUNCTION_CODE (fndecl))
return true;
}
}
const bool alloc_builtin = fndecl_built_in_p (alloc_decl, BUILT_IN_NORMAL);
alloc_kind_t realloc_dealloc_kind = alloc_kind_t::none;
/* If DEALLOC_DECL has an internal "*dealloc" attribute scan the list
of its associated allocation functions for ALLOC_DECL.
If the corresponding ALLOC_DECL is found they're a matching pair,
otherwise they're not.
With DDATS set to the Deallocator's *Dealloc ATtributes... */
for (tree ddats = DECL_ATTRIBUTES (dealloc_decl);
(ddats = lookup_attribute ("*dealloc", ddats));
ddats = TREE_CHAIN (ddats))
{
tree args = TREE_VALUE (ddats);
if (!args)
continue;
tree fname = TREE_VALUE (args);
if (!fname)
tree alloc = TREE_VALUE (args);
if (!alloc)
continue;
if (fname == DECL_NAME (alloc_decl))
if (alloc == DECL_NAME (dealloc_decl))
realloc_kind = alloc_kind_t::user;
if (DECL_P (alloc))
{
gcc_checking_assert (fndecl_built_in_p (alloc, BUILT_IN_NORMAL));
switch (DECL_FUNCTION_CODE (alloc))
{
case BUILT_IN_ALIGNED_ALLOC:
case BUILT_IN_CALLOC:
case BUILT_IN_GOMP_ALLOC:
case BUILT_IN_MALLOC:
case BUILT_IN_REALLOC:
case BUILT_IN_STRDUP:
case BUILT_IN_STRNDUP:
realloc_dealloc_kind = alloc_kind_t::builtin;
break;
default:
break;
}
if (!alloc_builtin)
continue;
if (DECL_FUNCTION_CODE (alloc) != DECL_FUNCTION_CODE (alloc_decl))
continue;
return true;
}
if (alloc == DECL_NAME (alloc_decl))
return true;
}
return false;
if (realloc_kind == alloc_kind_t::none)
return false;
hash_set<tree> common_deallocs;
/* Special handling for deallocators. Iterate over both the allocator's
and the reallocator's associated deallocator functions looking for
the first one in common. If one is found, the de/reallocator is
a match for the allocator even though the latter isn't directly
associated with the former. This simplifies declarations in system
headers.
With AMATS set to the Allocator's Malloc ATtributes,
and RMATS set to Reallocator's Malloc ATtributes... */
for (tree amats = DECL_ATTRIBUTES (alloc_decl),
rmats = DECL_ATTRIBUTES (dealloc_decl);
(amats = lookup_attribute ("malloc", amats))
|| (rmats = lookup_attribute ("malloc", rmats));
amats = amats ? TREE_CHAIN (amats) : NULL_TREE,
rmats = rmats ? TREE_CHAIN (rmats) : NULL_TREE)
{
if (tree args = amats ? TREE_VALUE (amats) : NULL_TREE)
if (tree adealloc = TREE_VALUE (args))
{
if (DECL_P (adealloc)
&& fndecl_built_in_p (adealloc, BUILT_IN_NORMAL))
{
built_in_function fncode = DECL_FUNCTION_CODE (adealloc);
if (fncode == BUILT_IN_FREE || fncode == BUILT_IN_REALLOC)
{
if (realloc_kind == alloc_kind_t::builtin)
return true;
alloc_dealloc_kind = alloc_kind_t::builtin;
}
continue;
}
common_deallocs.add (adealloc);
}
if (tree args = rmats ? TREE_VALUE (rmats) : NULL_TREE)
if (tree ddealloc = TREE_VALUE (args))
{
if (DECL_P (ddealloc)
&& fndecl_built_in_p (ddealloc, BUILT_IN_NORMAL))
{
built_in_function fncode = DECL_FUNCTION_CODE (ddealloc);
if (fncode == BUILT_IN_FREE || fncode == BUILT_IN_REALLOC)
{
if (alloc_dealloc_kind == alloc_kind_t::builtin)
return true;
realloc_dealloc_kind = alloc_kind_t::builtin;
}
continue;
}
if (common_deallocs.add (ddealloc))
return true;
}
}
/* Succeed only if ALLOC_DECL and the reallocator DEALLOC_DECL share
a built-in deallocator. */
return (alloc_dealloc_kind == alloc_kind_t::builtin
&& realloc_dealloc_kind == alloc_kind_t::builtin);
}
/* Return true if DEALLOC_DECL is a function suitable to deallocate
@ -13167,15 +13386,36 @@ matching_alloc_calls_p (gimple *alloc, tree dealloc_decl)
return matching_alloc_calls_p (alloc_decl, dealloc_decl);
}
/* Diagnose a call to FNDECL to deallocate a pointer referenced by
AREF that includes a nonzero offset. Such a pointer cannot refer
to the beginning of an allocated object. A negative offset may
refer to it only if the target pointer is unknown. */
/* Diagnose a call EXP to deallocate a pointer referenced by AREF if it
includes a nonzero offset. Such a pointer cannot refer to the beginning
of an allocated object. A negative offset may refer to it only if
the target pointer is unknown. */
static bool
warn_dealloc_offset (location_t loc, tree exp, tree fndecl,
const access_ref &aref)
warn_dealloc_offset (location_t loc, tree exp, const access_ref &aref)
{
if (aref.deref || aref.offrng[0] <= 0 || aref.offrng[1] <= 0)
return false;
tree dealloc_decl = get_callee_fndecl (exp);
if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
&& !DECL_IS_REPLACEABLE_OPERATOR (dealloc_decl))
{
/* A call to a user-defined operator delete with a pointer plus offset
may be valid if it's returned from an unknown function (i.e., one
that's not operator new). */
if (TREE_CODE (aref.ref) == SSA_NAME)
{
gimple *def_stmt = SSA_NAME_DEF_STMT (aref.ref);
if (is_gimple_call (def_stmt))
{
tree alloc_decl = gimple_call_fndecl (def_stmt);
if (!DECL_IS_OPERATOR_NEW_P (alloc_decl))
return false;
}
}
}
char offstr[80];
offstr[0] = '\0';
if (wi::fits_shwi_p (aref.offrng[0]))
@ -13192,7 +13432,7 @@ warn_dealloc_offset (location_t loc, tree exp, tree fndecl,
if (!warning_at (loc, OPT_Wfree_nonheap_object,
"%K%qD called on pointer %qE with nonzero offset%s",
exp, fndecl, aref.ref, offstr))
exp, dealloc_decl, aref.ref, offstr))
return false;
if (DECL_P (aref.ref))
@ -13202,9 +13442,16 @@ warn_dealloc_offset (location_t loc, tree exp, tree fndecl,
gimple *def_stmt = SSA_NAME_DEF_STMT (aref.ref);
if (is_gimple_call (def_stmt))
{
location_t def_loc = gimple_location (def_stmt);
tree alloc_decl = gimple_call_fndecl (def_stmt);
inform (gimple_location (def_stmt),
"returned from a call to %qD", alloc_decl);
if (alloc_decl)
inform (def_loc,
"returned from %qD", alloc_decl);
else if (tree alloc_fntype = gimple_call_fntype (def_stmt))
inform (def_loc,
"returned from %qT", alloc_fntype);
else
inform (def_loc, "obtained here");
}
}
@ -13240,8 +13487,7 @@ maybe_emit_free_warning (tree exp)
return;
tree dealloc_decl = get_callee_fndecl (exp);
location_t loc = tree_nonartificial_location (exp);
loc = expansion_point_location_if_in_system_header (loc);
location_t loc = tree_inlined_location (exp);
if (DECL_P (ref) || EXPR_P (ref))
{
@ -13251,18 +13497,18 @@ maybe_emit_free_warning (tree exp)
"%K%qD called on unallocated object %qD",
exp, dealloc_decl, ref))
{
inform (DECL_SOURCE_LOCATION (ref),
"declared here");
loc = (DECL_P (ref)
? DECL_SOURCE_LOCATION (ref)
: EXPR_LOCATION (ref));
inform (loc, "declared here");
return;
}
/* Diagnose freeing a pointer that includes a positive offset.
Such a pointer cannot refer to the beginning of an allocated
object. A negative offset may refer to it. */
if (!aref.deref
&& aref.sizrng[0] != aref.sizrng[1]
&& aref.offrng[0] > 0 && aref.offrng[1] > 0
&& warn_dealloc_offset (loc, exp, dealloc_decl, aref))
if (aref.sizrng[0] != aref.sizrng[1]
&& warn_dealloc_offset (loc, exp, aref))
return;
}
else if (CONSTANT_CLASS_P (ref))
@ -13295,9 +13541,7 @@ maybe_emit_free_warning (tree exp)
{
if (matching_alloc_calls_p (def_stmt, dealloc_decl))
{
if (!aref.deref
&& aref.offrng[0] > 0 && aref.offrng[1] > 0
&& warn_dealloc_offset (loc, exp, dealloc_decl, aref))
if (warn_dealloc_offset (loc, exp, aref))
return;
}
else
@ -13320,16 +13564,14 @@ maybe_emit_free_warning (tree exp)
"%K%qD called on pointer to "
"an unallocated object",
exp, dealloc_decl);
else if (!aref.deref
&& aref.offrng[0] > 0 && aref.offrng[1] > 0
&& warn_dealloc_offset (loc, exp, dealloc_decl, aref))
else if (warn_dealloc_offset (loc, exp, aref))
return;
if (warned)
{
tree fndecl = gimple_call_fndecl (def_stmt);
inform (gimple_location (def_stmt),
"returned from a call to %qD", fndecl);
"returned from %qD", fndecl);
return;
}
}
@ -13341,7 +13583,7 @@ maybe_emit_free_warning (tree exp)
&& !aref.deref
&& aref.sizrng[0] != aref.sizrng[1]
&& aref.offrng[0] > 0 && aref.offrng[1] > 0
&& warn_dealloc_offset (loc, exp, dealloc_decl, aref))
&& warn_dealloc_offset (loc, exp, aref))
return;
}
}

View File

@ -3130,12 +3130,64 @@ handle_no_profile_instrument_function_attribute (tree *node, tree name, tree,
return NULL_TREE;
}
/* If ALLOC_DECL and DEALLOC_DECL are a pair of user-defined functions,
if they are declared inline issue warnings and return null. Otherwise
create attribute noinline, install it in ALLOC_DECL, and return it.
Otherwise return null. */
static tree
maybe_add_noinline (tree name, tree alloc_decl, tree dealloc_decl,
bool *no_add_attrs)
{
if (fndecl_built_in_p (alloc_decl) || fndecl_built_in_p (dealloc_decl))
return NULL_TREE;
/* When inlining (or optimization) is enabled and the allocator and
deallocator are not built-in functions, ignore the attribute on
functions declared inline since it could lead to false positives
when inlining one or the other call would wind up calling
a mismatched allocator or deallocator. */
if ((optimize && DECL_DECLARED_INLINE_P (alloc_decl))
|| lookup_attribute ("always_inline", DECL_ATTRIBUTES (alloc_decl)))
{
warning (OPT_Wattributes,
"%<%E (%E)%> attribute ignored on functions "
"declared %qs", name, DECL_NAME (dealloc_decl), "inline");
*no_add_attrs = true;
return NULL_TREE;
}
if ((optimize && DECL_DECLARED_INLINE_P (dealloc_decl))
|| lookup_attribute ("always_inline", DECL_ATTRIBUTES (dealloc_decl)))
{
warning (OPT_Wattributes,
"%<%E (%E)%> attribute ignored with deallocation "
"functions declared %qs",
name, DECL_NAME (dealloc_decl), "inline");
inform (DECL_SOURCE_LOCATION (dealloc_decl),
"deallocation function declared here" );
*no_add_attrs = true;
return NULL_TREE;
}
/* Disable inlining for non-standard deallocators to avoid false
positives due to mismatches between the inlined implementation
of one and not the other pair of functions. */
tree attr = tree_cons (get_identifier ("noinline"), NULL_TREE, NULL_TREE);
decl_attributes (&alloc_decl, attr, 0);
return attr;
}
/* Handle the "malloc" attribute. */
static tree
handle_malloc_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags), bool *no_add_attrs)
handle_malloc_attribute (tree *node, tree name, tree args, int flags,
bool *no_add_attrs)
{
if (flags & ATTR_FLAG_INTERNAL)
/* Recursive call. */
return NULL_TREE;
tree fndecl = *node;
if (TREE_CODE (*node) != FUNCTION_DECL)
@ -3174,11 +3226,21 @@ handle_malloc_attribute (tree *node, tree name, tree args,
return NULL_TREE;
}
/* In C++ the argument may be wrapped in a cast to disambiguate one
of a number of overloads (such as operator delete). Strip it. */
STRIP_NOPS (dealloc);
if (TREE_CODE (dealloc) == ADDR_EXPR)
dealloc = TREE_OPERAND (dealloc, 0);
{
/* In C++ the argument may be wrapped in a cast to disambiguate
one of a number of overloads (such as operator delete). To
make things interesting, the cast looks different between
different C++ versions. Strip it and install the attribute
with the disambiguated function. */
dealloc = TREE_OPERAND (dealloc, 0);
*no_add_attrs = true;
tree attr = tree_cons (NULL_TREE, dealloc, TREE_CHAIN (args));
attr = build_tree_list (name, attr);
return decl_attributes (node, attr, 0);
}
if (TREE_CODE (dealloc) != FUNCTION_DECL)
{
@ -3233,10 +3295,21 @@ handle_malloc_attribute (tree *node, tree name, tree args,
return NULL_TREE;
}
*no_add_attrs = false;
tree attr_free = build_tree_list (NULL_TREE, DECL_NAME (fndecl));
attr_free = build_tree_list (get_identifier ("*dealloc"), attr_free);
decl_attributes (&dealloc, attr_free, 0);
/* Disable inlining for non-standard deallocators to avoid false
positives (or warn if either function is explicitly inline). */
tree at_noinline =
maybe_add_noinline (name, fndecl, dealloc, no_add_attrs);
if (*no_add_attrs)
return NULL_TREE;
/* Add attribute *dealloc to the deallocator function associating
it with this one. Ideally, the attribute would reference
the DECL of the deallocator but since that changes for each
redeclaration, use DECL_NAME instead. (DECL_ASSEMBLER_NAME
need not be set set this point and setting it here is too early. */
tree attrs = build_tree_list (NULL_TREE, DECL_NAME (fndecl));
attrs = tree_cons (get_identifier ("*dealloc"), attrs, at_noinline);
decl_attributes (&dealloc, attrs, 0);
return NULL_TREE;
}
@ -3248,15 +3321,21 @@ handle_malloc_attribute (tree *node, tree name, tree args,
return NULL_TREE;
}
/* As above, disable inlining for non-standard deallocators to avoid
false positives (or warn). */
tree at_noinline =
maybe_add_noinline (name, fndecl, dealloc, no_add_attrs);
if (*no_add_attrs)
return NULL_TREE;
/* It's valid to declare the same function with multiple instances
of attribute malloc, each naming the same or different deallocator
functions, and each referencing either the same or a different
positional argument. */
*no_add_attrs = false;
tree attr_free = tree_cons (NULL_TREE, argpos, NULL_TREE);
attr_free = tree_cons (NULL_TREE, DECL_NAME (fndecl), attr_free);
attr_free = build_tree_list (get_identifier ("*dealloc"), attr_free);
decl_attributes (&dealloc, attr_free, 0);
tree attrs = tree_cons (NULL_TREE, argpos, NULL_TREE);
attrs = tree_cons (NULL_TREE, DECL_NAME (fndecl), attrs);
attrs = tree_cons (get_identifier ("*dealloc"), attrs, at_noinline);
decl_attributes (&dealloc, attrs, 0);
return NULL_TREE;
}
@ -3274,11 +3353,13 @@ handle_dealloc_attribute (tree *node, tree name, tree args, int,
if (!attrs)
return NULL_TREE;
tree arg_fname = TREE_VALUE (args);
tree arg = TREE_VALUE (args);
args = TREE_CHAIN (args);
tree arg_pos = args ? TREE_VALUE (args) : NULL_TREE;
tree arg_pos = args ? TREE_VALUE (args) : integer_zero_node;
gcc_checking_assert (TREE_CODE (arg_fname) == IDENTIFIER_NODE);
gcc_checking_assert ((DECL_P (arg)
&& fndecl_built_in_p (arg, BUILT_IN_NORMAL))
|| TREE_CODE (arg) == IDENTIFIER_NODE);
const char* const namestr = IDENTIFIER_POINTER (name);
for (tree at = attrs; (at = lookup_attribute (namestr, at));
@ -3290,12 +3371,12 @@ handle_dealloc_attribute (tree *node, tree name, tree args, int,
tree pos = TREE_CHAIN (alloc);
alloc = TREE_VALUE (alloc);
pos = pos ? TREE_VALUE (pos) : NULL_TREE;
gcc_checking_assert (TREE_CODE (alloc) == IDENTIFIER_NODE);
pos = pos ? TREE_VALUE (pos) : integer_zero_node;
gcc_checking_assert ((DECL_P (alloc)
&& fndecl_built_in_p (alloc, BUILT_IN_NORMAL))
|| TREE_CODE (alloc) == IDENTIFIER_NODE);
if (alloc == arg_fname
&& ((!pos && !arg_pos)
|| (pos && arg_pos && tree_int_cst_equal (pos, arg_pos))))
if (alloc == arg && tree_int_cst_equal (pos, arg_pos))
{
/* The function already has the attribute either without any
arguments or with the same arguments as the attribute that's

View File

@ -3257,37 +3257,37 @@ as they may return pointers to storage containing pointers to existing
objects.
Associating a function with a @var{deallocator} helps detect calls to
mismatched allocation and deallocation functions and diagnose them
under the control of options such as @option{-Wmismatched-dealloc}.
To indicate that an allocation function both satisifies the nonaliasing
property and has a deallocator associated with it, both the plain form
of the attribute and the one with the @var{deallocator} argument must
be used.
mismatched allocation and deallocation functions and diagnose them under
the control of options such as @option{-Wmismatched-dealloc}. To indicate
that an allocation function both satisifies the nonaliasing property and
has a deallocator associated with it, both the plain form of the attribute
and the one with the @var{deallocator} argument must be used. The same
function can be both an allocator and a deallocator. Since inlining one
of the associated functions but not the other could result in apparent
mismatches, this form of attribute @code{malloc} is not accepted on inline
functions. For the same reason, using the attribute prevents both
the allocation and deallocation functions from being expanded inline.
For example, besides stating that the functions return pointers that do
not alias any others, the following declarations make the @code{fclose}
and @code{frepen} functions suitable deallocators for pointers returned
from all the functions that return them, and the @code{pclose} function
as the only other suitable deallocator besides @code{freopen} for pointers
returned from @code{popen}. The deallocator functions must declared
before they can be referenced in the attribute.
not alias any others, the following declarations make @code{fclose}
a suitable deallocator for pointers returned from all functions except
@code{popen}, and @code{pclose} as the only suitable deallocator for
pointers returned from @code{popen}. The deallocator functions must
declared before they can be referenced in the attribute.
@smallexample
int fclose (FILE*);
FILE* freopen (const char*, const char*, FILE*);
int pclose (FILE*);
int fclose (FILE*);
int pclose (FILE*);
__attribute__ ((malloc, malloc (fclose), malloc (freopen, 3)))
__attribute__ ((malloc, malloc (fclose (1))))
FILE* fdopen (int);
__attribute__ ((malloc, malloc (fclose), malloc (freopen, 3)))
__attribute__ ((malloc, malloc (fclose (1))))
FILE* fopen (const char*, const char*);
__attribute__ ((malloc, malloc (fclose), malloc (freopen, 3)))
__attribute__ ((malloc, malloc (fclose (1))))
FILE* fmemopen(void *, size_t, const char *);
__attribute__ ((malloc, malloc (fclose), malloc (freopen, 3)))
FILE* freopen (const char*, const char*, FILE*);
__attribute__ ((malloc, malloc (pclose), malloc (freopen, 3)))
__attribute__ ((malloc, malloc (pclose (1))))
FILE* popen (const char*, const char*);
__attribute__ ((malloc, malloc (fclose), malloc (freopen, 3)))
__attribute__ ((malloc, malloc (fclose (1))))
FILE* tmpfile (void);
@end smallexample

View File

@ -0,0 +1,52 @@
/* PR middle-end/98166: bogus -Wmismatched-dealloc on user-defined allocator
and inlining
Verify that the allocator can be declared inline without a warning when
it's associated with a standard deallocator. Associating an inline
deallocator with an allocator would cause false positives when the former
calls a deallocation function the allocator isn't associated with, so
that triggers a warning on declaration.
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
__attribute__ ((malloc (__builtin_free)))
inline int*
alloc_int (int n)
{
return (int*)__builtin_malloc (n + sizeof (int));
}
void test_nowarn_int (int n)
{
{
int *p = alloc_int (n);
__builtin_free (p);
}
{
int *p = alloc_int (n);
__builtin_free (p + 1); // { dg-warning "\\\[-Wfree-nonheap-object" }
}
}
inline void
dealloc_long (long *p)
{
__builtin_free (p); // { dg-warning "'__builtin_free|void __builtin_free\\(void\\*\\)' called on pointer 'p|<unknown>' with nonzero offset" }
}
__attribute__ ((malloc (dealloc_long)))
long* alloc_long (int); // { dg-warning "'malloc \\\(dealloc_long\\\)' attribute ignored with deallocation functions declared 'inline'" }
void test_nowarn_long (int n)
{
{
long *p = alloc_long (n);
dealloc_long (p);
}
{
long *p = alloc_long (n);
dealloc_long (p + 1);
}
}

View File

@ -0,0 +1,70 @@
/* PR middle-end/98166: bogus -Wmismatched-dealloc on user-defined allocator
and inlining
Verify that without inlining, both the allocator and the deallocator
can be declared inline without a warning and that mismatched calls are
detected, but that declaring them always_inline does trigger a warning.
{ dg-do compile }
{ dg-options "-Wall" } */
__attribute__ ((malloc (__builtin_free)))
inline int*
alloc_int (int n)
{
return (int*)__builtin_malloc (n + sizeof (int));
}
void test_nowarn_int (int n)
{
{
int *p = alloc_int (n);
__builtin_free (p);
}
{
int *p = alloc_int (n);
__builtin_free (p + 1); // { dg-warning "'__builtin_free|void __builtin_free\\(void\\*\\)' called on pointer 'p|<unknown>' with nonzero offset" }
}
}
inline void
dealloc_long (long *p) { __builtin_free (p); }
__attribute__ ((malloc (dealloc_long)))
long* alloc_long (int);
void test_nowarn_long (int n)
{
{
long *p = alloc_long (n);
dealloc_long (p);
}
{
long *p = alloc_long (n);
dealloc_long (p + 1); // { dg-warning "'dealloc_long' called on pointer 'p|<unknown>' with nonzero offset" }
}
}
inline __attribute__ ((always_inline)) void
dealloc_float (float *p) // { dg-message "deallocation function declared here" }
{
__builtin_free (p); // { dg-warning "'__builtin_free|void __builtin_free\\(void\\*\\)' called on pointer 'p|<unknown>' with nonzero offset" }
}
__attribute__ ((malloc (dealloc_float)))
float* alloc_float (int); // { dg-warning "'malloc \\(dealloc_float\\)' attribute ignored with deallocation functions declared 'inline'" }
void test_nowarn_float (int n)
{
{
float *p = alloc_float (n);
dealloc_float (p);
}
{
float *p = alloc_float (n);
dealloc_float (p + 2);
}
}

View File

@ -0,0 +1,50 @@
/* Verify that built-in forms of functions can be used interchangeably
with their ordinary (library) forms in attribute malloc.
{ dg-do compile }
{ dg-options "-Wall" } */
char* f (void) __attribute__ ((malloc (__builtin_free)));
#if __cplusplus
extern "C" {
#endif
void free (void*);
#if __cplusplus
}
#endif
char* g (void) __attribute__ ((malloc (free)));
void test_nowarm (void)
{
char *p = f ();
free (p);
p = g ();
free (p);
p = f ();
__builtin_free (p);
p = g ();
__builtin_free (p);
}
void test_warn (void)
{
char *p = f ();
free (p + 1); // { dg-warning "'free|void free\\(void\\*\\)' called on pointer 'p|<unknown>' with nonzero offset" }
p = g ();
free (p + 2); // { dg-warning "'free|void free\\(void\\*\\)' called on pointer 'p|<unknown>' with nonzero offset" }
p = f ();
__builtin_free (p + 3); // { dg-warning "'__builtin_free|void __builtin_free\\(void\\*\\)' called on pointer 'p|<unknown>' with nonzero offset" }
p = g ();
__builtin_free (p + 4); // { dg-warning "'__builtin_free|void __builtin_free\\(void\\*\\)' called on pointer 'p|<unknown>' with nonzero offset" }
}

View File

@ -0,0 +1,67 @@
/* PR middle-end/98166: bogus -Wmismatched-dealloc on user-defined allocator
and inlining
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
void dealloc_shrt (short *p)
{
/* A positive offset would be diagnosed but a negative one must
not be. */
__builtin_free (p - 1); // { dg-bogus "-Wmismatched-dealloc" }
}
__attribute__ ((malloc (dealloc_shrt)))
short* alloc_shrt (int n) /* { return malloc (n) + 1; } */;
void test_nowarn_shrt (int n)
{
short *p = alloc_shrt (n);
dealloc_shrt (p);
}
void dealloc_int (int *p) /* { free (p - 1); } */;
__attribute__ ((malloc (dealloc_int, 1)))
int* alloc_int (int n)
{
return (int*)__builtin_malloc (n) + 1;
}
void test_nowarn_int (int n)
{
int *p = alloc_int (n);
dealloc_int (p); // { dg-bogus "-Wmismatched-dealloc" }
}
void dealloc_long (int, long *p) /* { free (p - 2); } */;
__attribute__ ((malloc (dealloc_long, 2)))
inline long*
alloc_long (int n) { // { dg-warning "'malloc \\(\[^\n\r\]*dealloc_long\[^\n\r\]*\\)' attribute ignored on functions declared 'inline'" }
return (long*)__builtin_malloc (n) + 2;
}
void test_nowarn_long (int n)
{
long *p = alloc_long (n);
dealloc_long (0, p); // { dg-bogus "\\\[-Wmismatched-dealloc" }
}
inline void
dealloc_float (int, int, float *p) // { dg-message "deallocation function declared here" }
{
__builtin_free (p - 3);
}
__attribute__ ((malloc (dealloc_float, 3)))
float* alloc_float (int n); // { dg-warning "'malloc \\(\[^\n\r\]*dealloc_float\[^\n\r\]*\\)' attribute ignored with deallocation functions declared 'inline'" }
void test_nowarn_float (int n)
{
float *p = alloc_float (n);
dealloc_float (0, 1, p); // { dg-bogus "\\\[-Wmismatched-dealloc" }
}

View File

@ -0,0 +1,38 @@
/* PR c++/57111 - Generalize -Wfree-nonheap-object to delete
Verify that even without -Wsystem-headers the warning is issued
for pairs of library functions defined in system headers.
{ dg-do compile { target c++11 } }
{ dg-options "-O2 -Wall" } */
#include <memory>
#include <string>
void test_string ()
{
std::string str ("abc"); // { dg-message "declared here" }
const char *s = str.c_str ();
__builtin_printf ("%s\n", s);
/* Because the delete call is made directly in the function this
does not exercise the same thing as test_unique_ptr. */
delete s; // { dg-warning "'void operator delete\\(void\\*\[^\\)\]*\\)' called on unallocated object 'str'" }
}
void test_unique_ptr ()
{
int arr[]= { 1, 2 }; // { dg-message "declared here" }
std::unique_ptr<int[]> up (arr);
__builtin_printf ("%i %i\n", up[0], up[1]);
/* TO DO: verify that the warning is printed, including its inlining
context (the directive below doesn't work):
{ Xdg-message "In member function.*inlined from 'void test_unique_ptr\\(\\)'.*warning: 'void operator delete \\\[]\\(void\\*\\)' called on unallocated object 'arr'" "" { target *-*-* } 0 } */
/* Here, the delete call is made indirectly from std::unique_ptr
dtor. */
}
/* Prune out the warning from test_unique_ptr().
{ dg-prune-output "-Wfree-nonheap-object" } */

View File

@ -0,0 +1,26 @@
/* PR middle-end/98160: bogus -Wfree-nonheap-object calling member delete
on the result of inline member new plus offset
{ dg-do compile }
{ dg-options "-O2" } */
struct MemoryManager { void* allocate (); };
struct XMemory
{
void* operator new (__SIZE_TYPE__, MemoryManager *mgr)
{
void *p = mgr->allocate ();
return (char*)p + sizeof(MemoryManager);
}
void operator delete (void*, MemoryManager*);
};
struct XMLMutex: XMemory {
XMLMutex();
};
void gValidatorMutex (MemoryManager *mgr)
{
new (mgr) XMLMutex; // { dg-bogus "\\\[-Wfree-nonheap-object" }
}

View File

@ -59,13 +59,13 @@ void test_my_new ()
{
void *p = my_new (1);
// { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
operator delete[] (p);
// { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
}
{
void *p = my_new (1);
// { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
sink (p);
operator delete[] (p);
// { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
@ -89,7 +89,7 @@ void test_my_new ()
{
void *p = my_new (1);
// { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
sink (p);
my_array_delete ("3", p);
// { dg-warning "'void my_array_delete\\\(const char\\\*, void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
@ -97,7 +97,7 @@ void test_my_new ()
{
void *p = my_new (1);
// { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
sink (p);
free (p);
// { dg-warning "'void free\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
@ -105,7 +105,7 @@ void test_my_new ()
{
void *p = my_new (1);
// { dg-message "returned from a call to 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'int\\\* my_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
sink (p);
p = realloc (p, 123);
// { dg-warning "'void\\\* realloc\\\(void\\\*, size_t\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
@ -132,13 +132,13 @@ void test_my_array_new ()
{
void *p = my_array_new (1);
// { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
operator delete (p);
// { dg-warning "'void operator delete\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
}
{
void *p = my_array_new (1);
// { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
sink (p);
operator delete (p);
// { dg-warning "'void operator delete\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function \\\[-Wmismatched-new-delete" "" { target *-*-* } .-1 }
@ -161,7 +161,7 @@ void test_my_array_new ()
}
{
void *p = my_array_new (1);
// { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
sink (p);
my_delete ("3", p);
// { dg-warning "'void my_delete\\\(const char\\\*, void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
@ -169,7 +169,7 @@ void test_my_array_new ()
{
void *p = my_array_new (1);
// { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
sink (p);
free (p);
// { dg-warning "'void free\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
@ -177,7 +177,7 @@ void test_my_array_new ()
{
void *p = my_array_new (1);
// { dg-message "returned from a call to 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'int\\\* my_array_new\\\(size_t\\\)'" "note" { target *-*-* } .-1 }
sink (p);
p = realloc (p, 123);
// { dg-warning "'void\\\* realloc\\\(void\\\*, size_t\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }

View File

@ -0,0 +1,249 @@
/* Verify that implicit and explicit calls to member operator new and delete
are handled correctly.
{ dg-do compile }
{ dg-options "-Wmismatched-new-delete" } */
typedef __SIZE_TYPE__ size_t;
namespace std
{
#if __cplusplus >= 201703L
enum class align_val_t: size_t { };
#else
enum align_val_t { };
#endif
struct nothrow_t { };
const nothrow_t nothrow = { };
}
void sink (void*, ...);
struct POD
{
void* operator new (size_t);
void operator delete (void*);
void* operator new[] (size_t);
void operator delete[] (void*);
};
POD* nowarn_pod ()
{
POD *p = new POD;
delete p;
return new POD;
}
void warn_pod_array_mismatch ()
{
POD *p = new POD;
delete[] p; // { dg-warning "'static void POD::operator delete \\\[]\\(void\\*\\)' called on pointer returned from a mismatched allocation function" }
p = new POD[3];
delete p; // { dg-warning "'static void POD::operator delete\\(void\\*\\)' called on pointer returned from a mismatched allocation function" }
}
struct X1
{
X1 ();
void* operator new (size_t);
void* operator new (size_t, std::align_val_t);
void* operator new (size_t, std::nothrow_t) throw ();
void* operator new (size_t, std::align_val_t, std::nothrow_t) throw ();
void* operator new[] (size_t);
void* operator new[] (size_t, std::align_val_t);
void* operator new[] (size_t, std::nothrow_t) throw ();
void* operator new[] (size_t, std::align_val_t, std::nothrow_t) throw ();
void operator delete (void*);
void operator delete (void*, size_t);
void operator delete (void*, std::align_val_t);
void operator delete (void*, size_t, std::align_val_t);
void operator delete (void*, std::nothrow_t) throw ();
void operator delete (void*, std::align_val_t, std::nothrow_t) throw ();
void operator delete[] (void*);
void operator delete[] (void*, size_t);
void operator delete[] (void*, std::align_val_t);
void operator delete[] (void*, size_t, std::align_val_t);
void operator delete[] (void*, std::nothrow_t) throw ();
void operator delete[] (void*, std::align_val_t, std::nothrow_t) throw ();
};
X1* nowarn_x1 ()
{
return new X1;
}
X1* nowarn_x1_array ()
{
return new X1[2];
}
X1* nowarn_align_val ()
{
X1 *p = new (std::align_val_t (32)) X1;
delete p;
return new (std::align_val_t (64)) X1;
}
X1* nowarn_align_val_array ()
{
X1 *p = new (std::align_val_t (32)) X1[2];
delete[] p;
return new (std::align_val_t (64)) X1[2];
}
X1* nowarn_x1_nothrow ()
{
X1 *p = new (std::nothrow) X1;
delete p;
return new (std::nothrow) X1;
}
X1* nowarn_x1_nothrow_array ()
{
X1 *p = new (std::nothrow) X1[3];
delete[] p;
return new (std::nothrow) X1[3];
}
X1* nowarn_align_val_nothrow ()
{
X1 *p = new (std::align_val_t (32), std::nothrow) X1;
delete p;
return new (std::align_val_t (64), std::nothrow) X1;
}
X1* nowarn_align_val_nothrow_array ()
{
X1 *p = new (std::align_val_t (32), std::nothrow) X1[4];
delete[] p;
return new (std::align_val_t (64), std::nothrow) X1[4];
}
void warn_x1_array_mismatch ()
{
{
X1 *p = new X1;
delete[] p; // { dg-warning "'static void X1::operator delete \\\[]\\(void\\*\\)' called on pointer returned from a mismatched allocation function" }
}
{
X1 *p = new X1[2];
delete p; // { dg-warning "'static void X1::operator delete\\(void\\*\\)' called on pointer returned from a mismatched allocation function" }
}
{
X1 *p = new (std::align_val_t (32)) X1[2];
delete p; // { dg-warning "'static void X1::operator delete\\(void\\*\\)' called on pointer returned from a mismatched allocation function" }
}
{
// The following requires optimization (see warn_x1_array_mismatch()).
X1 *p = new (std::nothrow) X1[3];
delete p; // { dg-warning "'static void X1::operator delete\\(void\\*\\)' called on pointer returned from a mismatched allocation function" "pr?????" { xfail *-*-* } }
}
}
#pragma GCC push_options
#pragma GCC optimize "1"
void warn_x1_nothrow_array_mismatch ()
{
X1 *p = new (std::nothrow) X1[3];
delete p; // { dg-warning "'static void X1::operator delete\\(void\\*\\)' called on pointer returned from a mismatched allocation function" }
}
#pragma GCC pop_options
struct X2: X1
{
X2 ();
void* operator new (size_t);
void operator delete (void*);
};
X2* nowarn_x2 ()
{
X2 *p = new X2;
sink (p);
return new X2;
}
void warn_x2 ()
{
X1 *p = new X2; // { dg-message "returned from 'static void\\* X2::operator new\\(size_t\\)'" "note" }
sink (p);
delete p; // { dg-warning "'static void X1::operator delete\\(void\\*\\)' called on pointer returned from a mismatched allocation function" }
}
namespace N {
namespace NS {
namespace NmSpc {
namespace NameSpace {
namespace dl { // same name as operator delete
namespace nw { // and as operator new
struct X3: X2
{
X3 ();
void* operator new (size_t);
void operator delete (void*);
};
X3* nowarn_x3 ()
{
X3 *p = new X3;
sink (p);
return new X3;
}
void warn_x3 ()
{
X1 *p = new X3; // { dg-message "returned from 'static void\\* N::NS::NmSpc::NameSpace::dl::nw::X3::operator new\\(size_t\\)'" "note" }
sink (p);
delete p; // { dg-warning "'static void X1::operator delete\\(void\\*\\)' called on pointer returned from a mismatched allocation function" }
}
template <int N>
struct X4: X2
{
X4 ();
void* operator new (size_t);
void operator delete (void*);
};
void* nowarn_x4 ()
{
X4<0> *p = new X4<0>;
sink (p);
return new X4<1>;
}
void warn_x4 ()
{
X1 *p = new X4<1>; // { dg-message "returned from 'static void\\* N::NS::NmSpc::NameSpace::dl::nw::X4<N>::operator new\\(size_t\\) \\\[with int N = 1]'" "note" }
sink (p);
delete p; // { dg-warning "'static void X1::operator delete\\(void\\*\\)' called on pointer returned from a mismatched allocation function" }
}
void warn_x4_inst_mismatch ()
{
void *p = new X4<2>; // { dg-message "returned from 'static void\\* N::NS::NmSpc::NameSpace::dl::nw::X4<N>::operator new\\(size_t\\) \\\[with int N = 2]'" "note" }
sink (p);
X4<3> *q = (X4<3>*)p;
delete q; // { dg-warning "'static void N::NS::NmSpc::NameSpace::dl::nw::X4<N>::operator delete\\(void\\*\\) \\\[with int N = 3]' called on pointer returned from a mismatched allocation function" }
}
} // nw
} // dl
} // NameSpace
} // NmSpc
} // NS
} // N

View File

@ -44,14 +44,14 @@ void warn_new_free (int n)
{
{
void *p = operator new (n);
// { dg-message "returned from a call to 'void\\\* operator new\\\(" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'void\\\* operator new\\\(" "note" { target *-*-* } .-1 }
sink (p);
free (p);
// { dg-warning "'void free\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
}
{
char *p = new char[n];
// { dg-message "returned from a call to 'void\\\* operator new \\\[" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'void\\\* operator new \\\[" "note" { target *-*-* } .-1 }
sink (p);
free (p);
// { dg-warning "'void free\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
@ -66,7 +66,7 @@ void warn_new_realloc (int n)
{
{
void *p = operator new (n);
// { dg-message "returned from a call to 'void\\\* operator new\\\(" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'void\\\* operator new\\\(" "note" { target *-*-* } .-1 }
sink (p);
p = realloc (p, n * 2);
// { dg-warning "'void\\\* realloc\\\(\[^)\]+\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
@ -74,7 +74,7 @@ void warn_new_realloc (int n)
}
{
void *p = new char[n];
// { dg-message "returned from a call to 'void\\\* operator new \\\[" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'void\\\* operator new \\\[" "note" { target *-*-* } .-1 }
sink (p);
p = realloc (p, n * 2);
// { dg-warning "'void\\\* realloc\\\(\[^)\]+\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
@ -89,7 +89,7 @@ void warn_new_realloc (int n)
void warn_malloc_op_delete (int n)
{
char *p = (char *)malloc (n);
// { dg-message "returned from a call to 'void\\\* malloc\\\(" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'void\\\* malloc\\\(" "note" { target *-*-* } .-1 }
sink (p);
operator delete (p);
// { dg-warning "'void operator delete\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
@ -97,13 +97,13 @@ void warn_malloc_op_delete (int n)
/* Verify a warning for an invocation of either form of the delete
expression with a pointer returned from a call to malloc(). */
expression with a pointer returned from malloc(). */
void warn_malloc_delete (int n)
{
{
char *p = (char *)malloc (n);
// { dg-message "returned from a call to 'void\\\* malloc\\\(" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'void\\\* malloc\\\(" "note" { target *-*-* } .-1 }
sink (p);
/* C++98 calls operator delete (void*) but later versions call
operator delete (void*, size_t). The difference doesn't matter
@ -114,7 +114,7 @@ void warn_malloc_delete (int n)
{
char *p = (char *)malloc (n);
// { dg-message "returned from a call to 'void\\\* malloc\\\(" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'void\\\* malloc\\\(" "note" { target *-*-* } .-1 }
sink (p);
delete[] p;
// { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
@ -123,13 +123,13 @@ void warn_malloc_delete (int n)
/* Verify a warning for an invocation of either form of the delete
expression with a pointer returned from a call to realloc(). */
expression with a pointer returned from realloc(). */
void warn_realloc_delete (void *p1, void *p2, int n)
{
{
char *q = (char *)realloc (p1, n);
// { dg-message "returned from a call to 'void\\\* realloc\\\(" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'void\\\* realloc\\\(" "note" { target *-*-* } .-1 }
sink (q);
/* C++98 calls operator delete (void*) but later versions call
operator delete (void*, size_t). The difference doesn't matter
@ -140,7 +140,7 @@ void warn_realloc_delete (void *p1, void *p2, int n)
{
char *q = (char *)realloc (p2, n);
// { dg-message "returned from a call to 'void\\\* realloc\\\(" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'void\\\* realloc\\\(" "note" { target *-*-* } .-1 }
sink (q);
delete[] q;
// { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
@ -149,13 +149,13 @@ void warn_realloc_delete (void *p1, void *p2, int n)
/* Verify a warning for an invocation of either form of the delete
expression with a pointer returned from a call to strdup(). */
expression with a pointer returned from strdup(). */
void warn_strdup_delete (const char *s1, const char *s2)
{
{
char *q = strdup (s1);
// { dg-message "returned from a call to 'char\\\* strdup\\\(" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'char\\\* strdup\\\(" "note" { target *-*-* } .-1 }
sink (q);
/* C++98 calls operator delete (void*) but later versions call
operator delete (void*, size_t). The difference doesn't matter
@ -166,7 +166,7 @@ void warn_strdup_delete (const char *s1, const char *s2)
{
char *q = strdup (s2);
// { dg-message "returned from a call to 'char\\\* strdup\\\(" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'char\\\* strdup\\\(" "note" { target *-*-* } .-1 }
sink (q);
delete[] q;
// { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }
@ -176,13 +176,13 @@ void warn_strdup_delete (const char *s1, const char *s2)
/* Verify a warning for an invocation of either form of the delete
expression with a pointer returned from a call to strndup(). */
expression with a pointer returned from strndup(). */
void warn_strdup_delete (const char *s1, const char *s2, size_t n)
{
{
char *q = strndup (s1, n);
// { dg-message "returned from a call to 'char\\\* strndup\\\(" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'char\\\* strndup\\\(" "note" { target *-*-* } .-1 }
sink (q);
/* C++98 calls operator delete (void*) but later versions call
operator delete (void*, size_t). The difference doesn't matter
@ -193,7 +193,7 @@ void warn_strdup_delete (const char *s1, const char *s2, size_t n)
{
char *q = strndup (s2, n);
// { dg-message "returned from a call to 'char\\\* strndup\\\(" "note" { target *-*-* } .-1 }
// { dg-message "returned from 'char\\\* strndup\\\(" "note" { target *-*-* } .-1 }
sink (q);
delete[] q;
// { dg-warning "'void operator delete \\\[]\\\(void\\\*\\\)' called on pointer returned from a mismatched allocation function" "" { target *-*-* } .-1 }

View File

@ -0,0 +1,141 @@
/* PR middle-end/94527 - Add an attribute that marks a function as freeing
an object
Verify that attribute malloc with one or two arguments has the expected
effect on diagnostics.
{ dg-options "-Wall -ftrack-macro-expansion=0" } */
#define A(...) __attribute__ ((malloc (__VA_ARGS__), noipa))
typedef __SIZE_TYPE__ size_t;
typedef struct A A;
typedef struct B B;
/* A pointer returned by any of the four functions must be deallocated
either by dealloc() or by realloc_{A,B}(). */
A (__builtin_free) A* alloc_A (int);
A (__builtin_free) B* alloc_B (int);
A (__builtin_free) A* realloc_A (A *p, int n) { return p; }
A (__builtin_free) B* realloc_B (B *p, int n) { return p; }
A (realloc_A) A* alloc_A (int);
A (realloc_B) B* alloc_B (int);
A (realloc_A) A* realloc_A (A*, int);
A (realloc_B) B* realloc_B (B*, int);
void dealloc (void*);
A (dealloc) void* alloc (int);
void sink (void*);
void test_alloc_A (void)
{
{
void *p = alloc_A (1);
p = realloc_A (p, 2);
__builtin_free (p);
}
{
void *p = alloc_A (1);
/* Verify that calling realloc doesn't trigger a warning even though
alloc_A is not directly associated with it. */
p = __builtin_realloc (p, 2);
sink (p);
}
{
void *p = alloc_A (1); // { dg-message "returned from 'alloc_A'" }
dealloc (p); // { dg-warning "'dealloc' called on pointer returned from a mismatched allocation function" }
}
{
/* Because alloc_A() and realloc_B() share free() as a deallocator
they must also be valid as each other's deallocators. */
void *p = alloc_A (1);
p = realloc_B ((B*)p, 2);
__builtin_free (p);
}
{
void *p = alloc_A (1);
p = realloc_A (p, 2);
p = __builtin_realloc (p, 3);
__builtin_free (p);
}
}
void test_realloc_A (void *ptr)
{
{
void *p = realloc_A (0, 1);
p = realloc_A (p, 2);
__builtin_free (p);
}
{
void *p = realloc_A (ptr, 2);
p = realloc_A (p, 2);
__builtin_free (p);
}
{
void *p = realloc_A (0, 3);
p = __builtin_realloc (p, 2);
sink (p);
}
{
void *p = realloc_A (0, 4); // { dg-message "returned from 'realloc_A'" }
dealloc (p); // { dg-warning "'dealloc' called on pointer returned from a mismatched allocation function" }
}
{
/* Because realloc_A() and realloc_B() share free() as a deallocator
they must also be valid as each other's deallocators. */
void *p = realloc_A (0, 5);
p = realloc_B ((B*)p, 2);
__builtin_free (p);
}
{
void *p = realloc_A (0, 6);
p = realloc_A ((A*)p, 2);
p = __builtin_realloc (p, 3);
__builtin_free (p);
}
}
void test_realloc (void *ptr)
{
extern void free (void*);
extern void* realloc (void*, size_t);
{
void *p = realloc (ptr, 1);
p = realloc_A (p, 2);
__builtin_free (p);
}
{
void *p = realloc (ptr, 2);
p = realloc_A (p, 2);
free (p);
}
{
void *p = realloc (ptr, 3);
free (p);
}
{
void *p = realloc (ptr, 4);
__builtin_free (p);
}
{
void *p = realloc (ptr, 5); // { dg-message "returned from 'realloc'" }
dealloc (p); // { dg-warning "'dealloc' called on pointer returned from a mismatched allocation function" }
}
}

View File

@ -0,0 +1,265 @@
/* Verify that Glibc <stdlib.h> declarations are handled correctly
{ dg-do compile }
{ dg-options "-Wall" } */
#define A(...) __attribute__ ((malloc (__VA_ARGS__), noipa))
typedef __SIZE_TYPE__ size_t;
/* All functions with the same standard deallocator are associated
with each other. */
void free (void*);
void* calloc (size_t, size_t);
void* malloc (size_t);
void* realloc (void*, size_t);
A (__builtin_free) void* aligned_alloc (size_t, size_t);
/* Like realloc(), reallocarray() is both an allocator and a deallocator.
It must be associated with both free() and with itself, but nothing
else. */
A (__builtin_free) void* reallocarray (void*, size_t, size_t);
A (reallocarray) void* reallocarray (void*, size_t, size_t);
A (__builtin_free) extern char *canonicalize_file_name (const char*);
void dealloc (void*);
A (dealloc) void* alloc (size_t);
void sink (void*);
void* source (void);
void test_builtin_aligned_alloc (void *p)
{
{
void *q = __builtin_aligned_alloc (1, 2);
sink (q);
__builtin_free (q);
}
{
void *q = __builtin_aligned_alloc (1, 2);
sink (q);
free (q);
}
{
void *q = __builtin_aligned_alloc (1, 2);
q = __builtin_realloc (q, 3);
sink (q);
free (q);
}
{
void *q = __builtin_aligned_alloc (1, 2);
q = realloc (q, 3);
sink (q);
free (q);
}
{
void *q;
q = __builtin_aligned_alloc (1, 2); // { dg-message "returned from '__builtin_aligned_alloc'" }
sink (q);
dealloc (q); // { dg-warning "'dealloc' called on pointer returned from a mismatched allocation function" }
}
}
void test_aligned_alloc (void *p)
{
{
void *q = aligned_alloc (1, 2);
sink (q);
__builtin_free (q);
}
{
void *q = aligned_alloc (1, 2);
sink (q);
free (q);
}
{
void *q = aligned_alloc (1, 2);
q = __builtin_realloc (q, 3);
sink (q);
free (q);
}
{
void *q = aligned_alloc (1, 2);
q = realloc (q, 3);
sink (q);
free (q);
}
{
void *q = aligned_alloc (1, 2); // { dg-message "returned from 'aligned_alloc'" }
sink (q);
dealloc (q); // { dg-warning "'dealloc' called on pointer returned from a mismatched allocation function" }
}
}
void test_reallocarray (void *p)
{
{
void *q = __builtin_aligned_alloc (1, 2);
q = reallocarray (q, 2, 3);
sink (q);
free (q);
}
{
void *q = aligned_alloc (1, 2);
q = reallocarray (q, 2, 3);
sink (q);
free (q);
}
{
void *q = __builtin_calloc (1, 2);
q = reallocarray (q, 2, 3);
sink (q);
free (q);
}
{
void *q = calloc (1, 2);
q = reallocarray (q, 2, 3);
sink (q);
free (q);
}
{
void *q = __builtin_malloc (1);
q = reallocarray (q, 2, 3);
sink (q);
free (q);
}
{
void *q = malloc (1);
q = reallocarray (q, 2, 3);
sink (q);
free (q);
}
{
void *q = __builtin_realloc (p, 1);
q = reallocarray (q, 2, 3);
sink (q);
free (q);
}
{
void *q = realloc (p, 1);
q = reallocarray (q, 2, 3);
sink (q);
free (q);
}
{
void *q = __builtin_strdup ("abc");
q = reallocarray (q, 3, 4);
sink (q);
free (q);
}
{
void *q = __builtin_strndup ("abcd", 3);
q = reallocarray (q, 4, 5);
sink (q);
free (q);
}
{
void *q = source ();
q = reallocarray (q, 5, 6);
sink (q);
free (q);
}
{
void *q = alloc (1); // { dg-message "returned from 'alloc'" }
q = reallocarray (q, 6, 7); // { dg-warning "'reallocarray' called on pointer returned from a mismatched allocation function" }
sink (q);
free (q);
}
{
void *q = reallocarray (p, 7, 8);
q = __builtin_realloc (q, 9);
sink (q);
free (q);
}
{
void *q = reallocarray (p, 7, 8);
q = realloc (q, 9);
sink (q);
free (q);
}
{
void *q = reallocarray (p, 8, 9);
q = reallocarray (q, 3, 4);
sink (q);
free (q);
}
{
void *q = reallocarray (p, 9, 10);
q = reallocarray (q, 3, 4);
sink (q);
dealloc (q); // { dg-warning "'dealloc' called on pointer returned from a mismatched allocation function" }
}
}
void test_canonicalize_filename (void *p)
{
{
void *q = canonicalize_file_name ("a");
sink (q);
__builtin_free (q);
}
{
void *q = canonicalize_file_name ("b");
sink (q);
free (q);
}
{
void *q = canonicalize_file_name ("c");
q = __builtin_realloc (q, 2);
sink (q);
free (q);
}
{
void *q = canonicalize_file_name ("d");
q = realloc (q, 3);
sink (q);
free (q);
}
{
void *q = canonicalize_file_name ("e");
q = reallocarray (q, 4, 5);
sink (q);
free (q);
}
{
void *q;
q = canonicalize_file_name ("f"); // { dg-message "returned from 'canonicalize_file_name'" }
sink (q);
dealloc (q); // { dg-warning "'dealloc' called on pointer returned from a mismatched allocation function" }
}
}

View File

@ -13,29 +13,28 @@ void free (void*);
void* malloc (size_t);
void* realloc (void*, size_t);
int fclose (FILE*);
FILE* freopen (const char*, const char*, FILE*);
int pclose (FILE*);
/* Declare functions with the minimum attributes malloc how they're
likely going to be declared in <stdio.h>. */
int fclose (FILE*);
A (fclose) FILE* fdopen (int);
A (fclose) FILE* fopen (const char*, const char*);
A (fclose) FILE* fmemopen(void *, size_t, const char *);
A (fclose) FILE* freopen (const char*, const char*, FILE*);
A (freopen, 3) FILE* freopen (const char*, const char*, FILE*);
A (fclose) FILE* tmpfile (void);
A (fclose) A (freopen, 3)
FILE* fdopen (int);
A (fclose) A (freopen, 3)
FILE* fopen (const char*, const char*);
A (fclose) A (freopen, 3)
FILE* fmemopen(void *, size_t, const char *);
A (fclose) A (freopen, 3)
FILE* freopen (const char*, const char*, FILE*);
A (pclose) A (freopen, 3)
FILE* popen (const char*, const char*);
A (fclose) A (freopen, 3)
FILE* tmpfile (void);
A (fclose) FILE* open_memstream (char**, size_t*);
A (fclose) FILE* open_wmemstream (char**, size_t*);
int pclose (FILE*);
A (pclose) FILE* popen (const char*, const char*);
void release (void*);
A (release) FILE* acquire (void);
void sink (FILE*);
void release (void*);
A (release) FILE* acquire (void);
void nowarn_fdopen (void)
{
{
@ -68,18 +67,18 @@ void nowarn_fdopen (void)
void warn_fdopen (void)
{
{
FILE *q = fdopen (0); // { dg-message "returned from a call to 'fdopen'" "note" }
FILE *q = fdopen (0); // { dg-message "returned from 'fdopen'" "note" }
sink (q);
release (q); // { dg-warning "'release' called on pointer returned from a mismatched allocation function" }
}
{
FILE *q = fdopen (0); // { dg-message "returned from a call to 'fdopen'" "note" }
FILE *q = fdopen (0); // { dg-message "returned from 'fdopen'" "note" }
sink (q);
free (q); // { dg-warning "'free' called on pointer returned from a mismatched allocation function" }
}
{
FILE *q = fdopen (0); // { dg-message "returned from a call to 'fdopen'" "note" }
FILE *q = fdopen (0); // { dg-message "returned from 'fdopen'" "note" }
sink (q);
q = realloc (q, 7); // { dg-warning "'realloc' called on pointer returned from a mismatched allocation function" }
sink (q);
@ -132,29 +131,27 @@ void warn_fopen (void)
}
void test_popen (void)
void test_freopen (FILE *p[])
{
{
FILE *p = popen ("1", "r");
sink (p);
pclose (p);
FILE *q = freopen ("1", "r", p[0]);
sink (q);
fclose (q);
}
{
FILE *q = freopen ("2", "r", p[1]);
sink (q);
q = freopen ("3", "r", q);
sink (q);
fclose (q);
}
{
FILE *p;
p = popen ("2", "r"); // { dg-message "returned from a call to 'popen'" "note" }
sink (p);
fclose (p); // { dg-warning "'fclose' called on pointer returned from a mismatched allocation function" }
}
{
/* freopen() can close a stream open by popen() but pclose() can't
close the stream returned from freopen(). */
FILE *p = popen ("2", "r");
sink (p);
p = freopen ("3", "r", p); // { dg-message "returned from a call to 'freopen'" "note" }
sink (p);
pclose (p); // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" }
FILE *q;
q = freopen ("3", "r", p[2]); // { dg-message "returned from 'freopen'" }
sink (q);
q = realloc (q, 7); // { dg-warning "'realloc' called on pointer returned from a mismatched allocation function" }
sink (q);
}
}
@ -176,29 +173,107 @@ void test_tmpfile (void)
}
{
FILE *p = tmpfile (); // { dg-message "returned from a call to 'tmpfile'" "note" }
FILE *p = tmpfile (); // { dg-message "returned from 'tmpfile'" "note" }
sink (p);
pclose (p); // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" }
}
}
void test_open_memstream (char **bufp, size_t *sizep)
{
{
FILE *p = open_memstream (bufp, sizep);
sink (p);
fclose (p);
}
{
FILE *p = open_memstream (bufp, sizep);
sink (p);
p = freopen ("1", "r", p);
sink (p);
fclose (p);
}
{
FILE *p;
p = open_memstream (bufp, sizep); // { dg-message "returned from 'open_memstream'" "note" }
sink (p);
pclose (p); // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" }
}
{
FILE *p;
p = open_memstream (bufp, sizep); // { dg-message "returned from 'open_memstream'" "note" }
sink (p);
free (p); // { dg-warning "'free' called on pointer returned from a mismatched allocation function" }
}
{
FILE *p;
p = open_memstream (bufp, sizep); // { dg-message "returned from 'open_memstream'" "note" }
sink (p);
release (p); // { dg-warning "'release' called on pointer returned from a mismatched allocation function" }
}
}
void test_open_wmemstream (char **bufp, size_t *sizep)
{
{
FILE *p = open_wmemstream (bufp, sizep);
sink (p);
fclose (p);
}
{
FILE *p = open_wmemstream (bufp, sizep);
sink (p);
p = freopen ("1", "r", p);
sink (p);
fclose (p);
}
{
FILE *p;
p = open_wmemstream (bufp, sizep); // { dg-message "returned from 'open_wmemstream'" "note" }
sink (p);
pclose (p); // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" }
}
{
FILE *p;
p = open_wmemstream (bufp, sizep); // { dg-message "returned from 'open_wmemstream'" "note" }
sink (p);
free (p); // { dg-warning "'free' called on pointer returned from a mismatched allocation function" }
}
{
FILE *p;
p = open_wmemstream (bufp, sizep); // { dg-message "returned from 'open_wmemstream'" "note" }
sink (p);
release (p); // { dg-warning "'release' called on pointer returned from a mismatched allocation function" }
}
}
void warn_malloc (void)
{
{
FILE *p = malloc (100); // { dg-message "returned from a call to 'malloc'" "note" }
FILE *p = malloc (100); // { dg-message "returned from 'malloc'" "note" }
sink (p);
fclose (p); // { dg-warning "'fclose' called on pointer returned from a mismatched allocation function" }
}
{
FILE *p = malloc (100); // { dg-message "returned from a call to 'malloc'" "note" }
FILE *p = malloc (100); // { dg-message "returned from 'malloc'" "note" }
sink (p);
p = freopen ("1", "r", p);// { dg-warning "'freopen' called on pointer returned from a mismatched allocation function" }
}
{
FILE *p = malloc (100); // { dg-message "returned from a call to 'malloc'" "note" }
FILE *p = malloc (100); // { dg-message "returned from 'malloc'" "note" }
sink (p);
pclose (p); // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" }
}
@ -219,32 +294,32 @@ void test_acquire (void)
}
{
FILE *p = acquire (); // { dg-message "returned from a call to 'acquire'" "note" }
FILE *p = acquire (); // { dg-message "returned from 'acquire'" "note" }
sink (p);
fclose (p); // { dg-warning "'fclose' called on pointer returned from a mismatched allocation function" }
}
{
FILE *p = acquire (); // { dg-message "returned from a call to 'acquire'" "note" }
FILE *p = acquire (); // { dg-message "returned from 'acquire'" "note" }
sink (p);
pclose (p); // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" }
}
{
FILE *p = acquire (); // { dg-message "returned from a call to 'acquire'" "note" }
FILE *p = acquire (); // { dg-message "returned from 'acquire'" "note" }
sink (p);
p = freopen ("1", "r", p); // { dg-warning "'freopen' called on pointer returned from a mismatched allocation function" }
sink (p);
}
{
FILE *p = acquire (); // { dg-message "returned from a call to 'acquire'" "note" }
FILE *p = acquire (); // { dg-message "returned from 'acquire'" "note" }
sink (p);
free (p); // { dg-warning "'free' called on pointer returned from a mismatched allocation function" }
}
{
FILE *p = acquire (); // { dg-message "returned from a call to 'acquire'" "note" }
FILE *p = acquire (); // { dg-message "returned from 'acquire'" "note" }
sink (p);
p = realloc (p, 123); // { dg-warning "'realloc' called on pointer returned from a mismatched allocation function" }
sink (p);

View File

@ -656,67 +656,7 @@ valid_new_delete_pair_p (gimple *new_call, gimple *delete_call)
{
tree new_asm = DECL_ASSEMBLER_NAME (gimple_call_fndecl (new_call));
tree delete_asm = DECL_ASSEMBLER_NAME (gimple_call_fndecl (delete_call));
const char *new_name = IDENTIFIER_POINTER (new_asm);
const char *delete_name = IDENTIFIER_POINTER (delete_asm);
unsigned int new_len = IDENTIFIER_LENGTH (new_asm);
unsigned int delete_len = IDENTIFIER_LENGTH (delete_asm);
if (new_len < 5 || delete_len < 6)
return false;
if (new_name[0] == '_')
++new_name, --new_len;
if (new_name[0] == '_')
++new_name, --new_len;
if (delete_name[0] == '_')
++delete_name, --delete_len;
if (delete_name[0] == '_')
++delete_name, --delete_len;
if (new_len < 4 || delete_len < 5)
return false;
/* *_len is now just the length after initial underscores. */
if (new_name[0] != 'Z' || new_name[1] != 'n')
return false;
if (delete_name[0] != 'Z' || delete_name[1] != 'd')
return false;
/* _Znw must match _Zdl, _Zna must match _Zda. */
if ((new_name[2] != 'w' || delete_name[2] != 'l')
&& (new_name[2] != 'a' || delete_name[2] != 'a'))
return false;
/* 'j', 'm' and 'y' correspond to size_t. */
if (new_name[3] != 'j' && new_name[3] != 'm' && new_name[3] != 'y')
return false;
if (delete_name[3] != 'P' || delete_name[4] != 'v')
return false;
if (new_len == 4
|| (new_len == 18 && !memcmp (new_name + 4, "RKSt9nothrow_t", 14)))
{
/* _ZnXY or _ZnXYRKSt9nothrow_t matches
_ZdXPv, _ZdXPvY and _ZdXPvRKSt9nothrow_t. */
if (delete_len == 5)
return true;
if (delete_len == 6 && delete_name[5] == new_name[3])
return true;
if (delete_len == 19 && !memcmp (delete_name + 5, "RKSt9nothrow_t", 14))
return true;
}
else if ((new_len == 19 && !memcmp (new_name + 4, "St11align_val_t", 15))
|| (new_len == 33
&& !memcmp (new_name + 4, "St11align_val_tRKSt9nothrow_t", 29)))
{
/* _ZnXYSt11align_val_t or _ZnXYSt11align_val_tRKSt9nothrow_t matches
_ZdXPvSt11align_val_t or _ZdXPvYSt11align_val_t or or
_ZdXPvSt11align_val_tRKSt9nothrow_t. */
if (delete_len == 20 && !memcmp (delete_name + 5, "St11align_val_t", 15))
return true;
if (delete_len == 21
&& delete_name[5] == new_name[3]
&& !memcmp (delete_name + 6, "St11align_val_t", 15))
return true;
if (delete_len == 34
&& !memcmp (delete_name + 5, "St11align_val_tRKSt9nothrow_t", 29))
return true;
}
return false;
return valid_new_delete_pair_p (new_asm, delete_asm);
}
/* Propagate necessity using the operands of necessary statements.

View File

@ -12610,8 +12610,40 @@ tree_nonartificial_location (tree exp)
return EXPR_LOCATION (exp);
}
/* Return the location into which EXP has been inlined. Analogous
to tree_nonartificial_location() above but not limited to artificial
functions declared inline. If SYSTEM_HEADER is true, return
the macro expansion point of the location if it's in a system header */
/* These are the hash table functions for the hash table of OPTIMIZATION_NODEq
location_t
tree_inlined_location (tree exp, bool system_header /* = true */)
{
location_t loc = UNKNOWN_LOCATION;
tree block = TREE_BLOCK (exp);
while (block && TREE_CODE (block) == BLOCK
&& BLOCK_ABSTRACT_ORIGIN (block))
{
tree ao = BLOCK_ABSTRACT_ORIGIN (block);
if (TREE_CODE (ao) == FUNCTION_DECL)
loc = BLOCK_SOURCE_LOCATION (block);
else if (TREE_CODE (ao) != BLOCK)
break;
block = BLOCK_SUPERCONTEXT (block);
}
if (loc == UNKNOWN_LOCATION)
loc = EXPR_LOCATION (exp);
if (system_header)
return expansion_point_location_if_in_system_header (loc);
return loc;
}
/* These are the hash table functions for the hash table of OPTIMIZATION_NODE
nodes. */
/* Return the hash code X, an OPTIMIZATION_NODE or TARGET_OPTION code. */
@ -15386,6 +15418,75 @@ verify_type_context (location_t loc, type_context_kind context,
|| targetm.verify_type_context (loc, context, type, silent_p));
}
/* Return that NEW_ASM and DELETE_ASM name a valid pair of new and
delete operators. */
bool
valid_new_delete_pair_p (tree new_asm, tree delete_asm)
{
const char *new_name = IDENTIFIER_POINTER (new_asm);
const char *delete_name = IDENTIFIER_POINTER (delete_asm);
unsigned int new_len = IDENTIFIER_LENGTH (new_asm);
unsigned int delete_len = IDENTIFIER_LENGTH (delete_asm);
if (new_len < 5 || delete_len < 6)
return false;
if (new_name[0] == '_')
++new_name, --new_len;
if (new_name[0] == '_')
++new_name, --new_len;
if (delete_name[0] == '_')
++delete_name, --delete_len;
if (delete_name[0] == '_')
++delete_name, --delete_len;
if (new_len < 4 || delete_len < 5)
return false;
/* *_len is now just the length after initial underscores. */
if (new_name[0] != 'Z' || new_name[1] != 'n')
return false;
if (delete_name[0] != 'Z' || delete_name[1] != 'd')
return false;
/* _Znw must match _Zdl, _Zna must match _Zda. */
if ((new_name[2] != 'w' || delete_name[2] != 'l')
&& (new_name[2] != 'a' || delete_name[2] != 'a'))
return false;
/* 'j', 'm' and 'y' correspond to size_t. */
if (new_name[3] != 'j' && new_name[3] != 'm' && new_name[3] != 'y')
return false;
if (delete_name[3] != 'P' || delete_name[4] != 'v')
return false;
if (new_len == 4
|| (new_len == 18 && !memcmp (new_name + 4, "RKSt9nothrow_t", 14)))
{
/* _ZnXY or _ZnXYRKSt9nothrow_t matches
_ZdXPv, _ZdXPvY and _ZdXPvRKSt9nothrow_t. */
if (delete_len == 5)
return true;
if (delete_len == 6 && delete_name[5] == new_name[3])
return true;
if (delete_len == 19 && !memcmp (delete_name + 5, "RKSt9nothrow_t", 14))
return true;
}
else if ((new_len == 19 && !memcmp (new_name + 4, "St11align_val_t", 15))
|| (new_len == 33
&& !memcmp (new_name + 4, "St11align_val_tRKSt9nothrow_t", 29)))
{
/* _ZnXYSt11align_val_t or _ZnXYSt11align_val_tRKSt9nothrow_t matches
_ZdXPvSt11align_val_t or _ZdXPvYSt11align_val_t or or
_ZdXPvSt11align_val_tRKSt9nothrow_t. */
if (delete_len == 20 && !memcmp (delete_name + 5, "St11align_val_t", 15))
return true;
if (delete_len == 21
&& delete_name[5] == new_name[3]
&& !memcmp (delete_name + 6, "St11align_val_t", 15))
return true;
if (delete_len == 34
&& !memcmp (delete_name + 5, "St11align_val_tRKSt9nothrow_t", 29))
return true;
}
return false;
}
#if CHECKING_P
namespace selftest {

View File

@ -5278,6 +5278,7 @@ extern tree tree_block (tree);
extern void tree_set_block (tree, tree);
extern location_t *block_nonartificial_location (tree);
extern location_t tree_nonartificial_location (tree);
extern location_t tree_inlined_location (tree, bool = true);
extern tree block_ultimate_origin (const_tree);
extern tree get_binfo_at_offset (tree, poly_int64, tree);
extern bool virtual_method_call_p (const_tree, bool = false);
@ -5355,6 +5356,7 @@ extern bool gimple_canonical_types_compatible_p (const_tree, const_tree,
extern bool type_with_interoperable_signedness (const_tree);
extern bitmap get_nonnull_args (const_tree);
extern int get_range_pos_neg (tree);
extern bool valid_new_delete_pair_p (tree, tree);
/* Return simplified tree code of type that is used for canonical type
merging. */

View File

@ -48,3 +48,7 @@ int main()
value();
return 0;
}
// The __versa_string destructor triggers a bogus -Wfree-nonheap-object
// due to pr54202.
// { dg-prune-output "\\\[-Wfree-nonheap-object" }

View File

@ -48,3 +48,7 @@ int main()
value();
return 0;
}
// The __versa_string destructor triggers a bogus -Wfree-nonheap-object
// due to pr54202.
// { dg-prune-output "\\\[-Wfree-nonheap-object" }