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);
| ^~~