diff mbox series

teach compute_objsize about placement new (PR 100876)

Message ID 6ccb6e6b-61b4-822b-d291-d2712192758e@gmail.com
State New
Headers show
Series teach compute_objsize about placement new (PR 100876) | expand

Commit Message

Martin Sebor June 2, 2021, 9:40 p.m. UTC
The two forms of placement operator new defined in <new> return their
pointer argument and may not be displaced by user-defined functions.
But because they are ordinary (not built-in) functions this property
isn't reflected in their declarations alone, and there's no user-
level attribute to annotate them with.  When they are inlined
the property is transparent in the IL but when they are not (without
inlining such as -O0), calls to the operators appear in the IL and
cause -Wmismatched-new-delete to try to match them with the functions
called to deallocate memory.  When the pointer to the memory was
obtained from a function that matches the deallocator but not
the placement new, the warning falsely triggers.

The attached patch solves this by detecting calls to placement new
and treating them the same as those to other pass-through calls (such
as memset).  In addition, it also teaches -Wfree-nonheap-object about
placement delete, for a similar reason as above.  Finally, it also
adds a test for attribute fn spec indicating a function returns its
argument.  It's not necessary for the fix (I had initially though
placement new might have the attribute) but it seems appropriate
to check.

Tested on x86_64-linux.

Martin

Comments

Marek Polacek June 2, 2021, 9:46 p.m. UTC | #1
On Wed, Jun 02, 2021 at 03:40:49PM -0600, Martin Sebor via Gcc-patches wrote:
> +  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
> +    {
> +      /* See if this is a call to placement new.  */
> +      if (!fn
> +	  || !DECL_IS_OPERATOR_NEW_P (fn)
> +	  || DECL_IS_REPLACEABLE_OPERATOR_NEW_P (fn))
> +	return NULL_TREE;
> +
> +      tree fname = DECL_ASSEMBLER_NAME (fn);
> +      const char *name = IDENTIFIER_POINTER (fname);
> +      if (strcmp (name, "_ZnwmPv")       // ordinary form
> +	  && strcmp (name, "_ZnamPv"))   // array form
> +	return NULL_TREE;

Not a review, but you can use id_equal here and simplify things.

Marek
Martin Sebor June 9, 2021, 3:47 p.m. UTC | #2
Ping: https://gcc.gnu.org/pipermail/gcc-patches/2021-June/571777.html

On 6/2/21 3:40 PM, Martin Sebor wrote:
> The two forms of placement operator new defined in <new> return their
> pointer argument and may not be displaced by user-defined functions.
> But because they are ordinary (not built-in) functions this property
> isn't reflected in their declarations alone, and there's no user-
> level attribute to annotate them with.  When they are inlined
> the property is transparent in the IL but when they are not (without
> inlining such as -O0), calls to the operators appear in the IL and
> cause -Wmismatched-new-delete to try to match them with the functions
> called to deallocate memory.  When the pointer to the memory was
> obtained from a function that matches the deallocator but not
> the placement new, the warning falsely triggers.
> 
> The attached patch solves this by detecting calls to placement new
> and treating them the same as those to other pass-through calls (such
> as memset).  In addition, it also teaches -Wfree-nonheap-object about
> placement delete, for a similar reason as above.  Finally, it also
> adds a test for attribute fn spec indicating a function returns its
> argument.  It's not necessary for the fix (I had initially though
> placement new might have the attribute) but it seems appropriate
> to check.
> 
> Tested on x86_64-linux.
> 
> Martin
Jeff Law June 13, 2021, 11:45 p.m. UTC | #3
On 6/2/2021 3:40 PM, Martin Sebor via Gcc-patches wrote:
> The two forms of placement operator new defined in <new> return their
> pointer argument and may not be displaced by user-defined functions.
> But because they are ordinary (not built-in) functions this property
> isn't reflected in their declarations alone, and there's no user-
> level attribute to annotate them with.  When they are inlined
> the property is transparent in the IL but when they are not (without
> inlining such as -O0), calls to the operators appear in the IL and
> cause -Wmismatched-new-delete to try to match them with the functions
> called to deallocate memory.  When the pointer to the memory was
> obtained from a function that matches the deallocator but not
> the placement new, the warning falsely triggers.
>
> The attached patch solves this by detecting calls to placement new
> and treating them the same as those to other pass-through calls (such
> as memset).  In addition, it also teaches -Wfree-nonheap-object about
> placement delete, for a similar reason as above.  Finally, it also
> adds a test for attribute fn spec indicating a function returns its
> argument.  It's not necessary for the fix (I had initially though
> placement new might have the attribute) but it seems appropriate
> to check.
>
> Tested on x86_64-linux.
>
> Martin
>
> gcc-100876.diff
>
> PR c++/100876 - -Wmismatched-new-delete should understand placement new when it's not inlined
>
> gcc/ChangeLog:
>
> 	PR c++/100876
> 	* builtins.c (gimple_call_return_array): Check for attribute fn spec.
> 	Handle calls to placement new.
> 	(ndecl_dealloc_argno): Avoid placement delete.
>
> gcc/testsuite/ChangeLog:
>
> 	PR c++/100876
> 	* g++.dg/warn/Wmismatched-new-delete-4.C: New test.
> 	* g++.dg/warn/Wmismatched-new-delete-5.C: New test.
> 	* g++.dg/warn/Wstringop-overflow-7.C: New test.
> 	* g++.dg/warn/Wfree-nonheap-object-6.C: New test.
> 	* g++.dg/analyzer/placement-new.C: Prune out expected warning.
>
> diff --git a/gcc/builtins.c b/gcc/builtins.c
> index af1fe49bb48..fb0717a0248 100644
> --- a/gcc/builtins.c
> +++ b/gcc/builtins.c
> @@ -5159,11 +5159,43 @@ static tree
>   gimple_call_return_array (gimple *stmt, offset_int offrng[2],
>   			  range_query *rvals)
>   {
> -  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
> -      || gimple_call_num_args (stmt) < 1)
> +  {
> +    /* Check for attribute fn spec to see if the function returns one
> +       of its arguments.  */
> +    attr_fnspec fnspec = gimple_call_fnspec (as_a <gcall *>(stmt));
> +    unsigned int argno;
> +    if (fnspec.returns_arg (&argno))
> +      {
> +	offrng[0] = offrng[1] = 0;
> +	return gimple_call_arg (stmt, argno);
> +      }
> +  }
> +
> +  if (gimple_call_num_args (stmt) < 1)
>       return NULL_TREE;
Nit.  You've got an unnecessary {} at the outer level of this hunk.

