gcc size_t and sizeof arithmetic conversion to int - c++

I decided to test compile a project with -Wsign-conversion enabled, to see what warnings would come up, and came across something that doesn't seem right, where gcc behaves differently than clang. Can someone please tell me which is correct?
I have a function that takes a size_t param:
void func(size_t) {}
some other struct
struct Test {};
and calling code
int i = some_initialiser();
func(sizeof(Test) + static_cast<size_t>(i));
So from my understanding, sizeof returns size_t, and arithmetic between two variables of type size_t should return a size_t, so there shouldn't be any conversion here other than my static_cast, but gcc gives me the warning
warning: conversion to ‘long unsigned int’ from ‘int’ may change the sign of the result [-Wsign-conversion]
Clang doesn't warn here, but does warn if I remove the static_cast in the function call, as expected.

This is a known bug in gcc, fixed in versions 9.3.0 and above.
The warning is valid (compilers can warn about anything they like), but gcc's behavior contradicts its own documentation. There is an existing bug report for this problem (see below).
Here's a simpler test case that illustrates the issue:
#include <cstddef>
int main() {
int i = 42;
size_t s0 = sizeof (int) + (size_t)i;
size_t s1 = sizeof (int) + static_cast<size_t>(i);
}
When I compile it on my system using gcc 9.1.0, I get:
$ g++ -Wsign-conversion -c c.cpp
c.cpp: In function ‘int main()’:
c.cpp:4:32: warning: conversion to ‘long unsigned int’ from ‘int’ may change the sign of the result [-Wsign-conversion]
4 | size_t s0 = sizeof (int) + (size_t)i;
| ^~~~~~~~~
c.cpp:5:32: warning: conversion to ‘long unsigned int’ from ‘int’ may change the sign of the result [-Wsign-conversion]
5 | size_t s1 = sizeof (int) + static_cast<size_t>(i);
| ^~~~~~~~~~~~~~~~~~~~~~
$
Note that the warning occurs both for a C-style cast and for a static_cast.
It's true that the conversion may change the sign of the result (converting a negative int to size_t yields a positive result), but gcc's documentation for -Wsign-conversion says:
'-Wsign-conversion'
Warn for implicit conversions that may change the sign of an
integer value, like assigning a signed integer expression to an
unsigned integer variable. An explicit cast silences the warning.
In C, this option is enabled also by '-Wconversion'.
In this case, an explicit cast is not silencing the warning.
This bug had already been reported:
Bug 87519 - -Wsign-conversion -Wconversion explicit cast fails to silence warning
The fix is commit 61e52125c935279af11b10d27060a96bff7477a4 in the gcc git repo, committed 2019-08-08.

The warning is correct.
If i has a negative value the casting will be problematic. Your function should return an unsigned value (e.g. unsigned int).
From GCC documentation - https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html:
For C++, also warn for confusing overload resolution for user-defined conversions; and conversions that never use a type conversion operator: conversions to void, the same type, a base class or a reference to them. Warnings about conversions between signed and unsigned integers are disabled by default in C++ unless -Wsign-conversion is explicitly enabled.

Related

Why does the "[]" operator of std::map<std::string, std::string> accept a double value but gives unexpected output? [duplicate]

