1

I am writing some low level code for my emulator which involves a lot of 16 and 8 bit unsigned integers. I enabled -Wconversion warning in my project and all warnings are considered as errors (-Werror).

Consider this code snippet:

#include <cstdint>

int main ()
{
    uint16_t a = 4;
    uint16_t b = 6;
    uint16_t c = a + b;
}

Until GCC 9.3 with -std=c++17 -Wconversion -Werror as compilation flags, gives the following error:

<source>: In function 'int main()':
<source>:7:20: error: conversion from 'int' to 'uint16_t' {aka 'short unsigned int'} may change value [-Werror=conversion]
    7 |     uint16_t c = a + b;

      |                  ~~^~~

But the same code does not give this error for GCC 10.1, and for any compiler version of Clang (Tested it until Clang 5.0.0). Link to compiler explorer.

So my questions are the following:

  • IMO, addition of two unsigned ints should not be implicitly converted to an int. Or is my assumption wrong?
  • Why Clang and GCC(until 9.3) produce different results? Does the standard stipulate any constraints on this operation or is it up to the compiler vendor to decide?
  • What changed in GCC 10.1? Why is this error not popping up for GCC 10.1?
pankycodes
  • 61
  • 2
  • 6
  • With `-Werror` this is no longer the standard C++, but a subset of it. The compilers can give you warnings on anything they want. – HolyBlackCat May 28 '20 at 10:21

3 Answers3

6

IMO, addition of two unsigned ints should not be implicitly converted to an int. Or is my assumption wrong?

This assumption is not wrong. Unsigned int is never implicitly converted to int.

However on your system, uint16_t happens to be unsigned short int. Assuming that unsigned short ints aren't implicitly converted to int is a wrong assumption. They are promoted to int on most systems.

There was recently a good question about why the promotion is to signed int: https://stackoverflow.com/a/62042330/2079303

Why Clang and GCC(until 9.3) produce different results?

They don't. Both will promote to int. One simply didn't warn about the conversion. The conversion isn't ill-formed so there is no requirement to issue a diagnostic.

or is it up to the compiler vendor to decide?

Diagnostic messages are up to the compiler vendor's discretion(, except that a diagnostic message is required when program is ill-formed, unless otherwise specified).

What changed in GCC 10.1? Why is this error not popping up for GCC 10.1?

Perhaps they decided that the warning isn't useful in this case, and got rid of it.

eerorika
  • 232,697
  • 12
  • 197
  • 326
4

There is no addition of two unsigned int in your code. In fact you add two unsigned short, since uint16_t is a typedef for unsigned short.

The integer promotion rules say that any integer type narrower than int which appears as an operand of + is promoted to int (not unsigned int as you might expect).

So the steps involved are that (int)4 is added to (int)6 giving (int)10 and that is then assigned back to c.

You should find that all compilers give the correct value for c, the behaviour of the code is well-defined.

The behaviour of -Wconversion is more contentious. As this code is a perfect example of, it often gives a warning for code which is well-defined. There is no obvious solution to this problem other than to just not use the flags, or wrap the false positive in some way (perhaps by #pragma or a function call).

Some people do want to see a warning for this code and some people don't. The exact set of cases for which a warning is generated by -Wconversion keeps changing in gcc due to people filing bug reports that they {do|don't} want to see a warning for some particular case.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Re: *There is no obvious solution to this problem...* An explicit cast should silence the warning: `uint16_t c = uint16_t(a + b);`. – Adrian Mole May 28 '20 at 10:23
  • @AdrianMole until some next (or previous) version of compiler comes along and doesn't treat that cast as a silencing request... that approach is a bit unstable IMO. As well as littering the code with casts which is generally considered undesirable – M.M May 28 '20 at 10:24
0

I ran into the same warning for doing a bit shift:

uint8_t left_shift(uint8_t a, uint8_t b) { return a << b; }

Clang output with -Wconversion warnings enabled:

<source>:10:53: warning: implicit conversion loses integer precision: 'int' to 'uint8_t' (aka 'unsigned char') [-Wimplicit-int-conversion]

uint8_t left_shift(uint8_t a, uint8_t b) { return a << b; }
                                           ~~~~~~ ~~^~~~

But the assembly listing shows no sign of the phantom int promotion others are referring to in this thread. Only byte widths are used throughout:

https://godbolt.org/z/eSVaQW

left_shift(unsigned char, unsigned char):
        push    rbp
        mov     rbp, rsp
        mov     byte ptr [rbp - 1], dil
        mov     byte ptr [rbp - 2], sil
        movzx   eax, byte ptr [rbp - 1]
        movzx   ecx, byte ptr [rbp - 2]
        shl     eax, cl
        movzx   eax, al
        pop     rbp
        ret