OK with the nit fixed.

THanks,
Jeff
Bernhard Reutner-Fischer June 14, 2021, 5:56 a.m. UTC | #4
On 14 June 2021 01:45:36 CEST, Jeff Law via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
>
>
>On 6/2/2021 3:40 PM, Martin Sebor via Gcc-patches wrote:
>> The two forms of placement operator new defined in <new> return their
>> pointer argument and may not be displaced by user-defined functions.
>> But because they are ordinary (not built-in) functions this property
>> isn't reflected in their declarations alone, and there's no user-
>> level attribute to annotate them with.  When they are inlined
>> the property is transparent in the IL but when they are not (without
>> inlining such as -O0), calls to the operators appear in the IL and
>> cause -Wmismatched-new-delete to try to match them with the functions
>> called to deallocate memory.  When the pointer to the memory was
>> obtained from a function that matches the deallocator but not
>> the placement new, the warning falsely triggers.
>>
>> The attached patch solves this by detecting calls to placement new
>> and treating them the same as those to other pass-through calls (such
>> as memset).  In addition, it also teaches -Wfree-nonheap-object about
>> placement delete, for a similar reason as above.  Finally, it also
>> adds a test for attribute fn spec indicating a function returns its
>> argument.  It's not necessary for the fix (I had initially though
>> placement new might have the attribute) but it seems appropriate
>> to check.
>>
>> Tested on x86_64-linux.
>>
>> Martin
>>
>> gcc-100876.diff
>>
>> PR c++/100876 - -Wmismatched-new-delete should understand placement
>new when it's not inlined
>>
>> gcc/ChangeLog:
>>
>> 	PR c++/100876
>> 	* builtins.c (gimple_call_return_array): Check for attribute fn
>spec.
>> 	Handle calls to placement new.
>> 	(ndecl_dealloc_argno): Avoid placement delete.
>>
>> gcc/testsuite/ChangeLog:
>>
>> 	PR c++/100876
>> 	* g++.dg/warn/Wmismatched-new-delete-4.C: New test.
>> 	* g++.dg/warn/Wmismatched-new-delete-5.C: New test.
>> 	* g++.dg/warn/Wstringop-overflow-7.C: New test.
>> 	* g++.dg/warn/Wfree-nonheap-object-6.C: New test.
>> 	* g++.dg/analyzer/placement-new.C: Prune out expected warning.
>>
>> diff --git a/gcc/builtins.c b/gcc/builtins.c
>> index af1fe49bb48..fb0717a0248 100644
>> --- a/gcc/builtins.c
>> +++ b/gcc/builtins.c
>> @@ -5159,11 +5159,43 @@ static tree
>>   gimple_call_return_array (gimple *stmt, offset_int offrng[2],
>>   			  range_query *rvals)
>>   {
>> -  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
>> -      || gimple_call_num_args (stmt) < 1)
>> +  {
>> +    /* Check for attribute fn spec to see if the function returns
>one
>> +       of its arguments.  */
>> +    attr_fnspec fnspec = gimple_call_fnspec (as_a <gcall *>(stmt));
>> +    unsigned int argno;
>> +    if (fnspec.returns_arg (&argno))
>> +      {
>> +	offrng[0] = offrng[1] = 0;
>> +	return gimple_call_arg (stmt, argno);
>> +      }
>> +  }
>> +
>> +  if (gimple_call_num_args (stmt) < 1)
>>       return NULL_TREE;
>Nit.  You've got an unnecessary {} at the outer level of this hunk.

