Resolving a conversion warning in a compound assignment - c

In my code I have a lot of variable <<= 1; sentences where variable is of type uint16_t. The compiler is spitting a warning saying
conversion to 'uint16_t' from 'int' may alter its value [-Wconversion]
How can I resolve it? I could use a long-form notation like variable = (uint16_t)(variable << 1) - but I'd like to keep the short notation.

Based upon my reading of the standard, you can not get away from this warning for this reason:
uint16_t foo;
foo <<= 1;
is equivalent to
uint16_t foo;
foo = foo << 1;
However, this is caught up in the world of "integer promotion".
The value of the "foo << 1" expression has the type of "foo", however before the left-shift can be conducted it first must go through "integer promotion;" section 6.3.1.1.2 of the C99 standard specifies: "if an int can represent all values of the original type, the value is converted to an int."
This makes the non-implicit version of your code (with extra parentheses) as follows:
uint16_t foo;
foo = ((int)foo) << 1;
Given the warning you are on a system with either 32 or 64 bit ints (or anything bigger than 16, really), you are indeed shoving a bigger value into a smaller one.
One way around this is to be explicit with your casts like so:
uint16_t foo;
foo = (uint16_t)(foo << 1);
But that means that no, you can not use the shorter bitwise shift assignment operator.
If you really are doing this a bunch, consider making a helper function that makes your code clear and compiles cleanly.
void LS(uint16_t &value, int shift) { // LS means LeftShift
*value = (uint16_t)(*value << shift);
}
LS(&foo, 1);
TL;DR: No, you can't use the short operator and avoid that warning at the same time.

You get the warning because variable <<= 1; is equivalent to:
variable = variable << 1;
where the right-hand side has type int, not uint16_t, due to default integer promotions. The best way around this is not to use smaller-than-int types.

Related

MISRA C: Type casting and <<, & bit-wise operators error

