public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3)
@ 2024-04-16  2:37 alx at kernel dot org
  2024-04-16  2:49 ` [Bug c/114731] " sjames at gcc dot gnu.org
                   ` (20 more replies)
  0 siblings, 21 replies; 22+ messages in thread
From: alx at kernel dot org @ 2024-04-16  2:37 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

            Bug ID: 114731
           Summary: -Wincompatible-pointer-types false positive in
                    combination with _Generic(3)
           Product: gcc
           Version: 13.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: alx at kernel dot org
  Target Milestone: ---

Hi!

I'm writing type-generic macros with two levels of _Generic(3), because I want
to act depending on the type of two arguments.  It's implemented as a
single-_Generic() macro, which calls other single-_Generic() macros,
effectively implementing two levels of switching.

Here's a small reproducer of these macros:


#define a2i(TYPE, ...)                                                        \
(                                                                             \
        _Generic((TYPE) 0,                                                    \
                long:               a2sl(__VA_ARGS__),                        \
                int:                a2si(__VA_ARGS__),                        \
        )                                                                     \
)


#define a2sl(n, s, endp, base, min, max)                                      \
(                                                                             \
        _Generic(endp,                                                        \
                const char **:  a2sl_c,                                       \
                char **:        a2sl_nc,                                      \
                void *:         a2sl_c                                        \
        )(n, s, endp, base, min, max)                                         \
)

#define a2si(n, s, endp, base, min, max)                                      \
(                                                                             \
        _Generic(endp,                                                        \
                const char **:  a2si_c,                                       \
                char **:        a2si_nc,                                      \
                void *:         a2si_c                                        \
        )(n, s, endp, base, min, max)                                         \
)


As long as you only use one _Generic, it's easy, because you can pass the
argument list outside of _Generic(), and it will call the right function. 
However, if you want to call a macro (the second level), you must write the
argument list inside the _Generic() switch.  This triggers warnings in all the
non-evaluated expressions.

These warnings should not be emitted, and only the evaluated expression in
_Generic() should emit warnings about types, I think.

Below is an entire reproducer program, and it's output:

$ cat g.c 
#include <bsd/inttypes.h>
#include <errno.h>
#include <time.h>


#define a2i(TYPE, ...)                                                        \
(                                                                             \
        _Generic((TYPE) 0,                                                    \
                long:               a2sl(__VA_ARGS__),                        \
                int:                a2si(__VA_ARGS__),                        \
        )                                                                     \
)


#define a2sl(n, s, endp, base, min, max)                                      \
(                                                                             \
        _Generic(endp,                                                        \
                const char **:  a2sl_c,                                       \
                char **:        a2sl_nc,                                      \
                void *:         a2sl_c                                        \
        )(n, s, endp, base, min, max)                                         \
)

#define a2si(n, s, endp, base, min, max)                                      \
(                                                                             \
        _Generic(endp,                                                        \
                const char **:  a2si_c,                                       \
                char **:        a2si_nc,                                      \
                void *:         a2si_c                                        \
        )(n, s, endp, base, min, max)                                         \
)


static inline int a2sl_c(long *restrict n, const char *s,
    const char **restrict endp, int base, long min, long max);
static inline int a2si_c(int *restrict n, const char *s,
    const char **restrict endp, int base, int min, int max);

static inline int a2sl_nc(long *restrict n, const char *s,
    char **restrict endp, int base, long min, long max);
static inline int a2si_nc(int *restrict n, const char *s,
    char **restrict endp, int base, int min, int max);


static inline int
a2sl_c(long *restrict n, const char *s,
    const char **restrict endp, int base, long min, long max)
{
        return a2sl_nc(n, s, (char **) endp, base, min, max);
}


static inline int
a2si_c(int *restrict n, const char *s,
    const char **restrict endp, int base, int min, int max)
{
        return a2si_nc(n, s, (char **) endp, base, min, max);
}


static inline int
a2sl_nc(long *restrict n, const char *s,
    char **restrict endp, int base, long min, long max)
{
        int  status;

        *n = strtoi(s, endp, base, min, max, &status);
        if (status != 0) {
                errno = status;
                return -1;
        }
        return 0;
}


static inline int
a2si_nc(int *restrict n, const char *s,
    char **restrict endp, int base, int min, int max)
{
        int  status;

        *n = strtoi(s, endp, base, min, max, &status);
        if (status != 0) {
                errno = status;
                return -1;
        }
        return 0;
}


