2

In a huge macro I have in a program aimed for a 16-bit processor, the following code (simplified) appears several times:

typedef unsigned short int  uint16_t;
uint16_t var;
var = ~0xFFFF;

MISRA complains with the warning 12.4: integer conversion resulted in truncation. The tool used to get this is Coverity.

I have checked the forum but I really need a solution (instead of changing the negation by the actual value) as this line is inside a macro with varying parameters.

I have tried many things and here is the final attempt which fails also:

var = (uint16_t)((~(uint16_t)(0xFFFFu))&(uint16_t)0xFFFFu);

(the value 0xFFFF is just an example. In the actual code, the value is a variable which can take whatever value (but 16 bits))

Do you have any other idea please? Thanks.

EDIT:

I have tried then to use 32bits value and the result is the same with the following code:

typedef unsigned int uint32_t; 
uint32_t var; 
var = (uint32_t)(~(uint32_t)(0xFFFF0000u));
Ben9000RPM
  • 101
  • 1
  • 8

2 Answers2

2

Summary:

Assuming you are using a static analyser for MISRA-C:2012, you should have gotten warnings for violations against rule 10.3 and 7.2.

Rule 12.4 is only concerned with wrap-around of unsigned integer constants, which can only occur with the binary + and - operators. It seems irrelevant here.


The warning text doesn't seem to make sense for neither MISRA-C:2004 12.4 nor MISRA-C:2012 12.4. Possibly, the tool is displaying the wrong warning.

There is however a MISRA:2012 rule 10.3 that forbids to assign a value to a variable that is of a smaller type than intended in the expression.

To use MISRA terms, the essential type of ~0xFFFF is unsigned, because the hex literal is of type unsigned int. On your system, unsigned int is apparently larger than uint16_t (int is a "greater ranked" integer type than short in the standard 6.3.1.1, even if they are of the same size). That is, uint16_t is of a narrower essential type than unsigned int, so your code does not conform to rule 10.3. This is what your tool should have reported.

The actual technical issue, which is hidden behind the MISRA terms, is that the ~ operator is dangerous because it comes with an implicit integer promotion. Which in turn causes code like for example

uint8_t x=0xFF; 
~x << n; // BAD, always a bug

to invoke undefined behavior when the value 0xFFFFFF00 is left shifted.

It is therefore always good practice to cast the result of the ~ operator to the correct, intended type. There was even an explicit rule about this in MISRA 2004, which has now merged into the "essential type" rules.

In addition, MISRA (7.2) states that all integer constants should have an u or U suffix.

MISRA-C:2012 compliant code would look like this:

uint16_t var;
var = (uint16_t)~0xFFFFu;

or overly pedantic:

var = (uint16_t)~(uint16_t)0xFFFFu;
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Thanks for this development. Unfortunately, var = (uint16_t)~(uint16_t)0xFFFFu; still raises the warning. Furthermore, I do not get any other warning than 12.4. – Ben9000RPM Nov 02 '16 at 12:41
  • I have tried then to use 32bits value and the result is the same with the following code: typedef unsigned int uint32_t; uint32_t var; var = (uint32_t)(~(uint32_t)(0xFFFF0000u)); – Ben9000RPM Nov 02 '16 at 12:43
  • 1
    @Ben9000RPM Seems like a tool bug. Which is no surprise, 95% of all MISRA checkers on the market are broken beyond repair. Don't obfuscate your code, simply write `var = (uint16_t)~0xFFFFu;` which is perfectly safe, and ignore the tool. – Lundin Nov 02 '16 at 12:44
0

When the compiler looks at the right side, first it sees the literal 0xFFFF. It is automatically promoted to an integer which is (obvious from the warning) 32-bit in your system. Now we can imagine that value as 0x0000FFFF (whole 32-bit). When the compiler does the ~ operation on it, it becomes 0xFFFF0000 (whole 32-bit). When you write var = ~0xFFFF; the compiler in fact sees var = 0xFFFF0000; just before the assign operation. And of course a truncation happens during this assignment...

Malkocoglu
  • 2,522
  • 2
  • 26
  • 32
  • 1
    My system is 16 bits. But maybe Coverity does not see it and extends it to 32 bits, I do not know. – Ben9000RPM Nov 02 '16 at 12:18
  • 1
    @Ben9000RPM: Your first comment made sense but the second suggests something wrong (maybe configuration problem) with the analyser... – Malkocoglu Nov 02 '16 at 15:05
  • @Ben9000RPM Out of curiosity, what does the --type_sizes switch say from your build log? Coverity auto-detects type sizes. Usually ints are 32-bit, but that could be different. If ints are 16-bit as well, it'd explain why your 32-bit example also complains - and would suggest a bug in the MISRA checker where it's assuming integers are 32-bits. – Caleb Nov 02 '16 at 17:00
  • @Caleb, after some investigations, Coverity takes into account that integer is 16 bit. A question has been raised to them. I'll keep you informed. – Ben9000RPM Nov 03 '16 at 07:59