This question already has answers here:
Why does C++ allow an integer to be assigned to a string?
(4 answers)
Closed 6 years ago.
Why isn't the compiler complaining about this code:
#include <string>
#include <iostream>
int main()
{
std::string a;
a = 2.3;
std::cout << "A:" << a << std::endl;
return 0;
}
GCC, MSVC don't seem to be concerned about this at all,
even though it is clearly wrong and doesn't actually work anyway!
The output is:
A:
OUCH ! Lead to an undetected error in my program.
std::string has an overload for operator= that takes a character. When you pass an argument to a function by value (i.e, an operator), copy initialization occurs. In copy initialization, standard conversions, also known as an "implicit conversion", may be used to the convert the value. In this case, your double is being silently converted to a char so that it may be used in operator=.
For GCC, -Wall -Wextra -pedantic will not make a diagnostic appear. You can try -Wfloat-conversion, which is enabled by -Wconversion. Example:
main.cpp:11:10: warning: conversion to 'char' alters 'double' constant value
[-Wfloat-conversion]
a = 3.2;
Alternatively, use braces to force a narrowing conversion error.
s = {4.3};
// warning: narrowing conversion of '4.2e+1' from 'double' to 'char' inside { }
// [-Wnarrowing]
The compiler will do an implicit conversion from 2 to the equivalent ASCII char: "start of text" (therefore you can't see anything).
Maybe you have disabled the warning of your compiler. Try to turn them on.
GCC:
compile in gcc with option [-Wconversion], then you'll see that the compiler will issue the warning:
warning: conversion to 'char' alters 'double' constant value [-Wfloat-conversion]
LIVE DEMO
MSVC don't seem to be concerned about this at all:
VS2013 produces the following warning:
warning C4244: 'argument' : conversion from 'double' to 'char', possible loss of data
even though it is clearly wrong and doesn't actually work anyway!
It's not wrong, it's just an implicit conversion from the truncated double 2.3 to a char with ASCII code 2 (start of text).

How to detect implicit conversion losses integer precision on std::vector::emplace_back

I compiled the following code with -Wconversion compiler option to detect implicit conversion loses integer precision:
#include <vector>
#include <cstdint>
int main() {
std::vector<std::uint16_t> v;
std::uint32_t a = 0;
v.emplace_back(a); // no warning
v.push_back(a); // warning: implicit conversion loses integer precision
}
Compiling Demo https://wandbox.org/permlink/K5E4sUlfGBw6C5w8
The vector's value_type is std::uint16_t.
If I push_back std::uint32_t value to the vector, then I got the following warning as I expected.
prog.cc:8:17: warning: implicit conversion loses integer precision: 'std::uint32_t' (aka 'unsigned int') to 'std::__1::vector<unsigned short, std::__1::allocator<unsigned short> >::value_type' (aka 'unsigned short') [-Wimplicit-int-conversion]
v.push_back(a); // warning: implicit conversion loses integer precision
~~~~~~~~~ ^
1 warning generated.
However, if I emplace_back the same value to the vector, no warning is detected.
I've tested it clang++ 10.0.0, clang++ 9.0.0, and g++ 9.3.0 and got the same result.
Is there any good way to detect implicit conversion losses integer precision on std::vector::emplace_back ?
There is no implicit conversion when you call v.emplace_back(a). There would be an implicit conversion if you called v.emplace_back<const std::uint16_t &>(a).
A key difference between push_back and emplace_back is that the latter is a template while the former is not. If you do not specify a template argument for emplace_back, the compiler deduces it from the function argument, which means no conversion will be necessary at the point of call. Within emplace_back the conversion happens in a system header, which suppresses the warning.
So in your example,
v.emplace_back(a);
is deduced as
v.emplace_back<const std::uint32_t &>(a);
where the function argument is expected to be a std::uint32_t. Perfect match, no conversion necessary outside the system header. If you were to enable warnings within system headers, you could end up with a bunch of spurious warnings
To get an implicit conversion in your code, you need to force emplace_back to expect a std::uint16_t, which can be done via
v.emplace_back<const std::uint16_t &>(a);
This would implicitly convert a to std::uint16_t before calling emplace_back, triggering the compiler warning in the same way that push_back does.

Unable to make function call in template argument

The following program compiles fine.
#include <bitset>
#include <cmath>
int main()
{
const int r = std::sqrt(100);
std::bitset<r> n;
}
$ g++ -Wall -Wextra -pedantic -std=c++11 foo.cpp
$
But the following program fails to compile.
#include <bitset>
#include <cmath>
int main()
{
std::bitset<std::sqrt(100)> n;
}
$ g++ -Wall -Wextra -pedantic -std=c++11 bar.cpp
bar.cpp: In function ‘int main()’:
bar.cpp:6:31: error: conversion from ‘__gnu_cxx::__enable_if<true, double>::__type {aka double}’ to ‘long unsigned int’ not considered for non-type template argument
std::bitset<std::sqrt(100)> n;
^
bar.cpp:6:31: error: could not convert template argument ‘std::sqrt<int>(100)’ to ‘long unsigned int’
bar.cpp:6:34: error: invalid type in declaration before ‘;’ token
std::bitset<std::sqrt(100)> n;
^
bar.cpp:6:33: warning: unused variable ‘n’ [-Wunused-variable]
std::bitset<std::sqrt(100)> n;
^
According to me, both C++ programs are equivalent. Why is it that then the second one does not compile whereas the first one does?
Update
Some of the answers are saying that std::sqrt() is generally not declared as constexpr but on gcc has extended it by declaring it constexpr. But it still does not answer my question.
If std::sqrt() is not declared as constexpr, then both programs should fail to compile.
If std::sqrt() is declared as constexpr in gcc, then both programs should compile successfully.
Why is it that only the first program compiles but the second one fails?
The first program might compile for you, but it is not portable because the std::sqrt function is not specified by the standard to be constexpr. It appears that GCC has decided to make it constexpr:
template<typename _Tp>
inline _GLIBCXX_CONSTEXPR
typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value,
double>::__type
sqrt(_Tp __x)
{ return __builtin_sqrt(__x); }
But another standard library implementation might not have a constexpr std::sqrt function so your first program would not compile there.
Let us instead simplify the code, and modify it a bit so that the relevant concepts are involved exactly:
constexpr std::size_t r = 10.0;
std::bitset<r> b1; // OK
std::bitset<10.0> b2; // ill-formed
It really looks as though the declarations of b1 and b2 should be treated the same way, but the rules for implicit conversions of template arguments are more strict than the rules for implicit conversions elsewhere.
According to the standard, when the type of a template argument (here, double) differs from the type of the template parameter it is being passed to (here, std::size_t), only a restricted set of conversion is allowed, namely "conversions permitted in a converted constant expression" ([temp.arg.nontype]/5). According to [expr.const]/3, a converted constant expression can involve only the following conversions:
user-defined conversions
lvalue-to-rvalue conversions
integral promotions
integral conversions other than narrowing conversions
A floating-integral conversion is not allowed in this context, even though it is allowed in the initialization of r.

Narrowing conversion from `int` (constant expression) to `unsigned int` - MSVC vs gcc vs clang

constexpr int i = 100;
struct F { F(unsigned int){} };
int main() { F{i}; }
The snippet above:
Compiles with no warnings on g++ 7 with -Wall -Wextra -Wpedantic.
Compiles with no warnings on clang++ 4 with -Wall -Wextra -Wpedantic.
Fails to compile on MSVC 2017:
conversion from 'const int' to 'unsigned int' requires a narrowing conversion
Q: is MSVC wrong here?
live example on godbolt.org
int i = 100;
struct F { F(unsigned int){} };
int main() { F{i}; }
Compiles with warnings on g++ 7 with -Wall -Wextra -Wpedantic:
narrowing conversion of 'i' from 'int' to 'unsigned int'
Fails to compile clang++ 4 with -Wall -Wextra -Wpedantic:
non-constant-expression cannot be narrowed from type 'int' to 'unsigned int' in initializer list
Fails to compile on MSVC 2017:
conversion from 'const int' to 'unsigned int' requires a narrowing conversion
Q: is g++ wrong here? (i.e. should it produce an hard error?)
live example on godbolt.org
There is never a requirement that any C++ program produce a hard error. There are requirements to print diagnostics. The form of the diagnostic is unspecified by the standard: an old joke is that printing out a single space satisifies the diagnostic requirements of the standard. That would be a quality of implementation issue.
There are ill-formed programs upon which the standard places no restrictions on their behavior, and sometimes a mandatory diagnostic.
There are cases where a program is ill-formed and a diagnostic is required. One way to handle that is to produce a message saying it is an error, then do not generate any binary to run. Another way is to produce a message saying it is a warning, then produce a binary that can be run.
So, g++ is not wrong under the standard for merely printing out a warning.
The resulting program is technically all undefined behavior; g++ could format your hard drive when it runs without violating the standard. That would be considered a quality of implementation issue.
Shafik's answer here covers the first question. i is constant expression and its value fits the target type; there should be no warning or error about the narrowing conversion.
The C++ standard does not defend you against hostile compilers.
Reportedly, -pedantic-errors can be passed to g++ to have it generate hard errors instead of warnings when the standard mandates the resulting program would be ill-formed.

GCC -Wconversion warns in conjunction with negation, but not otherwise

Take this piece of code:
int main()
{
short a = 2, b = 1;
float f = 5.36f;
-a * f;
b * f;
}
Compile:
~ $ g++ -std=c++11 wconversion.cpp -Wconversion
wconversion.cpp: In function ‘int main()’:
wconversion.cpp:6:8: warning: conversion to ‘float’ from ‘int’ may alter its value [-Wconversion]
-a * f;
Why does it warn for a, but not for b?
EDIT: Since it seems that it depends on the compiler version: I'm using GCC 4.9. Also, the behavior is the same in other code where a and b are not constant.
What does the warning mean?
Some values of a 32-bit type int cannot be represented exactly as float. If such an int value is converted to float, the nearest float will be picked (the choice between the two surrounding floats is implementation-defined, but nearly all implementations pick the nearest one).
The warning appears to be about this loss of information during the conversion from int or a wider integer type to float.
Should a smart compiler warn for b?
A smart compiler doesn't need to emit a warning for b, because b is a short (presumably 16-bit on the OP's architecture), and all the values b could have at run-time can be represented exactly as float.
Should a smart compiler warn for -a?
A smart compiler could avoid warning for a for the same reason. -a has type int because of promotions, but the values of -a range from -(215-1) to 215 (on the OP's platform). All these values can be represented exactly as float. However, the general warning that appears to be triggered here is for an expression of type int or wider. GCC does not seem to be able to detect that the situation the message warns about cannot arise.
It depends upon the version of gcc. With 4.4.5 I get a warning on both lines:
foo.c:2: warning: function declaration isn’t a prototype
foo.c: In function ‘main’:
foo.c:6: warning: conversion to ‘float’ from ‘int’ may alter its value
foo.c:6: warning: statement with no effect
foo.c:7: warning: conversion to ‘float’ from ‘int’ may alter its value
foo.c:7: warning: statement with no effect
foo.c:8: warning: control reaches end of non-void function
Perhaps the developers made a special case, recognizing that b is "1" and there is an exact conversion, while a "-2" runs into a potential ambiguity on the user's part (except for special cases, conversion will round off - and the heuristic may need refinement). With gcc 4.9, I get only one warning:
foo.c:1:5: warning: function declaration isn’t a prototype [-Wstrict-prototypes]
int main()
^
foo.c: In function ‘main’:
foo.c:6:6: warning: conversion to ‘float’ from ‘int’ may alter its value [-Wconversion]
-a * f;
^
foo.c:6:3: warning: statement with no effect [-Wunused-value]
-a * f;
^
foo.c:7:3: warning: statement with no effect [-Wunused-value]
b * f;
^
foo.c:8:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
gcc (Debian 4.9.2-10) 4.9.2