mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-13 22:11:47 +08:00
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:
parent
e63ae8c083
commit
fe7f75cf16
388
gcc/builtins.c
388
gcc/builtins.c
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
52
gcc/testsuite/c-c++-common/Wfree-nonheap-object-2.c
Normal file
52
gcc/testsuite/c-c++-common/Wfree-nonheap-object-2.c
Normal 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);
|
||||
}
|
||||
}
|
70
gcc/testsuite/c-c++-common/Wfree-nonheap-object-3.c
Normal file
70
gcc/testsuite/c-c++-common/Wfree-nonheap-object-3.c
Normal 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);
|
||||
}
|
||||
}
|
50
gcc/testsuite/c-c++-common/Wfree-nonheap-object.c
Normal file
50
gcc/testsuite/c-c++-common/Wfree-nonheap-object.c
Normal 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" }
|
||||
}
|
67
gcc/testsuite/c-c++-common/Wmismatched-dealloc.c
Normal file
67
gcc/testsuite/c-c++-common/Wmismatched-dealloc.c
Normal 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" }
|
||||
}
|
38
gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-3.C
Normal file
38
gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-3.C
Normal 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" } */
|
26
gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-4.C
Normal file
26
gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-4.C
Normal 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" }
|
||||
}
|
@ -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 }
|
||||
|
249
gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-2.C
Normal file
249
gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-2.C
Normal 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
|
@ -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 }
|
||||
|
141
gcc/testsuite/gcc.dg/Wmismatched-dealloc-2.c
Normal file
141
gcc/testsuite/gcc.dg/Wmismatched-dealloc-2.c
Normal 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" }
|
||||
}
|
||||
}
|
265
gcc/testsuite/gcc.dg/Wmismatched-dealloc-3.c
Normal file
265
gcc/testsuite/gcc.dg/Wmismatched-dealloc-3.c
Normal 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" }
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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.
|
||||
|
103
gcc/tree.c
103
gcc/tree.c
@ -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 {
|
||||
|
@ -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. */
|
||||
|
@ -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" }
|
||||
|
@ -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" }
|
||||
|
Loading…
x
Reference in New Issue
Block a user