gcc warning "-WOverflow" - c++

please consider the following snippet of C++98 code:
#include <iostream>
#include <limits>
template <typename T> T myLower() {
if (std::numeric_limits<T>::is_integer) {
return std::numeric_limits<T>::min();
} else {
return -std::numeric_limits<T>::max();
}
}
int main(int argc, char *argv[]) {
std::cout << (int)myLower<uint8_t>() << "\n";
return 0;
}
When compiled with g++ test.cpp && ./a.out it works as expected: 0 is printed. But when compiled with any optimization (-O1 upwards, -Os as well) the -Woverflow warning is emitted for the else-branch, which should only be there for floating-point numbers:
g++ test.cpp -Wall -O1 && ./a.out
test.cpp: In instantiation of ‘T myLower() [with T = unsigned char]’:
test.cpp:16:46: required from here
test.cpp:9:16: warning: large integer implicitly truncated to unsigned type [-Woverflow]
retval = -std::numeric_limits<T>::max();
~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0
This also fails for uint16, all other types work as they should. As far as I can see the std::numeric_limits<uint8_t>::is_integer should be a compile-time constant -- why is the else-branch compiled at all? The warning is not serious or did I miss something?
Ah, this happens in gcc5 and gcc6.
Thanks!
PS: I know that there will be std::numeric_limits<>::lowest() when the future will have arrived here...

The problem here is that
if (std::numeric_limits<T>::is_integer)
Does not limit what T can be. That means the else brance is always compiled and can trigger a warning if the operation would cause an overflow even if the branch is never reached.
If you had C++17 you could use if constexpr like
template <typename T> T myLower() {
if constexpr (std::numeric_limits<T>::is_integer) {
return std::numeric_limits<T>::min();
} else {
return -std::numeric_limits<T>::max();
}
}
Which would only compile the if block or the else block depending on the constexpr condition. Before C++17 you would have to use SFINAE to constrain the function to only compile if the template resolves.

Related

Why does `GCC 10 -Wtype-limits` not warn about comparison with `inline static constexpr` expressions when GCC 7 does?

When compiling this snippet of code (example code)
#include <iostream>
#include <cstdlib>
#include <chrono>
#ifndef USE_DEFINE
class ClassA
{
public:
inline static constexpr uint8_t A = 0;
};
#else
#define A 0
#endif
int main()
{
srand(std::chrono::system_clock::now().time_since_epoch().count());
uint8_t a = rand() % 255;
#ifndef USE_DEFINE
if(a < ClassA::A)
#else
if(a < A)
#endif
{
std::cout << "Shouldn't even compile using `-Wall -Wextra -Werror -Wfatal-errors -Wpedantic` flags." << std::endl;
return -1;
}
return 0;
}
with GCC 7.5 and the following flags
g++ -std=gnu++17 -Wall -Wextra -Wpedantic -Wfatal-errors -Werror -o test main.cc
i get the following warning/error (due to -Werror):
main.cc: In function ‘int main()’:
main.cc:40:7: error: comparison is always false due to limited range of data type [-Werror=type-limits]
40 | if(a < ClassA::A)
Which seems ok to me due to the fact that i'm trying to check if an uint8_t is less than 0. The same happens when defining USE_DEFINE. Both expressions are not constant due to the changing value of a so it could be checked if the type limits allow this kind of comparison to evaluate as true AND false.
Doing the same with a newer compiler (GCC 10.1.0 / CLang 10.0.1) i don't get this warning anymore when using the inline static constexpr uint8_t A = 0 of ClassA. When i use USE_DEFINE the same happens.
Is this behaviour of GCC/Clang erroneous or is this some sideeffect caused by attributes of const/constexpr?
The GCC-Manual says (for GCC 6.4/7.5 as well as 10):
-Wtype-limits
Warn if a comparison is always true or always false due to the limited range of the data type, but do not warn for constant expressions. For example, warn if an unsigned variable is compared against zero with < or >=. This warning is also enabled by -Wextra.
As far as i can see if(a < ClassA::A) isn't a constant expression even though i'm using the constexpr keyword. The same happens when using const instead of constexpr. What does lead to this behaviour?

gcc gives no warning for comparing unsigned integer < 0 even with Wextra enabled

