mastupristi
mastupristi

Reputation: 1478

how to fix errors caused by -Wconversion?

I have a piece of fake/sample code:

#include <stdint.h>

uint8_t func(uint16_t val0, uint16_t val1, uint16_t val2)
{
    uint8_t r;
    val0 += 0x7F;
    val1 += (uint16_t)0x7F;
    val2 += 0x7FU;
    r = (uint8_t)((val0+val1+val2) >> 8);
    r <<= (uint8_t)(val2 & 0x3);
    return r;
}

Here are the errors I get:

$ gcc -Wall -Wextra -Wconversion -O0 module.c -c -o module.o 
module.c: In function 'func':
module.c:6:10: warning: conversion from 'int' to 'uint16_t' {aka 'short unsigned int'} may change value [-Wconversion]
    6 |  val0 += 0x7F;
      |          ^~~~
module.c:7:10: warning: conversion from 'int' to 'uint16_t' {aka 'short unsigned int'} may change value [-Wconversion]
    7 |  val1 += (uint16_t)0x7F;
      |          ^
module.c:8:10: warning: conversion from 'unsigned int' to 'uint16_t' {aka 'short unsigned int'} may change value [-Wconversion]
    8 |  val2 += 0x7FU;
      |          ^~~~~
module.c:10:8: warning: conversion from 'int' to 'uint8_t' {aka 'unsigned char'} may change value [-Wconversion]
   10 |  r <<= (uint8_t)(val2 & 0x3);
      |        ^

The example shows some ways I tried to fix the problem (e.g. cast)

Any suggestion?

EDIT:

modified example

Upvotes: 4

Views: 1053

Answers (2)

Petr Skocik
Petr Skocik

Reputation: 60097

Casts/bitmasks can do the trick.

E.g.,

#include <stdint.h>

uint8_t func(uint16_t val0, uint16_t val1, uint16_t val2)
{
    unsigned int r;
    val0 = (uint16_t)(val0 + 0x7Fu);
    /*val0 = (val0 + 0x7Fu) & 0xFFFF;*/
    val1 = (val1 + 0x7Fu) & 0xFFFF;
    val2 = (val2+0x7FU) & 0xFFFF;
    r = ((0u+val0+val1+val2) >> 8);
    r = ( r << (uint8_t)(val2 & 0x3u)) & 0xFF;
    return r&0xFF;
}

It might be a good idea to try and avoid shorter-than-int types. Because of integer promotions (sub-int integer types getting promoted to int), they tend to lead to a lot of -Wconversion warnings.

Upvotes: 2

R.. GitHub STOP HELPING ICE
R.. GitHub STOP HELPING ICE

Reputation: 215387

This question illustrates why -Wconversion is so overzealous that it's largely useless.

In C, there is no arithmetic smaller than int. For example:

val0 += 0x7F;

is evaluated as if it were

val0 = (int)val0 + 0x7F;

The assignment back to val0 without a cast then triggers -Wconversion. In some sense this is a legitimate warning: the + operator does not overflow, but assiging the result back may lose part of the result, and the compiler is telling you this (albeit awkwardly).

If you're going to use -Wconversion, you essentially can't use compound assignment operator (like +=) with smaller-than-int types. You need to write out the equivalent form and use a cast to indicate that you intend for the lossy conversion to happen. For instance here you would write:

val0 = (uint16_t)(val0 + 0x7F);

I don't think this is terribly good style, but some coding standards/policies (MISRA, I think, for one) mandate it.

Upvotes: 9

Related Questions