int
main(void)
{
        time_t  t;

        a2i(time_t, &t, "42", NULL, 0, 0, 10);
}
$ gcc-13 -Wall -Wextra -Wincompatible-pointer-types g.c $(pkgconf --cflags
--libs libbsd)
g.c: In function ‘main’:
g.c:96:21: warning: passing argument 1 of ‘a2si_c’ from incompatible pointer
type [-Wincompatible-pointer-types]
   96 |         a2i(time_t, &t, "42", NULL, 0, 0, 10);
      |                     ^~
      |                     |
      |                     time_t * {aka long int *}
g.c:30:11: note: in definition of macro ‘a2si’
   30 |         )(n, s, endp, base, min, max)                                  
      \
      |           ^
g.c:96:9: note: in expansion of macro ‘a2i’
   96 |         a2i(time_t, &t, "42", NULL, 0, 0, 10);
      |         ^~~
g.c:54:22: note: expected ‘int * restrict’ but argument is of type ‘time_t *’
{aka ‘long int *’}
   54 | a2si_c(int *restrict n, const char *s,
      |        ~~~~~~~~~~~~~~^
g.c:11:9: error: expected specifier-qualifier-list before ‘)’ token
   11 |         )                                                              
      \
      |         ^
g.c:96:9: note: in expansion of macro ‘a2i’
   96 |         a2i(time_t, &t, "42", NULL, 0, 0, 10);
      |         ^~~

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
@ 2024-04-16  2:49 ` sjames at gcc dot gnu.org
  2024-04-16  2:56 ` alx at kernel dot org
                   ` (19 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: sjames at gcc dot gnu.org @ 2024-04-16  2:49 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

Sam James <sjames at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|UNCONFIRMED                 |WAITING
     Ever confirmed|0                           |1
   Last reconfirmed|                            |2024-04-16
                 CC|                            |sjames at gcc dot gnu.org

--- Comment #1 from Sam James <sjames at gcc dot gnu.org> ---
This example doesn't compile.

$ gcc-13 -Wall -Wextra -Wno-incompatible-pointer-types /tmp/foo.c $(pkgconf
--cflags --libs libbsd)
/tmp/foo.c: In function 'main':
/tmp/foo.c:11:9: error: expected specifier-qualifier-list before ')' token
   11 |         )                                                              
      \
      |         ^
/tmp/foo.c:96:9: note: in expansion of macro 'a2i'
   96 |         a2i(time_t, &t, "42", NULL, 0, 0, 10);
      |         ^~~

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
  2024-04-16  2:49 ` [Bug c/114731] " sjames at gcc dot gnu.org
