c – Why does gcc give a warning saying that the constant is unsigned because it is too large, when it is of the type __int128 and is signed?

Consider the following code:

#include <stdio.h>

int main(void) {
    printf("%llu\n", 18446744073709551615);
    printf("%llu\n", 18446744073709551615ULL);
    return 0;
}

Upon compilation (gcc -std=c18), I get the following warnings:

test1.c: In function ‘main’:
test1.c:4:26: warning: integer constant is so large that it is unsigned
    4 |         printf("%llu\n", 18446744073709551615);
      |                          ^~~~~~~~~~~~~~~~~~~~
test1.c:4:20: warning: format ‘%llu’ expects argument of type ‘long long unsigned int’, but argument 2 has type ‘__int128’ [-Wformat=]
    4 |         printf("%llu\n", 18446744073709551615);
      |                 ~~~^     ~~~~~~~~~~~~~~~~~~~~
      |                    |     |
      |                    |     __int128
      |                    long long unsigned int

The C standard section 6.4.4.1.5 and 6.4.4.1.6 say:

The type of an integer constant is the first of the corresponding list in which its value can be
represented.

enter image description here

If an integer constant cannot be represented by any type in its list, it may have an extended integer
type, if the extended integer type can represent its value. If all of the types in the list for the constant
are signed, the extended integer type shall be signed. If all of the types in the list for the constant
are unsigned, the extended integer type shall be unsigned. If the list contains both signed and
unsigned types, the extended integer type may be signed or unsigned. If an integer constant cannot
be represented by any type in its list and has no extended integer type, then the integer constant has
no type.

From the above it is clear that as ULONG_MAX cannot fit in int, long int and long long int, the compiler will try the signed extended integer types; as ULONG_MAX does fit inside __int128, that then becomes the type of the integer constant, as can be seen from the second warning message.

This is all expected behavior, but the issue I am facing is that clearly __int128 is a signed type, as is expected from the C standard. But then why does the first warning message (“integer constant is so large that it is unsigned”) say that the constant is treated as unsigned? That makes no sense to me, as according to 6.4.4.1.6 only the signed extended integer types are checked, so how is the integer constant being treated as unsigned?


To clarify the question a bit, my issue is not with printf; the format warning is expected, I just left it there to show that the constant is of type __int128.

Consider the code:

#include <stdio.h>

int main(void) {
    __int128 a = 18446744073709551615;
    unsigned long long b = 18446744073709551615;
    return 0;
}

Compiling this gives the warning:

test2.c: In function ‘main’:
test2.c:4:22: warning: integer constant is so large that it is unsigned
    4 |         __int128 a = 18446744073709551615;
      |                      ^~~~~~~~~~~~~~~~~~~~
test2.c:5:32: warning: integer constant is so large that it is unsigned
    5 |         unsigned long long b = 18446744073709551615;
      |                                ^~~~~~~~~~~~~~~~~~~~

My issue is that since the constant is of type __int128, why does the compiler say it is unsigned? Obviously __int128 is a signed type.

Read more here: Source link