if (unsigned int = 1 && fnspec.returns_arg (&argno))

doesn't look too appealing though, I'd leave the curly braces, no?

cheers,
>
>OK with the nit fixed.
>
>THanks,
>Jeff
Bernhard Reutner-Fischer June 14, 2021, 6:04 a.m. UTC | #5
On 14 June 2021 07:56:38 CEST, Bernhard Reutner-Fischer <rep.dot.nop@gmail.com> wrote:
>On 14 June 2021 01:45:36 CEST, Jeff Law via Gcc-patches
><gcc-patches@gcc.gnu.org> wrote:
>>
>>
>>On 6/2/2021 3:40 PM, Martin Sebor via Gcc-patches wrote:
>>> The two forms of placement operator new defined in <new> return
>their
>>> pointer argument and may not be displaced by user-defined functions.
>>> But because they are ordinary (not built-in) functions this property
>>> isn't reflected in their declarations alone, and there's no user-
>>> level attribute to annotate them with.  When they are inlined
>>> the property is transparent in the IL but when they are not (without
>>> inlining such as -O0), calls to the operators appear in the IL and
>>> cause -Wmismatched-new-delete to try to match them with the
>functions
>>> called to deallocate memory.  When the pointer to the memory was
>>> obtained from a function that matches the deallocator but not
>>> the placement new, the warning falsely triggers.
>>>
>>> The attached patch solves this by detecting calls to placement new
>>> and treating them the same as those to other pass-through calls
>(such
>>> as memset).  In addition, it also teaches -Wfree-nonheap-object
>about
>>> placement delete, for a similar reason as above.  Finally, it also
>>> adds a test for attribute fn spec indicating a function returns its
>>> argument.  It's not necessary for the fix (I had initially though
>>> placement new might have the attribute) but it seems appropriate
>>> to check.
>>>
>>> Tested on x86_64-linux.
>>>
>>> Martin
>>>
>>> gcc-100876.diff
>>>
>>> PR c++/100876 - -Wmismatched-new-delete should understand placement
>>new when it's not inlined
>>>
>>> gcc/ChangeLog:
>>>
>>> 	PR c++/100876
>>> 	* builtins.c (gimple_call_return_array): Check for attribute fn
>>spec.
>>> 	Handle calls to placement new.
>>> 	(ndecl_dealloc_argno): Avoid placement delete.
>>>
>>> gcc/testsuite/ChangeLog:
>>>
>>> 	PR c++/100876
>>> 	* g++.dg/warn/Wmismatched-new-delete-4.C: New test.
>>> 	* g++.dg/warn/Wmismatched-new-delete-5.C: New test.
>>> 	* g++.dg/warn/Wstringop-overflow-7.C: New test.
>>> 	* g++.dg/warn/Wfree-nonheap-object-6.C: New test.
>>> 	* g++.dg/analyzer/placement-new.C: Prune out expected warning.
>>>
>>> diff --git a/gcc/builtins.c b/gcc/builtins.c
>>> index af1fe49bb48..fb0717a0248 100644
>>> --- a/gcc/builtins.c
>>> +++ b/gcc/builtins.c
>>> @@ -5159,11 +5159,43 @@ static tree
>>>   gimple_call_return_array (gimple *stmt, offset_int offrng[2],
>>>   			  range_query *rvals)
>>>   {
>>> -  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
>>> -      || gimple_call_num_args (stmt) < 1)

