cgccc11

error: pointer/integer type mismatch in conditional expression


I have this code:

#include <stdio.h>
#include <wchar.h>

/* If either of the if-true or if-false expressions of ?: is void *, then the
 * of the result shall also be void * (plus const if either is const). */
#define TO_VOID_PTR(P)  (1 ? (P) : (void *)(P))

#define PTR_FMT(P)        \
    _Generic(TO_VOID_PTR(P), \
        void const *: "%p",  \
        void *      : "%p"   \
    )

/* Get the corresponding format specifier of a type. */
#define PRINTF_FMT(T)                                \
    _Generic((T),                                    \
                _Bool                   : "%d",      \
                char                    : "%c",      \
                signed char             : "%hhd",    \
                unsigned char           : "%hhu",    \
                short int               : "%hd",     \
                int                     : "%d",      \
                long int                : "%ld",     \
                long long int           : "%lld",    \
                unsigned short int      : "%hu",     \
                unsigned int            : "%u",      \
                unsigned long int       : "%lu",     \
                unsigned long long int  : "%llu",    \
                float                   : "%f",      \
                double                  : "%f",      \
                long double             : "%Lf",     \
                char *                  : "%s",      \
                wchar_t *               : "%ls",     \
                default                 : PTR_FMT(T) \
    )


#define print(x)    printf(PRINTF_FMT(x), x)

/* A separate call to putchar() is required because _Generic is not a macro, but
 * a primary expression. As such it is evaluated at a later translation phase (7)
 * than string concatenation (phase 6). */
#define println(x)  printf(PRINTF_FMT(x), x), putchar('\n')

int main(void)
{
    println("hello");
    println(10);
    println(5u);
    println('A');   
}

Compiling with GCC 14.1 with the following flags -std=c23 -O3 -Werror -Wall -Wextra resulted in these error messages:

<source>:48:5: error: pointer/integer type mismatch in conditional expression ('int' and 'void *') [-Werror,-Wconditional-type-mismatch]
   48 |     println(10);
      |     ^~~~~~~~~~~
<source>:43:28: note: expanded from macro 'println'
   43 | #define println(x)  printf(PRINTF_FMT(x), x), putchar('\n')
      |                            ^~~~~~~~~~~~~
<source>:34:43: note: expanded from macro 'PRINTF_FMT'
   34 |                 default                 : PTR_FMT(T) \
      |                                           ^~~~~~~~~~
<source>:9:14: note: expanded from macro 'PTR_FMT'
    9 |     _Generic(TO_VOID_PTR(P), \
      |              ^~~~~~~~~~~~~~
<source>:6:28: note: expanded from macro 'TO_VOID_PTR'
    6 | #define TO_VOID_PTR(P)  (1 ? (P) : (void *)(P))
      |                            ^ ~~~   ~~~~~~~~~~~
<source>:49:5: error: pointer/integer type mismatch in conditional expression ('unsigned int' and 'void *') [-Werror,-Wconditional-type-mismatch]
   49 |     println(5u);
      |     ^~~~~~~~~~~
<source>:43:28: note: expanded from macro 'println'
   43 | #define println(x)  printf(PRINTF_FMT(x), x), putchar('\n')
      |                            ^~~~~~~~~~~~~
<source>:34:43: note: expanded from macro 'PRINTF_FMT'
   34 |                 default                 : PTR_FMT(T) \
      |                                           ^~~~~~~~~~
<source>:9:14: note: expanded from macro 'PTR_FMT'
    9 |     _Generic(TO_VOID_PTR(P), \
      |              ^~~~~~~~~~~~~~
<source>:6:28: note: expanded from macro 'TO_VOID_PTR'
    6 | #define TO_VOID_PTR(P)  (1 ? (P) : (void *)(P))
      |                            ^ ~~~   ~~~~~~~~~~~
<source>:50:5: error: pointer/integer type mismatch in conditional expression ('int' and 'void *') [-Werror,-Wconditional-type-mismatch]
   50 |     println('A');   
      |     ^~~~~~~~~~~~
<source>:43:28: note: expanded from macro 'println'
   43 | #define println(x)  printf(PRINTF_FMT(x), x), putchar('\n')
      |                            ^~~~~~~~~~~~~
<source>:34:43: note: expanded from macro 'PRINTF_FMT'
   34 |                 default                 : PTR_FMT(T) \
      |                                           ^~~~~~~~~~
<source>:9:14: note: expanded from macro 'PTR_FMT'
    9 |     _Generic(TO_VOID_PTR(P), \
      |              ^~~~~~~~~~~~~~
<source>:6:28: note: expanded from macro 'TO_VOID_PTR'
    6 | #define TO_VOID_PTR(P)  (1 ? (P) : (void *)(P))
      |                            ^ ~~~   ~~~~~~~~~~~

Why is _Generic choosing the default case for 10, 5u, and 'A' when separate cases for int and unsigned int are present?


Solution

  • The _Generic expression isn't choosing the default case. The issue is that all cases must be a valid expression.

    This means that the conditional:

    (1 ? (P) : (void *)(P))
    

    Which for the second case is being evaluated as:

    (1 ? (10) : (void *)(10))
    

    Results in a conversion between an integer type and a pointer type between the second and third parts of the conditional, which is a constraint violation as per section 6.5.15p3 of the C standard regarding the conditional operator.

    You can accomplish what you want by getting rid of the conditional and just casting to const void *, since you don't intent to change what the result points to.

    #define TO_VOID_PTR(P)  ((const void *)(P))