Search code examples
cgccinteger-promotionuint16

uint16_t subtraction GCC compilation error


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.


Solution

  • 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.