GCC and -Wconversion

Viewed 1809

Let's compile the following program:

int main()
{
    uint16_t data = 0;
    data |= uint16_t(std::round(3.14f));
    return 0;
}

with g++ -Wconversion prog.cpp

We'll get warning: conversion to ‘uint16_t {aka short unsigned int}’ from ‘int’ may alter its value, but I can't see implicit conversions here.

This kind of warnings should be muted by explicit casts, for example:

double d = 3.14;
float foo1 = d; // Warning
float foo2 = float(d); // No warning
float foo2 = static_cast<float>(d); // No warning

Is GCC right here or it's a bug?

Note that my snippet is minimal. For example, warning disappears in following cases:

  • remove the f suffix from 3.14, i.e. make it double
  • use assignment instead of |=
  • remove std::round
  • cache rounding result: const auto r = uint16_t(std::round(3.14f));, then or-assign it to data.
2 Answers

Is GCC right here or it's a bug?

Since the behavior does not match the expectation, I'd call it a bug.

From https://godbolt.org/z/aSj--7, it seems that in GCC's eye, data |= uint16_t(std::round(3.14f)) is translated as

(void) (data = TARGET_EXPR <D.2364, (uint16_t) round (3.1400001049041748046875e+0)>;, data | NON_LVALUE_EXPR <D.2364>;)

(TARGET_EXPR represents a temporary object. D.2364 is an internal variable name.)

Translate GCC's internal language back to C++, and we'll get

data = (temp = (uint16_t) round (3.14e+0), data | temp)

Since the LHS of the comma expression does not affect the RHS, this should be as safe as data = data | temp. However, GCC warns at the former but not at the latter, which is very unlikely to be intentional. Thus I believe that it is an oversight of GCC maintainers.

The warning is bogus.

According to [over.built]/22:

For every triple (L, VQ, R), where L is an integral type, VQ is either volatile or empty, and R is a promoted integral type, there exist candidate operator functions of the form ...

VQ L&   operator|=(VQ L&, R);

So we get a built-in unsigned short operator |=(unsigned short&, unsigned int);

There are no implicit conversions in the given expression

uint16_t data = 0;
data |= uint16_t(std::round(3.14f));
Related