@ 2024-04-16  2:56 ` alx at kernel dot org
  2024-04-16  2:58 ` alx at kernel dot org
                   ` (18 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: alx at kernel dot org @ 2024-04-16  2:56 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #2 from Alejandro Colomar <alx at kernel dot org> ---
That's precisely what I meant (my bad, I used incorrect wording).  I think that
diagnostic is spurious, and should be removed.

A few self-corrections:

-  I should have said diagnostics, instead of warnings.
-  I should have said compiler output, instead of output.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
  2024-04-16  2:49 ` [Bug c/114731] " sjames at gcc dot gnu.org
  2024-04-16  2:56 ` alx at kernel dot org
@ 2024-04-16  2:58 ` alx at kernel dot org
  2024-04-16  2:59 ` sjames at gcc dot gnu.org
                   ` (17 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: alx at kernel dot org @ 2024-04-16  2:58 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #3 from Alejandro Colomar <alx at kernel dot org> ---
The diagnostic is spurious, because of course if I call the following macro



#define a2i(TYPE, ...)                                                        \
(                                                                             \
        _Generic((TYPE) 0,                                                    \
                long:               a2sl(__VA_ARGS__),                        \
                int:                a2si(__VA_ARGS__),                        \
        )                                                                     \
)


I only want to use the variant corresponding to the type that I passed, and of
course the other variant will be incompatible with the one I want.  That's the
whole reason to use _Generic() in the first place.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (2 preceding siblings ...)
  2024-04-16  2:58 ` alx at kernel dot org
@ 2024-04-16  2:59 ` sjames at gcc dot gnu.org
  2024-04-16  3:02 ` alx at kernel dot org
                   ` (16 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: sjames at gcc dot gnu.org @ 2024-04-16  2:59 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #4 from Sam James <sjames at gcc dot gnu.org> ---
I think this sort of thing is somewhat common when it comes to something the
frontend can't even parse. But we'll see what others say.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (3 preceding siblings ...)
  2024-04-16  2:59 ` sjames at gcc dot gnu.org
@ 2024-04-16  3:02 ` alx at kernel dot org
  2024-04-16  3:03 ` pinskia at gcc dot gnu.org
                   ` (15 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: alx at kernel dot org @ 2024-04-16  3:02 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #5 from Alejandro Colomar <alx at kernel dot org> ---
Ahhh, sorry Sam.  I had a mistake while writing the reproducer, and didn't
realize.  I thought it was a side effect of something else.


Here's a fixed reproducer, with the fixed diagnostics.


$ cat g.c 
#include <bsd/inttypes.h>
#include <errno.h>
#include <time.h>


#define a2i(TYPE, ...)                                                        \
(                                                                             \
        _Generic((TYPE) 0,                                                    \
                long:               a2sl(__VA_ARGS__),                        \
                int:                a2si(__VA_ARGS__)                        \
        )                                                                     \
)


#define a2sl(n, s, endp, base, min, max)                                      \
(                                                                             \
        _Generic(endp,                                                        \
                const char **:  a2sl_c,                                       \
                char **:        a2sl_nc,                                      \
                void *:         a2sl_c                                        \
        )(n, s, endp, base, min, max)                                         \
)

#define a2si(n, s, endp, base, min, max)                                      \
(                                                                             \
        _Generic(endp,                                                        \
                const char **:  a2si_c,                                       \
                char **:        a2si_nc,                                      \
                void *:         a2si_c                                        \
        )(n, s, endp, base, min, max)                                         \
)


static inline int a2sl_c(long *restrict n, const char *s,
    const char **restrict endp, int base, long min, long max);
static inline int a2si_c(int *restrict n, const char *s,
    const char **restrict endp, int base, int min, int max);

static inline int a2sl_nc(long *restrict n, const char *s,
    char **restrict endp, int base, long min, long max);
static inline int a2si_nc(int *restrict n, const char *s,
    char **restrict endp, int base, int min, int max);


static inline int
a2sl_c(long *restrict n, const char *s,
    const char **restrict endp, int base, long min, long max)
{
        return a2sl_nc(n, s, (char **) endp, base, min, max);
}


static inline int
a2si_c(int *restrict n, const char *s,
    const char **restrict endp, int base, int min, int max)
{
        return a2si_nc(n, s, (char **) endp, base, min, max);
}


static inline int
a2sl_nc(long *restrict n, const char *s,
    char **restrict endp, int base, long min, long max)
{
        int  status;

        *n = strtoi(s, endp, base, min, max, &status);
        if (status != 0) {
                errno = status;
                return -1;
        }
        return 0;
}


static inline int
a2si_nc(int *restrict n, const char *s,
    char **restrict endp, int base, int min, int max)
{
        int  status;

        *n = strtoi(s, endp, base, min, max, &status);
        if (status != 0) {
                errno = status;
                return -1;
        }
        return 0;
}


int
main(void)
{
        time_t  t;

        a2i(time_t, &t, "42", NULL, 0, 0, 10);
}
alx@debian:~/tmp/c$ cc -Wall -Wextra g.c 
g.c: In function ‘main’:
g.c:96:21: warning: passing argument 1 of ‘a2si_c’ from incompatible pointer
type [-Wincompatible-pointer-types]
   96 |         a2i(time_t, &t, "42", NULL, 0, 0, 10);
      |                     ^~
      |                     |
      |                     time_t * {aka long int *}
g.c:30:11: note: in definition of macro ‘a2si’
   30 |         )(n, s, endp, base, min, max)                                  
      \
      |           ^
g.c:96:9: note: in expansion of macro ‘a2i’
   96 |         a2i(time_t, &t, "42", NULL, 0, 0, 10);
      |         ^~~
g.c:54:22: note: expected ‘int * restrict’ but argument is of type ‘time_t *’
{aka ‘long int *’}
   54 | a2si_c(int *restrict n, const char *s,
      |        ~~~~~~~~~~~~~~^
/usr/bin/ld: /tmp/ccQQX6M9.o: in function `a2sl_nc':
g.c:(.text+0x86): undefined reference to `strtoi'
collect2: error: ld returned 1 exit status

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (4 preceding siblings ...)
  2024-04-16  3:02 ` alx at kernel dot org
@ 2024-04-16  3:03 ` pinskia at gcc dot gnu.org
  2024-04-16  3:05 ` pinskia at gcc dot gnu.org
                   ` (14 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: pinskia at gcc dot gnu.org @ 2024-04-16  3:03 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #6 from Andrew Pinski <pinskia at gcc dot gnu.org> ---
Because the inner _Generic gets selected first and then outter one is selected.

Also note clang has the same behavior .

Note the syntax error is just a2i should not have a comma in it.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (5 preceding siblings ...)
  2024-04-16  3:03 ` pinskia at gcc dot gnu.org
@ 2024-04-16  3:05 ` pinskia at gcc dot gnu.org
  2024-04-16  3:10 ` pinskia at gcc dot gnu.org
                   ` (13 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: pinskia at gcc dot gnu.org @ 2024-04-16  3:05 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

Andrew Pinski <pinskia at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |diagnostic
             Status|WAITING                     |NEW
           See Also|                            |https://gcc.gnu.org/bugzill
                   |                            |a/show_bug.cgi?id=97100

--- Comment #7 from Andrew Pinski <pinskia at gcc dot gnu.org> ---
Related to PR 97100 (and others).

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (6 preceding siblings ...)
  2024-04-16  3:05 ` pinskia at gcc dot gnu.org
@ 2024-04-16  3:10 ` pinskia at gcc dot gnu.org
  2024-04-16  3:11 ` alx at kernel dot org
                   ` (12 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: pinskia at gcc dot gnu.org @ 2024-04-16  3:10 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #8 from Andrew Pinski <pinskia at gcc dot gnu.org> ---
(In reply to Andrew Pinski from comment #6)
> Also note clang has the same behavior .

ICC does too.

MSVC actually gets it right (need to use /std:clatest) and change time_t into
long since time_t there is `long long` and your test does not include `long
long` support ...

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (7 preceding siblings ...)
  2024-04-16  3:10 ` pinskia at gcc dot gnu.org
@ 2024-04-16  3:11 ` alx at kernel dot org
  2024-04-16  3:15 ` pinskia at gcc dot gnu.org
                   ` (11 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: alx at kernel dot org @ 2024-04-16  3:11 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #9 from Alejandro Colomar <alx at kernel dot org> ---
The related bug claims to be fixed in trunk.  I can still reproduce mine in
gcc-14, from Debian RC-Buggy.


alx@debian:~/tmp/c$ gcc-14 -Wall -Wextra g.c -S
g.c: In function ‘main’:
g.c:96:21: error: passing argument 1 of ‘a2si_c’ from incompatible pointer type
[-Wincompatible-pointer-types]
   96 |         a2i(time_t, &t, "42", NULL, 0, 0, 10);
      |                     ^~
      |                     |
      |                     time_t * {aka long int *}
g.c:30:11: note: in definition of macro ‘a2si’
   30 |         )(n, s, endp, base, min, max)                                  
      \
      |           ^
g.c:96:9: note: in expansion of macro ‘a2i’
   96 |         a2i(time_t, &t, "42", NULL, 0, 0, 10);
      |         ^~~
g.c:54:22: note: expected ‘int * restrict’ but argument is of type ‘time_t *’
{aka ‘long int *’}
   54 | a2si_c(int *restrict n, const char *s,
      |        ~~~~~~~~~~~~~~^
alx@debian:~/tmp/c$ gcc-14 --version
gcc-14 (Debian 14-20240330-1) 14.0.1 20240330 (experimental) [master
r14-9728-g6fc84f680d0]
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (8 preceding siblings ...)
  2024-04-16  3:11 ` alx at kernel dot org
@ 2024-04-16  3:15 ` pinskia at gcc dot gnu.org
  2024-04-16  3:17 ` alx at kernel dot org
                   ` (10 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: pinskia at gcc dot gnu.org @ 2024-04-16  3:15 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #10 from Andrew Pinski <pinskia at gcc dot gnu.org> ---
(In reply to Alejandro Colomar from comment #9)
> The related bug claims to be fixed in trunk.  I can still reproduce mine in
> gcc-14, from Debian RC-Buggy.

Pedwarn warnings were not fixed ...

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (9 preceding siblings ...)
  2024-04-16  3:15 ` pinskia at gcc dot gnu.org
@ 2024-04-16  3:17 ` alx at kernel dot org
  2024-04-16 10:52 ` alx at kernel dot org
                   ` (9 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: alx at kernel dot org @ 2024-04-16  3:17 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #11 from Alejandro Colomar <alx at kernel dot org> ---
What are pedwarns?  :)

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (10 preceding siblings ...)
  2024-04-16  3:17 ` alx at kernel dot org
@ 2024-04-16 10:52 ` alx at kernel dot org
  2024-04-16 10:55 ` uecker at gcc dot gnu.org
                   ` (8 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: alx at kernel dot org @ 2024-04-16 10:52 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #12 from Alejandro Colomar <alx at kernel dot org> ---
Hi Martin,

On Tue, Apr 16, 2024 at 05:35:03AM +0000, uecker at gcc dot gnu.org wrote:
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97100
> 
> --- Comment #7 from uecker at gcc dot gnu.org ---
> 
> The fix suppresses certain warnings which are guarded by a flag, but it is not
> always clear whether a specific warning should be suppressed or not in dead
> code. 

_Generic(3)'s branches are by design using different types, to call
appropriate overload implementations.  These will necessarily result in
incompatible types somewhere.  If you're lucky and your overload
implementations are functions _and_ you call them all with the same
argument list, you can move the argument list out of the _Generic
expression, so as to avoid the warnings (because _Generic() will have
already been evaluated at the time the argument list is part of the
function call and the compiler has the information to diagnose.

If either you pass a different argument list (not my case) to each
overload, or your overloads must be implemented via macros (my case),
then you must put the argument list inside the _Generic() expression,
which will necessarily result in incompatible types (otherwise you
wouldn't be using _Generic() at all, probably).

> 
> You could also always add a cast.

I designed these macros precisely for type safety.  That is, to have the
compiler diagnose type mismatches.  For example, the call

        err = a2i(time_t, &t, "42", endp, base, min, max);

is a type-safe version of BSD's

        t = strtoi("42", endp, base, min, max);

Because nothing guarantees that intmax_t will be appropriate for parsing
a time_t.  My macro will call a wraper function that makes sure that we
call the right one, strtoi(3) or strtou(3), depending on the signedness
of time_t.  It goes even further and makes sure that &t is actually a
time_t, by wrapping strtoi(3) in a call that accepts a long*.

If I do cast, then I lose all that type safety, and need to increase
the complexity by tinkering with GNU extesions such as ({}) and
__builtin_types_compatible_p(), and I also need to insert a
_Static_assert(3).  All of this is error prone, and requires that the
program is not DRY, so I would really like to avoid it.

It's nice, though, that it's possible to workaround it with a cast +
GNU extensions + some extra complexity.  At least I have a fallback.
But I don't think I'm content with keeping it like that.  See the
program below to see what needs to be done to make it work safely.

You mentioned in the #97100 thread that a C compiler must diagnose all
constraint violations.  Is the following then a violation of the
standard?

        $ cat constraint.c 
        void f(int *);

        int main(void)
        {
                unsigned u;
                f(&u);
        }
        $ cc constraint.c -S
        $ 

I don't see the compiler should be allowed not emit a diagnostic there,
and it's not ok to not emit a diagnostic in a dead branch of _Generic(),
independently of me having asked the compiler to warn about
-Wincompatible-pointer-types.  Dead branches of _Generic() are not just
any dead code; it's dead code that we know will result in incompatible
types, by design.  A warning about them there is bogus.

Please present a case where not warning about incompatible types in a
dead _Generic() branch would be bad.

> Fundamentally, the program is that _Generic is not ideally designed for this
> use case. One could consider an extension

Why not?  What's the design use-case for _Generic() if it's not
overloading a macro for different types, and then call the appropriate
overload function or macro?  Even if you open-code the branches in the
_Generic() expressions, you're doing the same thing: writing code for
handling different types, which will almost necessarily result in
incompatible types in all branches but one.

Have a lovely day!
Alex

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (11 preceding siblings ...)
  2024-04-16 10:52 ` alx at kernel dot org
@ 2024-04-16 10:55 ` uecker at gcc dot gnu.org
  2024-04-16 11:01 ` alx at kernel dot org
                   ` (7 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: uecker at gcc dot gnu.org @ 2024-04-16 10:55 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

uecker at gcc dot gnu.org changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |uecker at gcc dot gnu.org

--- Comment #13 from uecker at gcc dot gnu.org ---
A couple of comments:

In principle, those warnings are not false positives. The design of _Generic
requires that the non-taken branches are valid in C, so some warnings are
required by the C standard.  We can tweak warnings (which we already did for
some), but I do not think it is possible to solve all issues in this wa.

There are different ways to work around this:

One can work around using explicit casts and/or temporary values so that the
code of all branches is always valid. 

It may be helpful to flatten multi-level _Generic to one level by matching an
artificial function pointer type:

#define foo(a, b) _Generic((void(*)(typeof(a), typeof(b)))0, void(*)(int, int):
...)


In my opinion, and extension such as 

_Generic(x, int i: ... i ...);

where i then has type int inside the branch would be useful.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (12 preceding siblings ...)
  2024-04-16 10:55 ` uecker at gcc dot gnu.org
@ 2024-04-16 11:01 ` alx at kernel dot org
  2024-04-16 11:07 ` alx at kernel dot org
                   ` (6 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: alx at kernel dot org @ 2024-04-16 11:01 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #14 from Alejandro Colomar <alx at kernel dot org> ---
Oops, I forgot to paste the version with casts.  Here it is.

It is too repetitive and error prone.


alx@debian:~/tmp/c$ cat g.c
#include <assert.h>
#include <bsd/inttypes.h>
#include <errno.h>
#include <time.h>


#define is_same_type(a, b)    __builtin_types_compatible_p(a, b)
#define is_same_typeof(a, b)  is_same_type(typeof(a), typeof(b))


#define a2i(TYPE, n, s, endp, base, min, max)                                 \
({                                                                            \
        _Static_assert(is_same_typeof(TYPE *, n), "");                        \
        _Generic((TYPE) 0,                                                    \
                long:               a2sl((long *) n, s, endp, base, min, max),
\
                int:                a2si( (int *) n, s, endp, base, min, max) 
\
        );                                                                    \
})


#define a2sl(n, s, endp, base, min, max)                                      \
(                                                                             \
        _Generic(endp,                                                        \
                const char **:  a2sl_c,                                       \
                char **:        a2sl_nc,                                      \
                void *:         a2sl_nc                                       \
        )(n, s, endp, base, min, max)                                         \
)

#define a2si(n, s, endp, base, min, max)                                      \
(                                                                             \
        _Generic(endp,                                                        \
                const char **:  a2si_c,                                       \
                char **:        a2si_nc,                                      \
                void *:         a2si_nc                                       \
        )(n, s, endp, base, min, max)                                         \
)


static inline int a2sl_c(long *restrict n, const char *s,
    const char **restrict endp, int base, long min, long max);
static inline int a2si_c(int *restrict n, const char *s,
    const char **restrict endp, int base, int min, int max);

static inline int a2sl_nc(long *restrict n, const char *s,
    char **restrict endp, int base, long min, long max);
static inline int a2si_nc(int *restrict n, const char *s,
    char **restrict endp, int base, int min, int max);


static inline int
a2sl_c(long *restrict n, const char *s,
    const char **restrict endp, int base, long min, long max)
{
        return a2sl_nc(n, s, (char **) endp, base, min, max);
}


static inline int
a2si_c(int *restrict n, const char *s,
    const char **restrict endp, int base, int min, int max)
{
        return a2si_nc(n, s, (char **) endp, base, min, max);
}


static inline int
a2sl_nc(long *restrict n, const char *s,
    char **restrict endp, int base, long min, long max)
{
        int  status;

        *n = strtoi(s, endp, base, min, max, &status);
        if (status != 0) {
                errno = status;
                return -1;
        }
        return 0;
}


static inline int
a2si_nc(int *restrict n, const char *s,
    char **restrict endp, int base, int min, int max)
{
        int  status;

        *n = strtoi(s, endp, base, min, max, &status);
        if (status != 0) {
                errno = status;
                return -1;
        }
        return 0;
}


int
main(void)
{
        time_t  t;

        a2i(time_t, &t, "42", NULL, 0, 0, 10);
}
alx@debian:~/tmp/c$ cc -Wall -Wextra g.c -S
alx@debian:~/tmp/c$

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (13 preceding siblings ...)
  2024-04-16 11:01 ` alx at kernel dot org
@ 2024-04-16 11:07 ` alx at kernel dot org
  2024-04-16 11:17 ` uecker at gcc dot gnu.org
                   ` (5 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: alx at kernel dot org @ 2024-04-16 11:07 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #15 from Alejandro Colomar <alx at kernel dot org> ---
(In reply to uecker from comment #13)
> A couple of comments:
> 
> In principle, those warnings are not false positives. The design of _Generic
> requires that the non-taken branches are valid in C, so some warnings are
> required by the C standard.  We can tweak warnings (which we already did for
> some), but I do not think it is possible to solve all issues in this wa.

I showed you an example program in comment #12 that is an evidence that either
there's no requirement by ISO C that this results in a diagnostic, or maybe
it's required by GCC is already violating the standard here.  I'm repeating it
here:

$ cat constraint.c
void f(int *);

int
main(void)
{
        unsigned u;
        f(&u);
}
$ cc constraint.c -S
$ 

> 
> There are different ways to work around this:
> 
> One can work around using explicit casts and/or temporary values so that the
> code of all branches is always valid. 

It's too error prone.

> It may be helpful to flatten multi-level _Generic to one level by matching
> an artificial function pointer type:
> 
> #define foo(a, b) _Generic((void(*)(typeof(a), typeof(b)))0, void(*)(int,
> int): ...)

The thing is that the intermediate macros are also an intended API, so I need
to either call it using two levels of macros, or reimplement it in the caller. 
This is also repetitive and error-prone, just like the casts.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (14 preceding siblings ...)
  2024-04-16 11:07 ` alx at kernel dot org
@ 2024-04-16 11:17 ` uecker at gcc dot gnu.org
  2024-04-16 11:26 ` alx at kernel dot org
                   ` (4 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: uecker at gcc dot gnu.org @ 2024-04-16 11:17 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #16 from uecker at gcc dot gnu.org ---

Your example program is indeed not conforming and violates a constraint because
you pass a pointer of type that can not be assigned to the type of the
argument. So a diagnostic is required and a C compiler could also reject it. 
GCC is generous regarding differences in signedness in this case in default
mode, but you would get a warning when mixing long* and int* for example.  You
need to pass "-pedantic" to get all warnings required by the C standard.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (15 preceding siblings ...)
  2024-04-16 11:17 ` uecker at gcc dot gnu.org
@ 2024-04-16 11:26 ` alx at kernel dot org
  2024-04-16 12:01 ` muecker at gwdg dot de
                   ` (3 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: alx at kernel dot org @ 2024-04-16 11:26 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #17 from Alejandro Colomar <alx at kernel dot org> ---
Neither -Wall nor -Wextra (nor -Wincompatible-pointer-types) imply -pedantic.

(Ahh, this is the pedwarn reference from Andrew.)

Which means the compiler could decide to turn off -Wincompatible-pointer-types
in dead _Generic() branches, as long as -pedantic is not specified.

Maybe split the warning into

-Wincompatible-pointer-types-generic
-Wincompatible-pointer-types

And make the -generic one not present in -Wall -Wextra, and only enabled by
-pedantic.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (16 preceding siblings ...)
  2024-04-16 11:26 ` alx at kernel dot org
@ 2024-04-16 12:01 ` muecker at gwdg dot de
  2024-04-16 12:05 ` muecker at gwdg dot de
                   ` (2 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: muecker at gwdg dot de @ 2024-04-16 12:01 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

Martin Uecker <muecker at gwdg dot de> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |muecker at gwdg dot de

--- Comment #18 from Martin Uecker <muecker at gwdg dot de> ---

In theory, we could do something like this, but note that this also has
undesirable consequences: This would turn off all such warnings also in
expressions passed as macro arguments, i.e. this would substantially reduce
type safety for expressions you pass to your 'a2i' macro and also affect other
sub expressions one might have inside the implementation of such macros. We
could try to be smart and find heuristics to avoid this, but then it already
gets more difficult. 

I personally find the version with the casts acceptable. I would suggest o
implement the type tests also using _Generic instead of using the builtin. For
example, you could do something like:

#define a2i(TYPE, ...) \
 _Generic((n),  \
 long: a2sl(_Generic((n), TYPE: (long*)(n), s, endp, base, min, max), \
 ...)

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (17 preceding siblings ...)
  2024-04-16 12:01 ` muecker at gwdg dot de
@ 2024-04-16 12:05 ` muecker at gwdg dot de
  2024-04-16 12:25 ` alx at kernel dot org
  2024-04-16 12:32 ` alx at kernel dot org
  20 siblings, 0 replies; 22+ messages in thread
From: muecker at gwdg dot de @ 2024-04-16 12:05 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #19 from Martin Uecker <muecker at gwdg dot de> ---
It would still work for other arguments that are used in the active branch, but
not arguments you may not use in active branch or other subexpressions not used
in the active branch.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (18 preceding siblings ...)
  2024-04-16 12:05 ` muecker at gwdg dot de
@ 2024-04-16 12:25 ` alx at kernel dot org
  2024-04-16 12:32 ` alx at kernel dot org
  20 siblings, 0 replies; 22+ messages in thread
From: alx at kernel dot org @ 2024-04-16 12:25 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #20 from Alejandro Colomar <alx at kernel dot org> ---
Hmmmm, I like the _Generic() to assert the type.  Thanks!

With that, I find it more acceptable.  At least I don't need to use GNU
extensions, and the cast is coupled with the verification of the type.  It's a
bit repetitive, but acceptable.

I agree that using heuristics to fix this would be bad.

Feel free to close as WONTFIX.

Have a lovely day!
Alex


Below is the program using _Generic() to assert the type.


alx@debian:~/tmp/c$ cat g.c
#include <assert.h>
#include <bsd/inttypes.h>
#include <errno.h>
#include <time.h>


#define a2i(TYPE, n, s, endp, base, min, max)                                 \
({                                                                            \
        _Generic((TYPE) 0,                                                    \
                long:               a2sl(_Generic(n, TYPE *: (long *) n), s,
endp, base, min, max), \
                int:                a2si(_Generic(n, TYPE *:  (int *) n), s,
endp, base, min, max)  \
        );                                                                    \
})


#define a2sl(n, s, endp, base, min, max)                                      \
(                                                                             \
        _Generic(endp,                                                        \
                const char **:  a2sl_c,                                       \
                char **:        a2sl_nc,                                      \
                void *:         a2sl_nc                                       \
        )(n, s, endp, base, min, max)                                         \
)

#define a2si(n, s, endp, base, min, max)                                      \
(                                                                             \
        _Generic(endp,                                                        \
                const char **:  a2si_c,                                       \
                char **:        a2si_nc,                                      \
                void *:         a2si_nc                                       \
        )(n, s, endp, base, min, max)                                         \
)


static inline int a2sl_c(long *restrict n, const char *s,
    const char **restrict endp, int base, long min, long max);
static inline int a2si_c(int *restrict n, const char *s,
    const char **restrict endp, int base, int min, int max);

static inline int a2sl_nc(long *restrict n, const char *s,
    char **restrict endp, int base, long min, long max);
static inline int a2si_nc(int *restrict n, const char *s,
    char **restrict endp, int base, int min, int max);


static inline int
a2sl_c(long *restrict n, const char *s,
    const char **restrict endp, int base, long min, long max)
{
        return a2sl_nc(n, s, (char **) endp, base, min, max);
}


static inline int
a2si_c(int *restrict n, const char *s,
    const char **restrict endp, int base, int min, int max)
{
        return a2si_nc(n, s, (char **) endp, base, min, max);
}


static inline int
a2sl_nc(long *restrict n, const char *s,
    char **restrict endp, int base, long min, long max)
{
        int  status;

        *n = strtoi(s, endp, base, min, max, &status);
        if (status != 0) {
                errno = status;
                return -1;
        }
        return 0;
}


static inline int
a2si_nc(int *restrict n, const char *s,
    char **restrict endp, int base, int min, int max)
{
        int  status;

        *n = strtoi(s, endp, base, min, max, &status);
        if (status != 0) {
                errno = status;
                return -1;
        }
        return 0;
}


int
main(void)
{
        time_t  t;

        a2i(time_t, &t, "42", NULL, 0, 0, 10);
}
alx@debian:~/tmp/c$ cc -Wall -Wextra g.c -S
alx@debian:~/tmp/c$

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [Bug c/114731] -Wincompatible-pointer-types false positive in combination with _Generic(3)
  2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
                   ` (19 preceding siblings ...)
  2024-04-16 12:25 ` alx at kernel dot org
@ 2024-04-16 12:32 ` alx at kernel dot org
  20 siblings, 0 replies; 22+ messages in thread
From: alx at kernel dot org @ 2024-04-16 12:32 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114731

--- Comment #21 from Alejandro Colomar <alx at kernel dot org> ---
It would be interesting to learn why MSVC gets it right.  Maybe there's a
deterministic way to avoid this warning.  Although maybe it's just that they're
doing heuristics.

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2024-04-16 12:32 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-16  2:37 [Bug c/114731] New: -Wincompatible-pointer-types false positive in combination with _Generic(3) alx at kernel dot org
2024-04-16  2:49 ` [Bug c/114731] " sjames at gcc dot gnu.org
2024-04-16  2:56 ` alx at kernel dot org
2024-04-16  2:58 ` alx at kernel dot org
2024-04-16  2:59 ` sjames at gcc dot gnu.org
2024-04-16  3:02 ` alx at kernel dot org
2024-04-16  3:03 ` pinskia at gcc dot gnu.org
2024-04-16  3:05 ` pinskia at gcc dot gnu.org
2024-04-16  3:10 ` pinskia at gcc dot gnu.org
2024-04-16  3:11 ` alx at kernel dot org
2024-04-16  3:15 ` pinskia at gcc dot gnu.org
2024-04-16  3:17 ` alx at kernel dot org
2024-04-16 10:52 ` alx at kernel dot org
2024-04-16 10:55 ` uecker at gcc dot gnu.org
2024-04-16 11:01 ` alx at kernel dot org
2024-04-16 11:07 ` alx at kernel dot org
2024-04-16 11:17 ` uecker at gcc dot gnu.org
2024-04-16 11:26 ` alx at kernel dot org
2024-04-16 12:01 ` muecker at gwdg dot de
2024-04-16 12:05 ` muecker at gwdg dot de
2024-04-16 12:25 ` alx at kernel dot org
2024-04-16 12:32 ` alx at kernel dot org

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).