Let platform 1 have the width of int 4 bytes and the width of long 8 bytes.
Let platform 2 have the width of int 4 bytes and the width of long be the same as the width of int.
Then given:
unsigned int x = 2;
long signed int y = 3;
func(x * y);
When ran on platform 1, the effective type of the first argument of func is long signed int. This is as expected. It follows § 6.3.1.1.1.4 that the unsigned int type has the same rank as signed int. It also then follows that signed int type has a lower rank than long signed int according to § 6.3.1.1.1.3. This then triggers the result of the multiplication to be converted to long signed int type, following § 6.3.1.8.1.4.4. Great!
When ran on platform 2, the result of the multiplication is long unsigned int. Why?
Background
Section § 6.3.1.1, subsection 1, point 3 in the C99 standard says:
The rank for
long long intshall be greater than the rank oflong int, which shall be greater than the rank ofint, which shall be greater that the rank ofshort int, which shall be greater than the rank ofsigned char.
One thing this suggests is that long int has a higher rank than int.
Also, point 4 in the same paragraph in the C99 standard says:
The rank of any
unsignedinteger type shall equal the rank of the correspondingsignedinteger type, if any.
A couple of things from here are that the unsigned int type has the same rank as the signed int type. Similarly, the long unsigned int type has the same rank as long signed int.
Lastly, in section § 6.3.1.8, subsection 1, point 4.3 says:
Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
And point 4.4:
Otherwise, if the type of the operand with signed integer type can represent all values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.
Test code
#include <stdio.h>
#define func(x) _Generic((x), long unsigned int: func_longunsignedint, long signed int: func_longsignedint, signed int: func_signedint, unsigned int: func_unsignedint)(x);
void func_longunsignedint (long unsigned int x)
{
printf("%s\t%lu\n", __func__, x);
}
void func_longsignedint (long signed int x)
{
printf("%s\t%ld\n", __func__, x);
}
void func_signedint (signed int x)
{
printf("%s\t%d\n", __func__, x);
}
void func_unsignedint (unsigned int x)
{
printf("%s\t%u\n", __func__, x);
}
int main(void)
{
printf("int width %d\n", sizeof(int));
printf("long width %d\n", sizeof(long));
unsigned int x = 2;
long signed int y = -3;
func(x * y);
}
For platform 1, compile with gcc -m64 to hopefully force long to the 8 bytes and int be 4 bytes.
For platform 2, compile with gcc -m32 to hopefully force long to the 4 bytes and int be 4 bytes.