Err, the if was removed, so right.
thanks,

>>> +  {
>>> +    /* Check for attribute fn spec to see if the function returns
>>one
>>> +       of its arguments.  */
>>> +    attr_fnspec fnspec = gimple_call_fnspec (as_a <gcall *>(stmt));
>>> +    unsigned int argno;
>>> +    if (fnspec.returns_arg (&argno))
>>> +      {
>>> +	offrng[0] = offrng[1] = 0;
>>> +	return gimple_call_arg (stmt, argno);
>>> +      }
>>> +  }
>>> +
>>> +  if (gimple_call_num_args (stmt) < 1)
>>>       return NULL_TREE;
>>Nit.  You've got an unnecessary {} at the outer level of this hunk.
>
>if (unsigned int = 1 && fnspec.returns_arg (&argno))
>
>doesn't look too appealing though, I'd leave the curly braces, no?
>
>cheers,
>>
>>OK with the nit fixed.
>>
>>THanks,
>>Jeff
Martin Sebor June 14, 2021, 10:56 p.m. UTC | #6
On 6/13/21 5:45 PM, Jeff Law wrote:
> 
> 
> On 6/2/2021 3:40 PM, Martin Sebor via Gcc-patches wrote:
>> The two forms of placement operator new defined in <new> return their
>> pointer argument and may not be displaced by user-defined functions.
>> But because they are ordinary (not built-in) functions this property
>> isn't reflected in their declarations alone, and there's no user-
>> level attribute to annotate them with.  When they are inlined
>> the property is transparent in the IL but when they are not (without
>> inlining such as -O0), calls to the operators appear in the IL and
>> cause -Wmismatched-new-delete to try to match them with the functions
>> called to deallocate memory.  When the pointer to the memory was
>> obtained from a function that matches the deallocator but not
>> the placement new, the warning falsely triggers.
>>
>> The attached patch solves this by detecting calls to placement new
>> and treating them the same as those to other pass-through calls (such
>> as memset).  In addition, it also teaches -Wfree-nonheap-object about
>> placement delete, for a similar reason as above.  Finally, it also
>> adds a test for attribute fn spec indicating a function returns its
>> argument.  It's not necessary for the fix (I had initially though
>> placement new might have the attribute) but it seems appropriate
>> to check.
>>
>> Tested on x86_64-linux.
>>
>> Martin
>>
>> gcc-100876.diff
>>
>> PR c++/100876 - -Wmismatched-new-delete should understand placement new when it's not inlined
>>
>> gcc/ChangeLog:
>>
>> 	PR c++/100876
>> 	* builtins.c (gimple_call_return_array): Check for attribute fn spec.
>> 	Handle calls to placement new.
>> 	(ndecl_dealloc_argno): Avoid placement delete.
>>
>> gcc/testsuite/ChangeLog:
>>
>> 	PR c++/100876
>> 	* g++.dg/warn/Wmismatched-new-delete-4.C: New test.
>> 	* g++.dg/warn/Wmismatched-new-delete-5.C: New test.
>> 	* g++.dg/warn/Wstringop-overflow-7.C: New test.
>> 	* g++.dg/warn/Wfree-nonheap-object-6.C: New test.
>> 	* g++.dg/analyzer/placement-new.C: Prune out expected warning.
>>
>> diff --git a/gcc/builtins.c b/gcc/builtins.c
>> index af1fe49bb48..fb0717a0248 100644
>> --- a/gcc/builtins.c
>> +++ b/gcc/builtins.c
>> @@ -5159,11 +5159,43 @@ static tree
>>   gimple_call_return_array (gimple *stmt, offset_int offrng[2],
>>   			  range_query *rvals)
>>   {
>> -  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
>> -      || gimple_call_num_args (stmt) < 1)
>> +  {
>> +    /* Check for attribute fn spec to see if the function returns one
>> +       of its arguments.  */
>> +    attr_fnspec fnspec = gimple_call_fnspec (as_a <gcall *>(stmt));
>> +    unsigned int argno;
>> +    if (fnspec.returns_arg (&argno))
>> +      {
>> +	offrng[0] = offrng[1] = 0;
>> +	return gimple_call_arg (stmt, argno);
>> +      }
>> +  }
>> +
>> +  if (gimple_call_num_args (stmt) < 1)
>>       return NULL_TREE;
> Nit.  You've got an unnecessary {} at the outer level of this hunk.
> 
> OK with the nit fixed.

