MarkWeston
MarkWeston

Reputation: 750

gcc 7.2: warning: left shift count >= width of type

Even though I cast every right operand to unsigned long long, the warning persists. Shouldn't uint8_t << uint64_t have an implicit cast like this: (uint64_t) uint8_t << uint64_t?

This answer suggests that I can promote either of the operands and the whole expression will cast to unsigned long long, but it might be wrong.

bool dgBioReadU64LE(DgBioFile *file, uint64_t *x) {
    uint8_t u[8];
    if (!dgBioReadU8v(file, LEN(u), u)) return false;
    *x = u[0]|(u[1]<<8ULL)|(u[2]<<16ULL)|(u[3]<<24ULL)|(u[4]<<32ULL)|(u[5]<<40ULL)|(u[6]<<48ULL)|(u[7]<<56ULL);
    return true;
}

bool dgBioReadU64BE(DgBioFile *file, uint64_t *x) {
    uint8_t u[8];
    if (!dgBioReadU8v(file, LEN(u), u)) return false;
    *x = u[7]|(u[6]<<8ULL)|(u[5]<<16ULL)|(u[4]<<24ULL)|(u[3]<<32ULL)|(u[2]<<40ULL)|(u[1]<<48ULL)|(u[0]<<56ULL);
    return true;
}

Upvotes: 0

Views: 1856

Answers (5)

Oliver Charlesworth
Oliver Charlesworth

Reputation: 272557

Shouldn't uint8_t << uint64_t have an implicit cast like this: (uint64_t) uint8_t << uint64_t?

TL;DR - No, shift operators are special.

Full answer

The behaviour you're describing (essentially, matching operand types) is known as the usual arithmetic conversions in the C standard.1

We see that the standard mandates this for many operators (e.g. for additive operators):

[6.5.6] If both operands have arithmetic type, the usual arithmetic conversions are performed on them.

However, we see no such phrase in the equivalent section for bitwise shift operators. The closest we have is:

[6.5.7] The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand.

However, the integer promotions are a different thing - they say (essentially) that any type smaller than [unsigned] int is converted to an [unsigned] int.

So the compiler is correct to warn here. (And as I'm sure you can guess, the solution is to perform an explicit cast on the left-hand operand ;)


1. For the purposes of this answer, I'm considering C11 (specifically N1570) canonical. The behaviour is the same at least as far back as C99.

Upvotes: 5

John Bollinger
John Bollinger

Reputation: 180418

Even though I cast every right operand to unsigned long long, the warning persists. Shouldn't uint8_t << uint64_t have an implicit cast like this: (uint64_t) uint8_t << uint64_t?

No.

This answer suggests that I can promote either of the operands and the whole expression will cast to unsigned long long, but it might be wrong.

I see why you think the linked answer says that, but it demonstrates using a constant of type unsigned long long as the left operand. That part is correct. To the extent that the answer suggests that the same could have been achieved by using a wider type on the right, it is misleading.

The standard specifies:

The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

(C2011, 6.5.7/3)

Note well that the type of the result expression is determined by the type of the left operand only, and that it is the value of the right operand relative to the (promoted) type of the left operand that can trigger this particular undefinedness provision.

You are left-shifting a uint8_t. The integer promotions will be performed on the left operand, resulting in a value of type int. Supposing that your ints are 32 bits wide, the left shifts by 32 or more bits produce undefined behavior, and in some cases you might get additional undefined behavior arising from the fact that it is signed, not unsigned int.

Correct this by casting the left operand. For example, (uint64_t) u[7] << 56.

Upvotes: 1

AnT stands with Russia
AnT stands with Russia

Reputation: 320551

The answer you linked is misleading. It seems to suggest that you can widen either operand and it will trigger automatic conversion of the other operand to the common (wider) type. That is true for most binary operators in C. However, that is not true for shift operators.

The shift operators in C are actually special in that regard. They behave asymmetrically. Changing the type of the right operand to a wider type does not trigger the conversion of the left operand to the type of the right operand, and does not affect the resultant type. The resultant type is always defined by the (possibly promoted) type of the left operand.

In your case you specifically have to convert the left operand to unsigned long long type.

Upvotes: 2

dbush
dbush

Reputation: 224112

The result of the left and right shift operators has the same type as the left operand. So the type of the right operand won’t affect the type of the result.

You need to cast the left operand to unsigned long long to get the desired result.

Upvotes: 1

Khoyo
Khoyo

Reputation: 1247

This answer suggests that I can promote either of the operands and the whole expression will cast to unsigned long long, but it might be wrong.

It is.

The standard says:

6.5.7 Bitwise shift operators
The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined

Which means that you need to cast the left operand, casting the right one doesn't change anything here.

Upvotes: 4

Related Questions