Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does a C shift expression have unsigned type? Why would Splint warn about a right-shift?

For the following program:

int main(void)
{
    int value = 2;
    int result = value >> 1U;
    return result;
}

...Splint 3.1.2 gives the warning:

splint_test.c: (in function main)
splint_test.c:4:18: Variable result initialized to type unsigned int, expects
                       int: value >> 1U
  To ignore signs in type comparisons use +ignoresigns

Splint seems to be claiming that an expression where a signed integer is shifted right has the type of an unsigned integer. However, all I can find in the ANSI C90 standard is:

The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 divided by the quantity, 2 raised to the power E2.

The primary target for this code is an embedded system with a mostly-C90 compiler. However, I'm interested in writing standards-compliant code. I have been testing on GCC and Clang in C99 mode so that restrict works.

My questions are:

  1. Does the C standard make any claims about the type of the result of a bit-shift?
  2. Do compilers?
  3. If not, why might Splint be issuing this warning?
like image 794
detly Avatar asked Apr 14 '19 07:04

detly


People also ask

What is unsigned shift right?

The unsigned right shift operator ( >>> ) (zero-fill right shift) evaluates the left-hand operand as an unsigned number, and shifts the binary representation of that number by the number of bits, modulo 32, specified by the right-hand operand.

What is right shift?

The right shift operator ( >> ) returns the signed number represented by the result of performing a sign-extending shift of the binary representation of the first operand (evaluated as a two's complement bit string) to the right by the number of bits, modulo 32, specified in the second operand.

What is right shift and left shift?

The bitwise shift operators move the bit values of a binary object. The left operand specifies the value to be shifted. The right operand specifies the number of positions that the bits in the value are to be shifted.

What does the operator do right shift operator left shift operator zero Fill Left Shift zero fill right shift?

Bitwise Zero Fill Right Shift Operator (>>>) Bitwise Zero Fill Right Shift Operator shifts the bits of the number towards the right a specified n number of positions. The sign bit filled with 0's. The symbol >>> represents the Bitwise Zero Fill Right Shift Operator.

What is an unsigned right shift operator?

Unsigned right-shift operator The unsigned right-shift operator is a special type of right-shift operator that doesn't use the sign bit for filling the trailing position. The unsigned right-shift operator always fills the trialing position by 0.

What does right shift do in C++?

Right Shifts. The right-shift operator causes the bit pattern in shift-expression to be shifted to the right by the number of positions specified by additive-expression. For unsigned numbers, the bit positions that have been vacated by the shift operation are zero-filled.

What is left shift operator in C++?

So, The Left-shift operator is another type of operator which is used to move the bits of the shift-expression to the left. The left-shift operator shifts the bits pattern to the left according to the number of positions specified the additive-expression.

Does Java support the unsigned left-shift operator?

Java doesn't support the unsigned left-shift operator. The Right-shift operator is a special type of operator which is used to move the bits of the shift-expression to the right. The right-shift operator shifts the bits pattern to the right according to the number of positions specified the additive-expression.


2 Answers

It's a bug in Splint. Splint wrongly assumes that the type of e1 << e2 is ctype_wider(te1, te2). The correct type would be just te1.

The buggy code starts here by using the same code path for the bitwise operators like &, | and ^, as well as for the << and >> operators.

The actual bug is at the end of that code, which assumes that for all these bitwise binary operators, the return type is ctype_wider(te1, te2).

I have opened a bug on Splint's GitHub issue tracker, referencing this question.


Update, February 2021:

NetBSD's lint says that in traditional C, the bitwise shift operators applied the usual arithmetic conversions to their operands:

    /* Make sure both operands are of the same type */
    if (mp->m_balance_operands || (tflag && (op == SHL || op == SHR)))
        balance(op, &ln, &rn);

Explanation of the code:

  • tflag is the flag for checking traditional C programs, as opposed to C90 or C99
  • mp->m_balance_operands is false for the bitwise shift operators, defined in this table, the b column in the middle
  • balance performs the usual arithmetic conversions

This was a change in C90. The Splint code may thus have been correct for traditional C, maybe it just had not been updated for C90 or C99.

like image 78
Roland Illig Avatar answered Oct 07 '22 17:10

Roland Illig


No. The standard says the type of a bitshift is the type of the left operand, promoted: 6.5.7p3

... The type of the result is that of the promoted left operand. ...

Your tool must be confused, inferring the type with usual arithmetic conversion, which applies to most binary operators but not << and >>.

You can also verify the type is int by inserting a _Generic-based type assert and observing that compilers accept it:

int main(void)
{
    int value = 2;
    int result = _Generic(value >> 1U, int: value>>1U); //compiles, the type is int
    return result;
}
like image 37
PSkocik Avatar answered Oct 07 '22 18:10

PSkocik