8

I get the following warning from our analysis tool Composite expression assigned to a wider essential type This is the code:

uint32_t result;
uint8_t resolution;

result = 1U << resolution;

I tried the following:

#define SHIFT_BY_ONE (uint8_t)1
result  = SHIFT_BY_ONE << resolution;

but that then throws this warning Shift left of signed quantity (int) so I guess I am not understanding the issue correctly. How do I fix this error?

Lundin
  • 195,001
  • 40
  • 254
  • 396
homeGrown
  • 375
  • 1
  • 8
  • 25
  • have you tried `result = (uint32_t)(1U << resolution);` – Jean-François Fabre Oct 24 '17 at 11:04
  • @Jean-FrançoisFabre Yes and that resulted in an different issue `Impermissible cast of composite expression (wider essential type for the destination)` – homeGrown Oct 24 '17 at 11:05
  • what about `result = 1UL << resolution;` ? what is your platform? have you printed `sizeof(1U)` ? – Jean-François Fabre Oct 24 '17 at 11:06
  • 1
    Since `1U` is `unsigned int`, which may not be the same as `uint32_t` on some platforms, try `result = ((uint32_t)1) << resolution;` – Sergey Kalinichenko Oct 24 '17 at 11:07
  • @Jean-FrançoisFabre Yes that's the one that worked great thanks – homeGrown Oct 24 '17 at 11:08
  • cool, I turned that to an answer. – Jean-François Fabre Oct 24 '17 at 11:09
  • @dasblinkenlight That also works thanks. So is the thought process behind the rule that you must cast the operation to be the same type as the variable result? – homeGrown Oct 24 '17 at 11:10
  • @homeGrown You need to cast the operand to ensure that operation result is of the correct type. You need to be especially careful with `uintN_t`s because there are no numeric suffixes that work for them. It turns out C supplies convenient macros for this purpose. – Sergey Kalinichenko Oct 24 '17 at 11:12
  • I take it you configured Lint to check for MISRA-C:2012 compliance? If so, you should tag your question [tag:MISRA]. The warning sounds a whole lot like a MISRA violation. – Lundin Oct 24 '17 at 14:53
  • At the heart of this problem is a lack of understanding (or a misunderstanding) of the behaviour of the C shift operator... this is not unreasonable, as the C standard is illogical - which is the basis of the MISRA C rule: Left shifting a **uint8_t** results in **signed int** – Andrew Oct 30 '17 at 07:41

4 Answers4

11

This sounds as if you are running a MISRA-C:2012 checker. Essential type and composite expression are terms from MISRA-C, not from the C standard.

In order to understand anything at all from this warning, you have to study the meaning of essential type (MISRA-C:2012 8.10) and composite expression (MISRA-C:2012 8.10.3).

As for the reason for the warning, it is rule 10.6:

The value of a composite expression shall not be assigned to an object with wider essential type.

If we ignore the meaning of all these terms, what the rule boils down to is that if you have an operation with 2 operands of a smaller type, the result should not get assigned to a variable of larger type than those operands.

This is to prevent code like this:

uint16_t a = 40000;
uint16_t b = 40000;
uint32_t result = a + b;

On a 16 bit system, the operands a and b will not get promoted, so the actual operation will get carried out on 16 bit type - and there will be an unsigned wrap-around (or overflow in case of signed variables).

Confused programmers that don't understand how implicit type promotions work in C might think that the above operation gets carried out on uint32_t just because the result is stored in such a type. But this is not correct, the left-hand side of the assignment operator has nothing to do with the sub-expression a + b what-so-ever. Which type that gets used in sub-expressions is entirely determined by operator precedence, and = has lower precedence than +.

Apparently MISRA-C believes that such misunderstandings are common, which is the rationale for this rule.

As for how to fix it, it is easy:

result = (uint32_t)1U << resolution;
Lundin
  • 195,001
  • 40
  • 254
  • 396
2

This happens because 1U does not match uint32_t type. Use UINT32_C(...) macro to ensure type compatibility:

result = UINT32_C(1) << resolution;
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • The UINT32_C macro is actually problematic to use with MISRA-C - which this is. Because MISRA-C does not regard `UINT32_C(1)` as an integer constant, it only regards `1` as one and it demands that all integer constants must have a `1U` suffix. But if you would write `UINT32_C(1U)`, the compiler will go bananas. And to be picky `UINT32_C` actually gives a type of `uint_least32_t` and not `uint32_t`. A better solution would be to use `(uint32_t)1U`. – Lundin Oct 24 '17 at 15:25
1

On your system 1U is probably 16-bit unsigned (which explains the "wider type" when trying to assign to 32-bit unsigned).

In that case, I would use the long suffix for the literal:

result = 1UL << resolution;

(some comments suggest ((uint32_t)1U) << resolution which would be the most portable way after all)

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
0

1U may be narrower (16-bit) than uint32_t and therefore "Composite expression assigned to a wider essential type". A shift of a 16-bit unsigned by 20 does not make 0x100000,

uint32_t result;
uint8_t resolution;
result = 1U << resolution; // Potential 16-bit assignment to a 32-bit type.

The narrow-ness of resolution is not an issue here.


How do I fix this error?

I prefer to avoid casting when able and offer 2 alternatives. Both effectively first make a 1 of the destination type without casting. The compiler will certainly emit optimized code.

result = 1u;
result <<= resolution;
// or 
result = (result*0u + 1u) << resolution;
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • The error is a MISRA error though, so non-compliant code won't solve anything. The compound assignment is ok for MISRA but not the second alternative. Plus, you need to use `U` suffix on all integer constants, regardless of how wide `int` happens to be. – Lundin Oct 25 '17 at 08:36
  • @Lundin Concerning appending `U`, would `uint16_t a = 40000;` and/or `result = 1;` generate a MISRA warning/error? – chux - Reinstate Monica Oct 25 '17 at 14:43
  • Yes it would, MISRA is rather rigid when it comes to integer constants. – Lundin Oct 26 '17 at 06:36