That's intentional, to minimize the scope of of the two new local
variables.

Martin

> 
> THanks,
> Jeff
>
Martin Sebor June 14, 2021, 10:56 p.m. UTC | #7
On 6/2/21 3:46 PM, Marek Polacek wrote:
> On Wed, Jun 02, 2021 at 03:40:49PM -0600, Martin Sebor via Gcc-patches wrote:
>> +  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
>> +    {
>> +      /* See if this is a call to placement new.  */
>> +      if (!fn
>> +	  || !DECL_IS_OPERATOR_NEW_P (fn)
>> +	  || DECL_IS_REPLACEABLE_OPERATOR_NEW_P (fn))
>> +	return NULL_TREE;
>> +
>> +      tree fname = DECL_ASSEMBLER_NAME (fn);
>> +      const char *name = IDENTIFIER_POINTER (fname);
>> +      if (strcmp (name, "_ZnwmPv")       // ordinary form
>> +	  && strcmp (name, "_ZnamPv"))   // array form
>> +	return NULL_TREE;
> 
> Not a review, but you can use id_equal here and simplify things.

Okay, done.

Thanks
Martin
Christophe Lyon June 15, 2021, 10:58 a.m. UTC | #8
Hi,

On Tue, 15 Jun 2021 at 00:58, Martin Sebor via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> On 6/2/21 3:46 PM, Marek Polacek wrote:
> > On Wed, Jun 02, 2021 at 03:40:49PM -0600, Martin Sebor via Gcc-patches wrote:
> >> +  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
> >> +    {
> >> +      /* See if this is a call to placement new.  */
> >> +      if (!fn
> >> +      || !DECL_IS_OPERATOR_NEW_P (fn)
> >> +      || DECL_IS_REPLACEABLE_OPERATOR_NEW_P (fn))
> >> +    return NULL_TREE;
> >> +
> >> +      tree fname = DECL_ASSEMBLER_NAME (fn);
> >> +      const char *name = IDENTIFIER_POINTER (fname);
> >> +      if (strcmp (name, "_ZnwmPv")       // ordinary form
> >> +      && strcmp (name, "_ZnamPv"))   // array form
> >> +    return NULL_TREE;
> >
> > Not a review, but you can use id_equal here and simplify things.
>
> Okay, done.
>

On arm the new test Wmismatched-new-delete-4.C fails:
/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C: In function
'void nowarn_placement_new()':
/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C:16:8: warning:
'void free(void*)' called on pointer returned from a mismatched
allocation function [-Wmismatched-new-delete]
/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C:16:42: note:
returned from 'void* operator new(unsigned int, void*)'
/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C: In function
'void nowarn_placement_array_new()':
/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C:21:8: warning:
'void free(void*)' called on pointer returned from a mismatched
allocation function [-Wmismatched-new-delete]
/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C:21:8: note:
returned from 'void* operator new [](unsigned int, void*)'
FAIL: g++.dg/warn/Wmismatched-new-delete-4.C  -std=gnu++98  (test for
bogus messages, line 16)
FAIL: g++.dg/warn/Wmismatched-new-delete-4.C  -std=gnu++98  (test for
bogus messages, line 21)
FAIL: g++.dg/warn/Wmismatched-new-delete-4.C  -std=gnu++98  (test for
warnings, line 29)
FAIL: g++.dg/warn/Wmismatched-new-delete-4.C  -std=gnu++98  (test for
warnings, line 36)


and Wstringop-overflow-7.C:
FAIL: g++.dg/warn/Wstringop-overflow-7.C  -std=gnu++98  (test for
warnings, line 23)
FAIL: g++.dg/warn/Wstringop-overflow-7.C  -std=gnu++98  (test for
warnings, line 40)
PASS: g++.dg/warn/Wstringop-overflow-7.C  -std=gnu++98 (test for excess errors)

(no warning emitted)

Can you  check?

Thanks,

Christophe

> Thanks
> Martin
Martin Sebor June 15, 2021, 6:48 p.m. UTC | #9
On 6/15/21 4:58 AM, Christophe Lyon wrote:
> Hi,
> 
> On Tue, 15 Jun 2021 at 00:58, Martin Sebor via Gcc-patches
> <gcc-patches@gcc.gnu.org> wrote:
>>
>> On 6/2/21 3:46 PM, Marek Polacek wrote:
>>> On Wed, Jun 02, 2021 at 03:40:49PM -0600, Martin Sebor via Gcc-patches wrote:
>>>> +  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
>>>> +    {
>>>> +      /* See if this is a call to placement new.  */
>>>> +      if (!fn
>>>> +      || !DECL_IS_OPERATOR_NEW_P (fn)
>>>> +      || DECL_IS_REPLACEABLE_OPERATOR_NEW_P (fn))
>>>> +    return NULL_TREE;
>>>> +
>>>> +      tree fname = DECL_ASSEMBLER_NAME (fn);
>>>> +      const char *name = IDENTIFIER_POINTER (fname);
>>>> +      if (strcmp (name, "_ZnwmPv")       // ordinary form
>>>> +      && strcmp (name, "_ZnamPv"))   // array form
>>>> +    return NULL_TREE;
>>>
>>> Not a review, but you can use id_equal here and simplify things.
>>
>> Okay, done.
>>
> 
> On arm the new test Wmismatched-new-delete-4.C fails:
> /gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C: In function
> 'void nowarn_placement_new()':
> /gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C:16:8: warning:
> 'void free(void*)' called on pointer returned from a mismatched
> allocation function [-Wmismatched-new-delete]
> /gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C:16:42: note:
> returned from 'void* operator new(unsigned int, void*)'
> /gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C: In function
> 'void nowarn_placement_array_new()':
> /gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C:21:8: warning:
> 'void free(void*)' called on pointer returned from a mismatched
> allocation function [-Wmismatched-new-delete]
> /gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C:21:8: note:
> returned from 'void* operator new [](unsigned int, void*)'
> FAIL: g++.dg/warn/Wmismatched-new-delete-4.C  -std=gnu++98  (test for
> bogus messages, line 16)
> FAIL: g++.dg/warn/Wmismatched-new-delete-4.C  -std=gnu++98  (test for
> bogus messages, line 21)
> FAIL: g++.dg/warn/Wmismatched-new-delete-4.C  -std=gnu++98  (test for
> warnings, line 29)
> FAIL: g++.dg/warn/Wmismatched-new-delete-4.C  -std=gnu++98  (test for
> warnings, line 36)
> 
> 
> and Wstringop-overflow-7.C:
> FAIL: g++.dg/warn/Wstringop-overflow-7.C  -std=gnu++98  (test for
> warnings, line 23)
> FAIL: g++.dg/warn/Wstringop-overflow-7.C  -std=gnu++98  (test for
> warnings, line 40)
> PASS: g++.dg/warn/Wstringop-overflow-7.C  -std=gnu++98 (test for excess errors)
> 
> (no warning emitted)
> 
> Can you  check?

I've pushed r12-1490 to adjust the code to check the mangling of
both size_t representations: unsigned int and unsigned long.

Martin
diff mbox series

Patch

PR c++/100876 - -Wmismatched-new-delete should understand placement new when it's not inlined

gcc/ChangeLog:

	PR c++/100876
	* builtins.c (gimple_call_return_array): Check for attribute fn spec.
	Handle calls to placement new.
	(ndecl_dealloc_argno): Avoid placement delete.

gcc/testsuite/ChangeLog:

	PR c++/100876
	* g++.dg/warn/Wmismatched-new-delete-4.C: New test.
	* g++.dg/warn/Wmismatched-new-delete-5.C: New test.
	* g++.dg/warn/Wstringop-overflow-7.C: New test.
	* g++.dg/warn/Wfree-nonheap-object-6.C: New test.
	* g++.dg/analyzer/placement-new.C: Prune out expected warning.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index af1fe49bb48..fb0717a0248 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -5159,11 +5159,43 @@  static tree
 gimple_call_return_array (gimple *stmt, offset_int offrng[2],
 			  range_query *rvals)
 {
-  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
-      || gimple_call_num_args (stmt) < 1)
+  {
+    /* Check for attribute fn spec to see if the function returns one
+       of its arguments.  */
+    attr_fnspec fnspec = gimple_call_fnspec (as_a <gcall *>(stmt));
+    unsigned int argno;
+    if (fnspec.returns_arg (&argno))
+      {
+	offrng[0] = offrng[1] = 0;
+	return gimple_call_arg (stmt, argno);
+      }
+  }
+
+  if (gimple_call_num_args (stmt) < 1)
     return NULL_TREE;
 
   tree fn = gimple_call_fndecl (stmt);
+  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
+    {
+      /* See if this is a call to placement new.  */
+      if (!fn
+	  || !DECL_IS_OPERATOR_NEW_P (fn)
+	  || DECL_IS_REPLACEABLE_OPERATOR_NEW_P (fn))
+	return NULL_TREE;
+
+      tree fname = DECL_ASSEMBLER_NAME (fn);
+      const char *name = IDENTIFIER_POINTER (fname);
+      if (strcmp (name, "_ZnwmPv")       // ordinary form
+	  && strcmp (name, "_ZnamPv"))   // array form
+	return NULL_TREE;
+
+      if (gimple_call_num_args (stmt) != 2)
+	return NULL_TREE;
+
+      offrng[0] = offrng[1] = 0;
+      return gimple_call_arg (stmt, 1);
+    }
+
   switch (DECL_FUNCTION_CODE (fn))
     {
     case BUILT_IN_MEMCPY:
@@ -13285,7 +13317,18 @@  fndecl_dealloc_argno (tree fndecl)
 {
   /* A call to operator delete isn't recognized as one to a built-in.  */
   if (DECL_IS_OPERATOR_DELETE_P (fndecl))
-    return 0;
+    {
+      if (DECL_IS_REPLACEABLE_OPERATOR (fndecl))
+	return 0;
+
+      /* Avoid placement delete that's not been inlined.  */
+      tree fname = DECL_ASSEMBLER_NAME (fndecl);
+      const char *name = IDENTIFIER_POINTER (fname);
+      if (strcmp (name, "_ZdlPvS_") == 0       // ordinary form
+	  || strcmp (name, "_ZdaPvS_") == 0)   // array form
+	return UINT_MAX;
+      return 0;
+    }
 
   /* TODO: Handle user-defined functions with attribute malloc?  Handle
      known non-built-ins like fopen?  */
diff --git a/gcc/testsuite/g++.dg/analyzer/placement-new.C b/gcc/testsuite/g++.dg/analyzer/placement-new.C
index 8250f45b9d9..b648a428247 100644
--- a/gcc/testsuite/g++.dg/analyzer/placement-new.C
+++ b/gcc/testsuite/g++.dg/analyzer/placement-new.C
@@ -24,3 +24,5 @@  void test_3 (void)
   int *p = new(buf) int (42);
   delete p; // { dg-warning "memory not on the heap" }
 }
+
+// { dg-prune-output "-Wfree-nonheap-object" }
diff --git a/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-6.C b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-6.C
new file mode 100644
index 00000000000..83b6ff9157c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-6.C
@@ -0,0 +1,45 @@ 
+/* { dg-do compile }
+   { dg-options "-O0 -Wall" } */
+
+#if __cplusplus < 201103L
+# define noexcept throw ()
+#endif
+
+void* operator new (__SIZE_TYPE__, void* __p) noexcept;
+void operator delete (void*, void*);
+
+void* operator new[] (__SIZE_TYPE__, void* __p) noexcept;
+void operator delete[] (void*, void*) noexcept;
+
+struct A { A (); ~A (); int i; };
+
+extern void *p;
+
+void nowarn_placement_new ()
+{
+  char a[sizeof (A)];
+  p = new (a) A ();           // { dg-bogus "-Wfree-nonheap-object" }
+}
+
+
+void warn_placement_new ()
+{
+  char a[sizeof (A)];
+  p = new (a + 1) A ();       // { dg-warning "\\\[-Wplacement-new" }
+                              // { dg-bogus "-Wfree-nonheap-object" "bogus" { target *-*-* } .-1 }
+}
+
+
+void nowarn_placement_new_array ()
+{
+  char a[sizeof (A)];
+  p = new (a) A[1];           // { dg-bogus "-Wfree-nonheap-object" }
+}
+
+
+void warn_placement_new_array ()
+{
+  char a[sizeof (A)];
+  p = new (a + 1) A[1];       // { dg-warning "\\\[-Wplacement-new" }
+                              // { dg-bogus "-Wfree-nonheap-object" "bogus" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C
new file mode 100644
index 00000000000..4320181e4d7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-4.C
@@ -0,0 +1,37 @@ 
+/* PR c++/100876 - -Wmismatched-new-delete should either look through
+   or ignore placement new
+   { dg-do compile }
+   { dg-options "-O0 -Wall" } */
+
+extern "C" {
+  void* malloc (__SIZE_TYPE__);
+  void free (void*);
+}
+
+void* operator new (__SIZE_TYPE__, void*);
+void* operator new[] (__SIZE_TYPE__, void*);
+
+void nowarn_placement_new ()
+{
+  free (new (malloc (sizeof (int))) int ());      // { dg-bogus "-Wmismatched-new-delete" }
+}
+
+void nowarn_placement_array_new ()
+{
+  free (new (malloc (sizeof (int) * 2)) int[2]);  // { dg-bogus "-Wmismatched-new-delete" }
+}
+
+
+void warn_placement_new ()
+{
+  void *p = malloc (sizeof (int));
+  int *q = new (p) int ();
+  delete q;                   // { dg-warning "-Wmismatched-new-delete" }
+}
+
+void warn_placement_array_new ()
+{
+  void *p = malloc (sizeof (int));
+  int *q = new (p) int[2];
+  delete q;                   // { dg-warning "-Wmismatched-new-delete" }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-5.C b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-5.C
new file mode 100644
index 00000000000..92c75df40d9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-5.C
@@ -0,0 +1,37 @@ 
+/* PR c++/100876 - -Wmismatched-new-delete should either look through
+   or ignore placement new
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+extern "C" {
+  void* malloc (__SIZE_TYPE__);
+  void free (void*);
+}
+
+inline void* operator new (__SIZE_TYPE__, void *p) { return p; }
+inline void* operator new[] (__SIZE_TYPE__, void *p) { return p; }
+
+void nowarn_placement_new ()
+{
+  free (new (malloc (sizeof (int))) int ());      // { dg-bogus "-Wmismatched-new-delete" }
+}
+
+void nowarn_placement_array_new ()
+{
+  free (new (malloc (sizeof (int) * 2)) int[2]);  // { dg-bogus "-Wmismatched-new-delete" }
+}
+
+
+void warn_placement_new ()
+{
+  void *p = malloc (sizeof (int));
+  int *q = new (p) int ();
+  delete q;                   // { dg-warning "-Wmismatched-new-delete" }
+}
+
+void warn_placement_array_new ()
+{
+  void *p = malloc (sizeof (int));
+  int *q = new (p) int[2];
+  delete q;                   // { dg-warning "-Wmismatched-new-delete" }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wstringop-overflow-7.C b/gcc/testsuite/g++.dg/warn/Wstringop-overflow-7.C
new file mode 100644
index 00000000000..d3d28f43d0a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wstringop-overflow-7.C
@@ -0,0 +1,42 @@ 
+/* PR c++/100876 - -Wmismatched-new-delete should either look through
+   or ignore placement new
+   { dg-do compile }
+   { dg-options "-O0 -Wall -Wno-array-bounds" } */
+
+inline void* operator new (__SIZE_TYPE__, void *p) { return p; }
+inline void* operator new[] (__SIZE_TYPE__, void *p) { return p; }
+
+void* nowarn_placement_new_memset ()
+{
+  struct S { int i; };
+  void *p = __builtin_malloc (sizeof (S));
+  S *q = new (p) S;
+  __builtin_memset (q, 0, sizeof (S));
+  return q;
+}
+
+void* warn_placement_new_memset ()
+{
+  struct S { int i; };
+  void *p = __builtin_malloc (sizeof (S));
+  S *q = new (p) S;
+  __builtin_memset (q, 0, sizeof (S) + 1);  // { dg-warning "\\\[-Wstringop-overflow" }
+  return q;
+}
+
+void* nowarn_placement_new_array_strncpy (const char *s)
+{
+  void *p = __builtin_malloc (5);
+  char *q = new (p) char[5];
+  __builtin_strncpy (q, s, 5);
+  return q;
+
+}
+
+void* warn_placement_new_array_strncpy (const char *s)
+{
+  void *p = __builtin_malloc (4);
+  char *q = new (p) char[5];
+  __builtin_strncpy (q, s, 5);  // { dg-warning "\\\[-Wstringop-overflow" }
+  return q;
+}