I've been having a lot of trouble with MISRA when doing << and & operations.
Definitions of types for context
typedef uint32_t uint32;
typedef volatile uint32* canreg_txrx_pointer;
canreg_txrx_pointer canreg_txrx;
uint8_t valueRead, value;
#define CAN_DIR_BIT_POS ((uint8_t)2U)
#define CAN_OUT_BIT_POS ((uint8_t)1U)
#define CAN_IN_BIT_POS ((uint8_t)0U)
When trying to perform a series of << and & operations on the registers, I continually get the error #1397-D (MISRA-C:2004 10.5/R) If the bitwise operators ~ and << are applied to an operand of underlying type unsigned char or unsigned short, the result shall be immediately cast to the underlying type of the operand
An example that doesn't pass the misra check and then an example that does is given below
*canreg_txrx = (uint32)((*canreg_txrx & (~(uint32)(1U << CAN_OUT_BIT_POS))) | (uint32)(value << CAN_OUT_BIT_POS)); //doesn't pass MISRA
valueRead = (uint8_t)(((uint8_t)*regToOperateOn & (uint8_t)(1U << CAN_OUT_BIT_POS)) >> CAN_OUT_BIT_POS); //passes MISRA
I've tried multiple changes to the offending line where none of them have resulted in MISRA letting it through (except randomly where it just gives me no warnings) to the point where I decided to cast everything and that still doesn't work
*canreg_txrx = (uint32)((*canreg_txrx & (uint32)(~(uint32)((uint32)1U << (uint32)CAN_OUT_BIT_POS))) | (uint32)((uint32)value << (uint32)CAN_OUT_BIT_POS)); //doesn't pass MISRA
I'm really confused on how to fix this code so that it will pass MISRA as this bitwise logic is used in multiple places and all of them don't pass. I assumed that if everything is cast to the same type then there should really be no issue since it's all operating at the same type but for some reason that doesn't work.
I have never used Misra but I find your code hard to read with all that noise of typedefs, casts and parenthesis. My suggestion would be to simplify and split the problem into smaller parts along these lines:
*canreg_txrx = (uint32)((*canreg_txrx & (~(uint32)(1U << CAN_OUT_BIT_POS))) | (uint32)(value << CAN_OUT_BIT_POS)); // =>
*canreg_txrx &= ~(1U << CAN_OUT_BIT_POS);
*canreg_txrx |= value << CAN_OUT_BIT_POS; // maybe =>
#define BIT_CLEAR(val, pos) ((val) &= ~(1U << (pos)))
BIT_CLEAR(*canreg_txrx, CAN_OUT_BIT_POS);
*canreg_txrx |= value << CAN_OUT_BIT_POS; // *
'*' If value is a bit, then this is just bit set, otherwise can't think of good name.
Then run it through Misra and only fix what it complains about.
MISRA-C is a bit confused since it insists on a cast to "underlying type" (MISRA-C:2004 term) after the shift has been done. I prefer to do the cast to a large unsigned type before the shift, since that rules out all problems that can happen.
What these rules strive to prevent is an accidental conversion to a signed type through Implicit type promotion rules followed by something like a bit shift, which could be dangerous and cause various bugs, including change of signedness, accidental sign extension, undefined behavior upon left shifting signed values, impl.defined behavior upon right shifting them, and so on.
In your case, you get these problems specifically because you are not casting the result of the ~ to the underlying type here: ~(uint32)(1U << CAN_OUT_BIT_POS)). It appears that your static analyser is giving a false positive, since the underlying type here appears to be uint32_t. There is no smaller type to account for unless since you appear to have have 32 bit integers (since you have 32 bit CAN registers) , but the tool might not know that your integers are 32 bits, in which case 1U could have been a 16 bit value.
Otherwise, maybe the tool is getting confused because you use confused casts when defining those macros... but for the shift operators specifically, the type of the right operand does not matter (though it's promoted too).
In this particular case, none of this should be dangerous since you expect unsigned 32. The warnings, the casts, everything - it's just noise, turning your code brittle and unreadable.
I'd suggest you salvage the code in the following manner:
Get rid of all the uint8 casts from the macros, that's just noise and potentially causing problems.
Get rid of the home-brewed uint32 etc types and use standard C uint32_t. typedef uint32_t uint32; is rotten code that shouldn't exist in any C program. Use the existing industry standards, don't invent new ones.
Don't hide pointers behind typedefs, that's very dangerous practice which makes the code hard to read.
Get rid of superfluous casts. Assuming this is a 32 bitter, you shouldn't cast 1U types to uint32_t since they are the same type already.
Break up unreadable lines into several expressions. For example we can do const uint32_t mask = ~(1U << CAN_OUT_BIT_POS);. There should be no need to cast since the underlying type is already uint32_t. Your MISRA checker might be too dumb to realize that you have 32 bit integers though, so you might have to add a cast (uint32_t)~ .... Not for MISRA-C:2004 compliance but to silence a potentially dumb tool.
In case you just need to cast to silence a particular tool, you should make an explicit comment about that: /* this cast just to silence BrokenAnalyser from Bugworks */. To show that you know what you are doing and that you separate false positives from actual necessary casts.
Another valid concern of MISRA-C is that you shouldn't be doing multiple accesses to volatile registers in the same expression, since this introduces unsequenced side effects, plus you can't really tell when and where on that line the actual register access happens, plus it blocks compiler optimizations. This is another MISRA violation that you have not yet spotted. This can also be solved by breaking up the long expression into several.
One example:
const uint32_t mask = ~(1U << CAN_OUT_BIT_POS);
uint32_t txrx = *canreg_txrx; // volatile access on a line of its own
txrx = (txrx & mask) | ((uint32_t)value << CAN_OUT_BIT_POS);
*canreg_txrx = txrx;
The middle txrx line will get optimized and in the machine code you'll just get a few instructions anyway. This should be MISRA-C:2004 and 2012 compliant, far as I can tell. This also doesn't shift the implicitly promoted value and then cast afterwards.

binary numbers: strange differences between + and |

I'm trying to manipulating binary numbers with c. I found a strange thing with the minimum code below. Can anyone tell me what is the difference between "+" and "|" here? Thank you!
char next_byte1 = 0b11111111;
char next_byte2 = 0b11110101;
short a = (next_byte1 << 8) | next_byte2;
short b = (next_byte1 << 8) + next_byte2;
printf("a vs b is %d ~ %d.\n", a, b);
It showed: a vs b is -11 ~ -267, which is 0b11111111 11110101 and 0b11111110 11110101. I'm very confused with this result.
The problem you are seeing is because next_byte2 is sign-extended to a full int before doing the bitwise operation and thus is "corrupting" the high byte.
When doing bit manipulation it's better to use unsigned types (that is actually what unsigned are to be used for). Plain char types can be (and normally are) signed types and thus are better avoided for these uses.
You should never use char for binary/bitwise arithmetic, because it has implementation-defined signedness and might be negative. In general, use stdint.h over the default C types.
In case char is signed, then the value inside it ends up converted to -1 in two's complement during the variable initialization. This happens to next_byte1 and next_byte2 both.
Whenever you use a small integer type inside an expression, it is usually promoted to signed int. So your -1 (0xFF) gets changed to -1 (0xFFFFFFF) before you left shift.
Left shifting a negative operand is undefined behavior, meaning that any kind of bugs may rain all over your program. This happens in this case, so no results are guaranteed.
Apparently in your case, the undefined behavior manifested itself as you ending up with a large negative number with the binary representation 0xFFFFFF00.
The difference between | and + is that the latter cares about sign, so in case of + you end up adding negative numbers together, but in case of | the binary representations are simply OR:ed together.
You can fix the program in the following way:
#include <stdio.h>
#include <stdint.h>
int main(void)
{
uint8_t next_byte1 = 0xFF;
uint8_t next_byte2 = 0xF5;
uint16_t a = (next_byte1 << 8) | next_byte2;
uint16_t b = (next_byte1 << 8) + next_byte2;
printf("a vs b is %d ~ %d.\n", a, b);
}
And now | and + work identically, as intended.
Both answers should resolve your problem. I just want to add clarity around '|' and '+' operators.
'|' is the bitwise inclusive OR operator
'+' is the addition operator, which translates into inclusive OR
operator with CARRY propagation for multi-bit numbers.
They are not the same operator and one should not expect the same result in general, although for some cases they might lead to the same result like in your example.

C, compound assignment - arithmetic operation, conversion may loose information

Is there a way to write the operation short and suppress the warning?
BYTE data[10];
int i = 0;
int offset = 0;
for (i = 0; i < 7; i++) {
// some code
data[offset] |= (1 << i); // WARNING: conversion may loose information
}
my attempts:
data[offset] |= (BYTE) (1 << i); // failed
data[offset] |= (1 << (BYTE) i); // failed
Compiler: TIA-V13 WinCC from Siemens
Solution: No compound assignment operators!
You did not define BYTE; I'll assume it is identical to stdint.hs uint8_t (which should always be preferred to homebrew types).
a |= b (roughly) translates to a = a | b, including integer promotions here as part of the usual arithmetic conversions. For the types given this means a is converted to int before the operation is performed, yielding an int result. The final assignment will truncate this int for the assignment, possibly loosing information (likely the sign), because int is at least 16 bits wide.
Casting b does not help, because the conversion is done for the operation and you have to cast the result.
The transformation is also the solution:
uint8_t a;
a = (uint8_t)(a | b);
Explicitly tells the compiler you know what you do and to shut up. Just make sure you really know what you do!
This is not very elegant, but the only way to suppress the warning. OTOH the behaviour is arithmetically consistent with the simple assignment-version.
Edit: You should use unsigned integer constants when shifting where possible. Even more, as you operate on unsigned variables anyway. As a general rule, try to avoid mixing signed and unsigned operands. If that is not possible make sure you know what is going on and catch all invalid cases.
Edit2: For some implementations/signed integer representations and some combinations for types and operations, it is possible to prove no information can be lost, thus the warning is obsolete. However, it seems this would require knowledege of machine details which might not be available at the stage the warning is generated.
Maybe the C standard should have defined the compound assignment to implicitly include the final cast. That would not be the only legacy. OTOH, this behaviour might have generated confusion in the other direction to people which were not aware of this (note that in C an assignment yields a result!). Which is the lesser of two evils?

Why does this code for incrementing an uint8_t include `& 0xFF`?

When reading through some example codes for DMAs from Xilinx, I came across this piece of code:
value = (value + 1) & 0xFF
where value is an uint8_t.
What is the point of the & 0xFF? Why not simply write value = value + 1?
My guess is that this code was intended to work correctly even if value is not a 1-byte (8-bit) type. The bitmask 0xFF makes sure that only the last byte of the value is kept.
This kind of code is common when you want to avoid problems with implicit type promotions, or when you simply wish to demonstrate that you have considered implicit promotions when you wrote the code, which is good programming practice.
uint8_t is a small integer type and therefore always promoted to int whenever you use it in an expression. The result of (value + 1) is always int.
Without the masking, some compilers give warnings such as "attempting to store int in uint8_t". I've encountered such warnings on several compilers. Theoretically int & 0xFF is still an int, but since it cannot have a value larger than 0xFF, the compiler is likely able to optimize the type down to uint8_t and the warning will go away.
Alternatively you could write value = (uint8_t)(value + 1u); which has the same meaning (but is a MISRA-C compatible version of the code).

ANSI C All-Bits-Set Type-Promotion Safe Literal with -Wsign-conversion

Can I set all bits in an unsigned variable of any width to 1s without triggering a sign conversion error (-Wsign-conversion) using the same literal?
Without -Wsign-conversion I could:
#define ALL_BITS_SET (-1)
uint32_t mask_32 = ALL_BITS_SET;
uint64_t mask_64 = ALL_BITS_SET;
uintptr_t mask_ptr = ALL_BITS_SET << 12; // here's the narrow problem!
But with -Wsign-conversion I'm stumped.
error: negative integer implicitly converted to unsigned type [-Werror=sign-conversion]
I've tried (~0) and (~0U) but no dice. The preprocessor promotes the first to int, which triggers -Wsign-conversion, and the second doesn't promote past 32 bits and only sets the lower 32 bits of the 64 bit variable.
Am I out of luck?
EDIT: Just to clarify, I'm using the defined ALL_BITS_SET in many places throughout the project, so I hesitate to litter the source with things like (~(uint32_t)0) and (~(uintptr_t)0).
one's complement change all zeros to ones, and vise versa.
so try
#define ALL_BITS_SET (~(0))
uint32_t mask_32 = ALL_BITS_SET;
uint64_t mask_64 = ALL_BITS_SET;
Try
uint32_t mask_32 = ~((uint32_t)0);
uint64_t mask_64 = ~((uint64_t)0);
uintptr_t mask_ptr = ~((uintptr_t)0);
Maybe clearer solutions exist - this one being a bit pedantic but confident it meets your needs.
The reason you're getting the warning "negative integer implicitly converted to unsigned type" is that 0 is a literal integer value. As a literal integer value, it of type int, which is a signed type, so (~(0)), as an all-bits-one value of type int, has the value of (int)-1. The only way to convert a negative value to an unsigned value non-implicitly is, of course, to do it explicitly, but you appear to have already rejected the suggestion of using a type-appropriate cast. Alternative options:
Obviously, you can also eliminate the implicit conversion to unsigned type by negating an unsigned 0... (~(0U)) but then you'd only have as many bits as are in an unsigned int
Write a slightly different macro, and use the macro to declare your variables
`#define ALL_BITS_VAR(type,name) type name = ~(type)0`
`ALL_BITS_VAR(uint64_t,mask_32);`
But that still only works for declarations.
Someone already suggested defining ALL_BITS_SET using the widest available type, which you rejected on the grounds of having an absurdly strict dev environment, but honestly, that's by far the best way to do it. If your development environment is really so strict as to forbid assignment of an unsigned value to an unsigned variable of a smaller type, (the result of which is very clearly defined and perfectly valid), then you really don't have a choice anymore, and have to do something type-specific:
#define ALL_BITS_ONE(type) (~(type)0)
uint32_t mask_32 = ALL_BITS_SET(uint32_t);
uint64_t mask_64 = ALL_BITS_SET(uint64_t);
uintptr_t mask_ptr = ALL_BITS_SET(uintptr_t) << 12;
That's all.
(Actually, that's not quite all... since you said that you're using GCC, there's some stuff you could do with GCC's typeof extension, but I still don't see how to make it work without a function macro that you pass a variable to.)

Resources