&& 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;
|| (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)
{
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]);
|| (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;
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);
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);
|| !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);
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:
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
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. */
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:
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:
}
}
- /* 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;
+
+ if (fndecl_built_in_p (dealloc_decl, BUILT_IN_NORMAL))
+ {
+ 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;
- for (tree funs = attrs;
- (funs = lookup_attribute ("*dealloc", funs));
- funs = TREE_CHAIN (funs))
+ /* 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 (funs);
+ 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
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]))
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))
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");
}
}
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))
{
"%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))
{
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
"%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;
}
}
&& !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;
}
}
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)
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)
{
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;
}
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;
}
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));
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
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
--- /dev/null
+/* 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);
+ }
+}
--- /dev/null
+/* 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);
+ }
+}
--- /dev/null
+/* 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" }
+}
--- /dev/null
+/* 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" }
+}
--- /dev/null
+/* 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" } */
--- /dev/null
+/* 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" }
+}
{
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 }
{
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 }
{
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 }
{
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 }
{
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 }
}
{
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 }
{
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 }
{
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 }
--- /dev/null
+/* 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
{
{
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 }
{
{
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 }
}
{
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 }
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 }
/* 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
{
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 }
/* 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
{
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 }
/* 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
{
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 }
/* 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
{
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 }
--- /dev/null
+/* 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" }
+ }
+}
--- /dev/null
+/* 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" }
+ }
+}
void* malloc (size_t);
void* realloc (void*, size_t);
-int fclose (FILE*);
-FILE* freopen (const char*, const char*, FILE*);
-int pclose (FILE*);
-
-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);
+/* 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);
-void sink (FILE*);
+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)
{
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);
}
-void test_popen (void)
+void test_freopen (FILE *p[])
{
{
- FILE *p = popen ("1", "r");
+ 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 *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);
+ }
+}
+
+
+void test_tmpfile (void)
+{
+ {
+ FILE *p = tmpfile ();
sink (p);
- pclose (p);
+ fclose (p);
}
{
- FILE *p;
- p = popen ("2", "r"); // { dg-message "returned from a call to 'popen'" "note" }
+ FILE *p = tmpfile ();
sink (p);
- fclose (p); // { dg-warning "'fclose' called on pointer returned from a mismatched allocation function" }
+ p = freopen ("1", "r", p);
+ sink (p);
+ fclose (p);
}
{
- /* freopen() can close a stream open by popen() but pclose() can't
- close the stream returned from freopen(). */
- FILE *p = popen ("2", "r");
+ FILE *p = tmpfile (); // { dg-message "returned from 'tmpfile'" "note" }
sink (p);
- p = freopen ("3", "r", p); // { dg-message "returned from a call to 'freopen'" "note" }
+ 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_tmpfile (void)
+void test_open_wmemstream (char **bufp, size_t *sizep)
{
{
- FILE *p = tmpfile ();
+ FILE *p = open_wmemstream (bufp, sizep);
sink (p);
fclose (p);
}
{
- FILE *p = tmpfile ();
+ FILE *p = open_wmemstream (bufp, sizep);
sink (p);
p = freopen ("1", "r", p);
sink (p);
}
{
- FILE *p = tmpfile (); // { dg-message "returned from a call to 'tmpfile'" "note" }
+ 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" }
}
}
{
- 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);
{
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.
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. */
|| 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 {
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);
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. */
value();
return 0;
}
+
+// The __versa_string destructor triggers a bogus -Wfree-nonheap-object
+// due to pr54202.
+// { dg-prune-output "\\\[-Wfree-nonheap-object" }
value();
return 0;
}
+
+// The __versa_string destructor triggers a bogus -Wfree-nonheap-object
+// due to pr54202.
+// { dg-prune-output "\\\[-Wfree-nonheap-object" }