Search code examples
cunsignediarinteger-promotionmisra

MISRA C:2004, error with bit shifting


I'm using IAR Workbench compiler with MISRA C:2004 checking on.
The fragment is:

#define UNS_32 unsigned int
UNS_32 arg = 3U;
UNS_32 converted_arg = (UNS_32) arg;
/* Error line --> */ UNS_32 irq_source = (UNS_32)(1U << converted_arg);

The MISRA error is: Error[Pm136]: illegal explicit conversion from underlying MISRA type "unsigned char" to "unsigned int" (MISRA C 2004 rule 10.3)

I don't see any unsigned char in any of the code above.

The discussion at Why did Misra throw an error here? discusses multiplication which may have different promoting rules than left shifting.

My understanding is that the compiler should promote an expression to the larger sized data type, not demote to a smaller size.

What is really going on here?

How do I make the code MISRA C:2004 compliant?

Edit 1:

Changing the error line to:

UNS_32 irq_source = (UNS_32)((UNS_32) 1U << converted_arg);  

does not make the error go away.


Solution

  • Second question first:

    How do I make the code MISRA C:2004 compliant?

    You can write this in a MISRA-compliant manner as follows:

    typedef unsigned int UNS_32;
    
    UNS_32 test(void);
    UNS_32 test(void)
    {
      UNS_32 original_arg = 3U;
      UNS_32 irq_source = 1UL << original_arg;
    
      return irq_source;
    }
    

    Back to the first question:

    What is really going on here?

    First Rule 10.3 says that complex integer expressions shall not be cast to a type wider than the underlying type.

    One key to understanding the error message is the concept underlying type, which is a MISRA-C specific concept. In short, the underlying type of a constant is the smallest type that it can fit into. In this case 1U has the underlying type unsigned char despite it having the language type unsigned int.

    The rationale behind the 10.3 rule is to avoid cases where the result of an operation is used in a context which is larger than the parts. The standard example of this is multiplication, where alpha and beta are 16 bit types:

    uint32_t res = alpha * beta;
    

    Here, if int is 16 bits, the multiplication will be performed in 16 bits, the result will then be converted to 32 bits. On the other hand, if int is 32 bits or larger, the multiplication will be performed in the larger precision. Concretely, this will make the result different when multiplying, say, 0x4000 and 0x10.

    MISRA rule 10.3 solved this by enforcing that the cast result is placed in a temporary, which is later cast to the larger type. That way you are forced to write code one way or the other.

    If the intention is to use a 16-bit multiplication:

    uint16_t tmp = alpha * beta;
    uint32_t res = tmp;
    

    On the other hand, if the intention is a 32-bit multiplication:

    UNS_32 res = (UNS_32)alpha * (UNS_32)beta;
    

    So, in this case, the expression 1U << count is the potential problem. If converted_arg is larger than 16 bits, this could lead to a problem when using 16-bit ints. MISRA does allow you, however, to write 1UL << count or (UNS_32)((UNS_32)1U << original_arg). You mentioned that the MISRA checker issued an error in the latter case -- mine does not so please check again.

    So, the way I see it, the MISRA C checker you used correctly identified a violation to rule 10.3.