/* 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* source (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) { extern void free (void*); extern void* realloc (void*, size_t); { void *p = realloc (source (), 1); p = realloc_A (p, 2); __builtin_free (p); } { void *p = realloc (source (), 2); p = realloc_A (p, 2); free (p); } { void *p = realloc (source (), 3); free (p); } { void *p = realloc (source (), 4); __builtin_free (p); } { void *p = realloc (source (), 5); // { dg-message "returned from 'realloc'" } dealloc (p); // { dg-warning "'dealloc' called on pointer returned from a mismatched allocation function" } } }