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 (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.
gcc/c-family/ChangeLog:
PR middle-end/98166
PR c++/57111
PR middle-end/98160
* c-attribs.c (handle_malloc_attribute): 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-new-delete-2.C: New test.
@@ -13065,12 +13051,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
@@ -13083,15 +13176,9 @@ matching_alloc_calls_p (tree alloc_decl, tree dealloc_decl)
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. */
@@ -13128,13 +13215,35 @@ 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;
+ if (fndecl_built_in_p (dealloc_decl, BUILT_IN_NORMAL))
+ {
+ tree alloc_attrs = DECL_ATTRIBUTES (alloc_decl);
+ built_in_function dealloc_code = DECL_FUNCTION_CODE (dealloc_decl);
+
+ for (tree alloc_funs = alloc_attrs;
+ (alloc_funs = lookup_attribute ("malloc", alloc_funs));
+ alloc_funs = TREE_CHAIN (alloc_funs))
+ {
+ tree args = TREE_VALUE (alloc_funs);
+ if (!args)
+ continue;
+
+ tree fndecl = TREE_VALUE (args);
+ if (fndecl
+ && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)
+ && dealloc_code == DECL_FUNCTION_CODE (fndecl))
+ return true;
+ }
+ return false;
+ }
+ /* If DEALLOC_DECL has an internal "*dealloc" attribute scan the list
+ of its associated allocation functions for ALLOC_DECL. In addition,
+ for built-ins, search the list of attributes of both the form
+ referenced by the attribute and the "internal" __builtin_xxx form/
+ If the corresponding ALLOC_DECL is found they're a matching pair,
+ otherwise they're not. */
+ tree attrs = DECL_ATTRIBUTES (dealloc_decl);
for (tree funs = attrs;
(funs = lookup_attribute ("*dealloc", funs));
funs = TREE_CHAIN (funs))
@@ -13167,15 +13276,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 +13322,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 +13332,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 +13377,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 +13387,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 +13431,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 +13454,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 +13473,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;
}
}
@@ -3174,11 +3174,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)
{
@@ -3234,9 +3244,50 @@ handle_malloc_attribute (tree *node, tree name, tree args,
}
*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);
+
+ tree at_noinline = NULL_TREE;
+ if (!fndecl_built_in_p (fndecl) && !fndecl_built_in_p (dealloc))
+ {
+ /* 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 (fndecl))
+ || lookup_attribute ("always_inline", DECL_ATTRIBUTES (fndecl)))
+ {
+ warning (OPT_Wattributes,
+ "%<%E (%E)%> attribute ignored on functions "
+ "declared %qs", name, DECL_NAME (dealloc), "inline");
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+
+ if ((optimize && DECL_DECLARED_INLINE_P (dealloc))
+ || lookup_attribute ("always_inline", DECL_ATTRIBUTES (dealloc)))
+ {
+ warning (OPT_Wattributes,
+ "%<%E (%E)%> attribute ignored with deallocation "
+ "functions declared %qs",
+ name, DECL_NAME (dealloc), "inline");
+ inform (DECL_SOURCE_LOCATION (dealloc),
+ "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. */
+ at_noinline
+ = tree_cons (get_identifier ("noinline"), NULL_TREE, NULL_TREE);
+ decl_attributes (&fndecl, at_noinline, 0);
+ }
+
+ 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;
}
new file mode 100644
@@ -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);
+ }
+}
new file mode 100644
@@ -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);
+ }
+}
new file mode 100644
@@ -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" }
+}
new file mode 100644
@@ -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)))
+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 (long *p) /* { free (p - 2); } */;
+
+__attribute__ ((malloc (dealloc_long)))
+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 (p); // { dg-bogus "\\\[-Wmismatched-dealloc" }
+}
+
+
+inline void
+dealloc_float (float *p) // { dg-message "deallocation function declared here" }
+{
+ __builtin_free (p - 3);
+}
+
+__attribute__ ((malloc (dealloc_float)))
+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 (p); // { dg-bogus "\\\[-Wmismatched-dealloc" }
+}
new file mode 100644
@@ -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" } */
new file mode 100644
@@ -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 }
@@ -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 }
@@ -68,18 +68,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);
@@ -142,7 +142,7 @@ void test_popen (void)
{
FILE *p;
- p = popen ("2", "r"); // { dg-message "returned from a call to 'popen'" "note" }
+ p = popen ("2", "r"); // { dg-message "returned from 'popen'" "note" }
sink (p);
fclose (p); // { dg-warning "'fclose' called on pointer returned from a mismatched allocation function" }
}
@@ -152,7 +152,7 @@ void test_popen (void)
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" }
+ p = freopen ("3", "r", p); // { dg-message "returned from 'freopen'" "note" }
sink (p);
pclose (p); // { dg-warning "'pclose' called on pointer returned from a mismatched allocation function" }
}
@@ -176,7 +176,7 @@ 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" }
}
@@ -186,19 +186,19 @@ void test_tmpfile (void)
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 +219,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);
@@ -3262,7 +3262,11 @@ 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.
+be used. Since inlining one of a the associated functions but not
+the other could result in apparent mismatches, this form of attribute
+@code{malloc} is not accepted on inline functions declared. 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}
new file mode 100644
@@ -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
@@ -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" }