Take the following code
#include <iostream>
template<typename T>
T f(T x, unsigned y) {
if (y < 0) return x;
return static_cast<T>(0);
}
using namespace std;
int main() {
int a = f(2, 3);
std::cout << a << std::endl;
return 0;
}
where function f clearly always returns 0. Compiling it with g++-7.2.0 -Wall -Wextra gives no hint about pointless comparison. However, clang warns us nicely:
a.cpp:7:11: warning: comparison of unsigned expression < 0 is always false [-Wtautological-compare]
if (y < 0) return x;
~ ^ ~
1 warning generated.
Why is this so (I presume templates are the root of the problem) and can gcc be forced to output the warning in this case?
This is a regression bug in some versions of GCC (including 8.x and 9.x - which are still the default compilers on many distributions at the time of writing).
The bug was tracked here (#jureslak file it again, but that was marked as dupe) and has been resolved. See the warning with GCC 10.1 (Godbolt).

Why constexpr implicit conversion doesn't always work?

#include <iostream>
struct Index {
constexpr operator int() const { return 666; }
};
template <int i> void foo() {
std::cout << i << std::endl;
}
void wrapper(Index index) {
foo<index>();
}
int main() {
Index index;
// foo<index>(); // error: the value of ‘index’ is not usable in a constant expression
wrapper(index);
}
Hello, everyone.
I'm using a constexpr conversion of a variable "index" to an int value, which is substituted to a "foo" templated function.
If I directly call foo<index>() from "main", I get a compiler error.
If the same call is done from the "wrapper", then everything compiles and works fine.
What am I missing there?
Compile command: g++ -std=c++14 main.tex with g++ (GCC) 5.3.1 20160406 (Red Hat 5.3.1-6).
A very similar error was reported here. It was first reported with GCC 4.9.0
In the analysis provided:
This is a GCC bug. It appears to be some sort of confusion in the way GCC handles internal linkage non-type template parameters of pointer type.
It has since been resolved.

Why does g++ 4.8.1 issue a conversion warning

When I compile the code below with g++ 4.8.1 (64bit) in this way:
$ g++ -Wconversion -o main main.cpp
I get this result:
main.cpp: In function ‘int main()’:
main.cpp:12:20: warning: conversion to ‘int’ from ‘long unsigned int’ may alter its value [-Wconversion]
int i = sizeof(x)/sizeof(x[0]);
^
My expectation would be that the compiler should be able to evaluate the expression at compile time. If you make a similar program in plain c, gcc works like a charm.
Should this be considered a bug in g++ (e.g. clang++ does not have this problem)?
if you change the problematic line to something like:
char c = 0x10000000/0x1000000;
then the compiler does not complain. This suggest that some constant evaluation is done before warning generation.
main.cpp:
#include <iostream>
struct foo {
int a;
int b;
};
foo x[50];
int main()
{
int i = sizeof(x)/sizeof(x[0]);
std::cout << i << std::endl;
return 0;
}
int i = sizeof(x)/sizeof(x[0]);
//int <-- std::size_t <-- std::size_t / std::size_t
The type of the expression sizeof(x)/sizeof(x[0]) is std::size_t which on your machine is unsigned long int. So conversion from this type to int is data-loss, if the source is bigger in size than the target.
Though, I agree that in your case, there would not be actual data-loss if the compiler actually computes the value, but I guess it applies -Wconversion before the actual computation.
sizeof() returns you std::size_t not int! So cast it or declare i as std::size_t.
std::size_t i = sizeof(x)/sizeof(x[0]);

Any ideas on how to turn this into a warning?

I'm using this is_enum function to check if a variable is an enum or not.
(See error below)
#include <boost/type_traits/is_enum.hpp>
#include <boost/static_assert.hpp>
template<typename T>
void is_enum(T)
{
BOOST_STATIC_ASSERT(boost::is_enum<T>::value == true);
}
int main()
{
char c = 'a';
is_enum(c);
return 0;
}
This gives me the following error:
-*- mode: compilation; default-directory: "/home/epronk/enums/" -*-
Compilation started at Thu Nov 10 21:20:05
g++ -I /home/epronk/src/boost_1_47_0/ q.cpp
q.cpp: In function ‘void is_enum(T) [with T = char]’:
q.cpp:13: instantiated from here
q.cpp:7: error: invalid application of ‘sizeof’ to incomplete type ‘boost::STATIC_ASSERTION_FAILURE<false>’
q.cpp:7: error: invalid application of ‘sizeof’ to incomplete type ‘boost::STATIC_ASSERTION_FAILURE<false>’
Compilation exited abnormally with code 1 at Thu Nov 10 21:20:05
(not sure why g++ (Debian 4.4.5-8) 4.4.5 give me the same error twice)
Is it possible to change this function so it becomes a warning?
For a char you can try to assign 256 to it which results in a overflow error.
edited
Some context: I want to find switch statements like this one.
#define switch(arg) \
is_enums(arg); \
switch(arg)
int main()
{
char c = Red;
switch(c)
{
case Banana: // No warning
break;
case Red:
break;
case Green:
break;
case Blue:
break;
}
return 0;
}
EDIT: Try BOOST_STATIC_WARNING http://www.boost.org/doc/libs/1_46_1/libs/serialization/doc/static_warning.html; the code below is my hand-hacked version of doing something similar.
Something like this:
#include <boost/type_traits/is_enum.hpp>
#include <boost/utility.hpp>
#include <boost/static_assert.hpp>
enum AB { A, B };
template<typename T>
typename boost::enable_if_c< boost::is_enum<T>::value,
void >::type is_enum(T) {
}
template<typename T>
typename boost::enable_if_c< !boost::is_enum<T>::value,
void >::type is_enum(T) {
int NOT_AN_ENUMERATION = 1;
}
int main()
{
char c = 'a';
is_enum(c);
is_enum(A);
is_enum(B);
return 0;
}
Will issue a warning about the unused variable if you get your compiler in the right state. With gcc and '-Wall', I get this sort of thing:
thing.cpp: In function 'typename boost::enable_if_c<(! boost::is_enum::value), void>::type is_enum(T) [with T = char]':
thing.cpp:21: instantiated from here
thing.cpp:15: warning: unused variable 'NOT_AN_ENUMERATION'
The error is intentional, since you call a static assert, which means exactly "please trigger a compile-time error if the condition is false".
Your function is strangely named, though: It's not a conditional check whether the variable is an enum, but rather an assertion that it is. You should call it assert_that_var_is_enum_or_die() or something meaningful like that.
A conditional should probably just be:
inline bool is_enum(T) { return boost::is_enum<T>::value; }
If you are using boost, you can use BOOST_STATIC_WARNING defined in header <boost/serialization/static_warning.hpp>. If that does not work on your compiler you can use BOOST_SERIALIZATION_BSW defined in same header. Example code looks like BOOST_SERIALIZATION_BSW(std::is_enum<T>::value,1); where the second parameter is an unique integer. The implementation is conceptually same as BOOST_STATIC_ASSERT except that it uses compiler specific warning such as "negative integer to unsigned conversion" for generation purpose.