assert: Suppress pedantic warning caused by statement expression [BZ# 21242]

Message ID a26386c3-7c12-be12-14f4-4d3ca7930452@redhat.com
State Superseded
Headers

Commit Message

Florian Weimer July 4, 2017, 2:10 p.m. UTC
  On 07/04/2017 04:00 PM, Florian Weimer wrote:
> On 06/26/2017 12:56 PM, Joseph Myers wrote:
>> On Sun, 25 Jun 2017, Florian Weimer wrote:
>>
>>> I think we can use __extension__ if we also expand the expression
>>> without __extension__ in an unevaluated context.  The tricky part is to
>>> find one that is independent of GNU extensions.
>>> Perhaps this would work?
>>>
>>> #  define assert(expr)                                           \
>>>   ((void) sizeof ((expr) == 0), __extension__ ({                 \
>>>       if (expr)                                                  \
>>>         ; /* empty */                                            \
>>>       else                                                       \
>>>         __assert_fail (#expr, __FILE__, __LINE__, __FUNCTION__); \
>>>     }))
>>>
>>> sizeof suppresses the evaluation of the first occurrence of expr.  The
>>> comparison is needed because sizeof cannot be applied to function
>>> pointers and bitfields.  C11 says that expr is compared to zero, so the
>>> (expr) == 0 expression is well-formed.
>>>
>>> What do you think?  Should we make this change?
>>
>> I think that's reasonable (appropriately commented to explain why it's 
>> done that way).
> 
> This is what I came up with as a patch.
> 
> I would consider this still appropriate during the freeze.  It's
> something we'd backport to glibc 2.25, too.

Hopefully now with attachment.

Florian
  

Comments

Zack Weinberg July 5, 2017, 3:46 p.m. UTC | #1
On Tue, Jul 4, 2017 at 10:10 AM, Florian Weimer <fweimer@redhat.com> wrote:
> On 07/04/2017 04:00 PM, Florian Weimer wrote:
>> On 06/26/2017 12:56 PM, Joseph Myers wrote:
>>> On Sun, 25 Jun 2017, Florian Weimer wrote:
>>>
>>>> I think we can use __extension__ if we also expand the expression
>>>> without __extension__ in an unevaluated context.
...
>>
>> This is what I came up with as a patch.
>>
>> I would consider this still appropriate during the freeze.  It's
>> something we'd backport to glibc 2.25, too.
>
> Hopefully now with attachment.
...

> +/* The first occurrence of EXPR is not evaluated due to the sizeof,
> +   but will trigger any pedantic warnings masked by the __extension__
> +   for the second occurrence.  The explicit comparison against zero is
> +   required to support function pointers and bit fields in this
> +   context.  */
> #  define assert(expr)                            \
> -    ({                                    \
> +  ((void) sizeof ((expr) == 0), __extension__ ({            \

A problem occurs to me: expressions involving VLAs _are_ evaluated
inside sizeof.  Consider

   extern int f(int);
   void g(void)
   {
       const int n = 4;
       int array[n] = { 0, 0, 0, 0 }; // in C99, array is a VLA
       assert(array[f(n)] == 0);
    }

f() should be called only once, but I _think_ it will be called twice
with this definition of assert.

zw
  
Florian Weimer July 5, 2017, 3:51 p.m. UTC | #2
On 07/05/2017 05:46 PM, Zack Weinberg wrote:
> A problem occurs to me: expressions involving VLAs _are_ evaluated
> inside sizeof.  Consider
> 
>    extern int f(int);
>    void g(void)
>    {
>        const int n = 4;
>        int array[n] = { 0, 0, 0, 0 }; // in C99, array is a VLA
>        assert(array[f(n)] == 0);
>     }
> 
> f() should be called only once, but I _think_ it will be called twice
> with this definition of assert.

The type of the sizeof argument would still be int (due to the
comparison against 0), so this doesn't actually occur.

Florian
  
Zack Weinberg July 5, 2017, 8:15 p.m. UTC | #3
On Wed, Jul 5, 2017 at 11:51 AM, Florian Weimer <fweimer@redhat.com> wrote:
> On 07/05/2017 05:46 PM, Zack Weinberg wrote:
>> A problem occurs to me: expressions involving VLAs _are_ evaluated
>> inside sizeof.
>
> The type of the sizeof argument would still be int (due to the
> comparison against 0), so this doesn't actually occur.

I rechecked what C99 says about sizeof and VLAs, and you're right -
the operand of sizeof is only evaluated when sizeof is _directly_
applied to a VLA.  So this is indeed safe, but I think this wrinkle
should be mentioned in the comment.  Perhaps

/* The first occurrence of EXPR is not evaluated due to the sizeof,
   but will trigger any pedantic warnings masked by the __extension__
   for the second occurrence.  The explicit comparison against zero
   ensures that sizeof is not directly applied to a function pointer or
   bit-field (which would be ill-formed) or VLA (which would be evaluated).  */

zw
  

Patch

assert: Suppress pedantic warning caused by statement expression

2017-07-04  Florian Weimer  <fweimer@redhat.com>

	[BZ #21242]
	* assert/assert.h [__GNUC__ && !__STRICT_ANSI__] (assert):
	Suppress pedantic warning resulting from statement expression.
	(__ASSERT_FUNCTION): Add missing __extendsion__.

diff --git a/assert/assert.h b/assert/assert.h
index 22f0195..0ded8c7 100644
--- a/assert/assert.h
+++ b/assert/assert.h
@@ -91,13 +91,18 @@  __END_DECLS
      ? __ASSERT_VOID_CAST (0)						\
      : __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION))
 # else
+/* The first occurrence of EXPR is not evaluated due to the sizeof,
+   but will trigger any pedantic warnings masked by the __extension__
+   for the second occurrence.  The explicit comparison against zero is
+   required to support function pointers and bit fields in this
+   context.  */
 #  define assert(expr)							\
-    ({									\
+  ((void) sizeof ((expr) == 0), __extension__ ({			\
       if (expr)								\
         ; /* empty */							\
       else								\
         __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION);	\
-    })
+    }))
 # endif
 
 # ifdef	__USE_GNU
@@ -113,7 +118,7 @@  __END_DECLS
    C9x has a similar variable called __func__, but prefer the GCC one since
    it demangles C++ function names.  */
 # if defined __cplusplus ? __GNUC_PREREQ (2, 6) : __GNUC_PREREQ (2, 4)
-#   define __ASSERT_FUNCTION	__PRETTY_FUNCTION__
+#   define __ASSERT_FUNCTION	__extension__ __PRETTY_FUNCTION__
 # else
 #  if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
 #   define __ASSERT_FUNCTION	__func__