1

I have the following program

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>

int main(void) {
    uint16_t o = 100;
    uint32_t i1 = 30;
    uint32_t i2 = 20;

    o = (uint16_t) (o - (i1 - i2)); /*Case A*/
    o -= (uint16_t) (i1 - i2);      /*Case B*/
    (void)o;
    return 0;
}

Case A compiles with no errors.
Case B causes the following error
[error: conversion to ‘uint16_t’ from ‘int’ may alter its value [-Werror=conversion]]

The warning options I'm using are:
-Werror -Werror=strict-prototypes -pedantic-errors -Wconversion -pedantic -Wall -Wextra -Wno-unused-function

I'm using GCC 4.9.2 on Ubuntu 15.04 64-bits.

Why do I get this error in Case B but not in Case A?

PS: I ran the same example with clang compiler and both cases are compiled fine.

Mohamed
  • 265
  • 3
  • 6

2 Answers2

1

Your case B is equivalent to:

o = o - (uint16_t) (i1 - i2);      /*Case B*/

The result is an int which may not fit in uint16_t, so, per your extreme warning options, it produces a warning (and thus an error since you're treating warnings as errors).

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • The result *does* fit in uint16_t in this code, so the compiler is being lazy. – M.M Aug 20 '15 at 21:23
  • @MattMcNabb, feel free to fix it. – ams Aug 20 '15 at 21:35
  • @ams no doubt I'd then have to wage war with a bunch of people who want to see this warning even when it is bogus – M.M Aug 20 '15 at 21:37
  • I imagine it's just that the warning is emitted before constant propagation occurs. Fixing it probably requires more code than is worthwhile. – ams Aug 20 '15 at 21:42
  • The same error is generated with all the following optimization options `[O0, O1, O2, O3, Os]` – Mohamed Aug 21 '15 at 23:02
1

Integer Promotion is a strange thing. Basically, all integer values, of any smaller size, are promoted to int so they can be operated on efficiently, and then converted back to the smaller size when stored. This is mandated by the C standard.

So, Case A really looks like this:

o = (uint16_t) ((int)o - ((uint32_t)i1 - (uint32_t)i2));

(Note that uint32_t does not fit in int, so needs no promotion.)

And, Case B really looks like this:

o = (int)o - (int)(uint16_t) ((uint32_t)i1 - (uint32_t)i2);

The main difference is that Case A has an explicit cast, whereas Case B has an implicit conversion.

From the GCC manual:

-Wconversion

Warn for implicit conversions that may alter a value. ....

So, only Case B gets a warning.

ams
  • 24,923
  • 4
  • 54
  • 75