18

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?
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
detly
  • 29,332
  • 18
  • 93
  • 152
  • 4
    Have you looked in the Splint source code whether it treats `>>` the same as the other binary operators? That would be a bug in Splint, and since the current version seems to be still the same as in 2007, I don't think much has happened since then. – Roland Illig Apr 14 '19 at 07:41
  • 3
    https://github.com/ravenexp/splint/blob/d8b2a2466a35bd225794e598df9c665ea6727dae/src/exprNode.c#L5689; the actual bug is in line 5775, which takes the wider type as the result instead of just taking the type of the left operand. – Roland Illig Apr 14 '19 at 07:52
  • 2
    clang is a much better option than splint for static analysis these days, especially since it's been over a decade since splint was last updated. – Shawn Apr 14 '19 at 08:27
  • @Shawn Clang doesn't do strict typedef checking. Nothing except Splint does (that I know of). – detly Apr 14 '19 at 12:00
  • @Shawn Although thanks for reminding me about scan-build, I will now add it to my CI config! – detly Apr 14 '19 at 12:08

3 Answers3

18

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:

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.

Roland Illig
  • 40,703
  • 10
  • 88
  • 121
17

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;
}
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
2

The C99 through C17 standards say:

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

Since value is an int it requires no promotion and the type of the "promoted left operand" is int, and the type of the result of << is the same.

C89/C90 says the same except with the word "integer" replaced by "integral".

hobbs
  • 223,387
  • 19
  • 210
  • 288