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

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

Related

narrowing conversion from 'int' to 'SHORT [duplicate]

I learnt about curly-brace-delimited initializer in The C++ Programming Language, 4th ed. > Chapter 2: A Tour of C++: The Basics.
I am quoting from the book below.
The = form is traditional and dates back to C, but if in doubt, use the general {} -list form (§6.3.5.2).
If nothing else, it saves you from conversions that lose information (narrowing conversions; §10.5):
int i1 = 7.2; // i1 becomes 7
int i2 {7.2}; // error : floating-point to integer conversion
int i3 = {7.2}; // error : floating-point to integer conversion (the = is redundant)
However, I am unable to reproduce these results.
I have the following code.
#include <iostream>
int main()
{
int i1 = 7.2;
int i2 {7.2};
int i3 = {7.2};
std::cout << i1 << "\n";
std::cout << i2 << "\n";
std::cout << i3 << "\n";
}
When I compile and run it, I don't get any error. I get a warning about std=c++11 but no error.
$ g++ init.cpp
init.cpp: In function ‘int main()’:
init.cpp:6:12: warning: extended initializer lists only available with -std=c++11 or -std=gnu++11
int i2 {7.2};
^
$ ./a.out
7
7
7
Further, the warning is only for the second assignment but there is no warning for the third assignment. This seems to indicate that the = is not really redundant as mentioned in the book. If = were redundant, either both the second and third assignments would have produced warnings or both would not have produced warnings.
Then I compile them with the -std=c++11 flag.
$ g++ -std=c++11 init.cpp
init.cpp: In function ‘int main()’:
init.cpp:6:16: warning: narrowing conversion of ‘7.2000000000000002e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
int i2 {7.2};
^
init.cpp:7:18: warning: narrowing conversion of ‘7.2000000000000002e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
int i3 = {7.2};
^
$ ./a.out
7
7
7
Still no error. Only warnings. Although in this case the second and third assignments behave identically with respect to generating warnings.
So my question is: Although the book mentions that the second and third assignments are errors, why doesn't this code fail to compile?
This is ill-formed and there should be diagnostic, however it can either be a warning(which you received) or an error. gcc made this a warning for several versions due to porting issue from C++03:
The standard only requires that "a conforming implementation shall issue at least one diagnostic message" so compiling the program with a warning is allowed. As Andrew said, -Werror=narrowing allows you to make it an error if you want.
G++ 4.6 gave an error but it was changed to a warning intentionally for 4.7 because many people (myself included) found that narrowing conversions where one of the most commonly encountered problems when trying to compile large C++03 codebases as C++11. Previously well-formed code such as char c[] = { i, 0 }; (where i will only ever be within the range of char) caused errors and had to be changed to char c[] = { (char)i, 0 }
but now recent versions of gcc and clang make this an error, see it live for gcc.
For reference the draft C++11 standard section 8.5.4 [dcl.init.list] says:
Otherwise, if the initializer list has a single element, the object or
reference is initialized from that element; if a narrowing conversion
(see below) is required to convert the element to T, the program is
ill-formed. [ Example:
int x1 {2}; // OK
int x2 {2.0}; // error: narrowing
—end example ]
and:
A narrowing conversion is an implicit conversion
from a floating-point type to an integer type, or
[...]
[ Note: As indicated above, such conversions are not allowed at the top level in list-initializations.—end
note ] [ Example:
[...]
int ii = {2.0}; // error: narrows
[...]
So a floating point to integer conversion is a narrowing conversion and is ill-formed.
and section 1.4 Implementation compliance [intro.compliance] says:
Although this International Standard states only requirements on C++ implementations, those requirements
are often easier to understand if they are phrased as requirements on programs, parts of programs, or
execution of programs. Such requirements have the following meaning:
[...]
If a program contains a violation of any diagnosable rule or an occurrence of a construct described in
this Standard as “conditionally-supported” when the implementation does not support that construct,
a conforming implementation shall issue at least one diagnostic message.
[...]
Tells us that only a diagnostic is required.
C++ language does not distinguish "warnings" from "errors". C++ only has diagnostic messages. The warnings you received are diagnostic messages. The language specification does not require compilers to stop compilation when they encounter erroneous (aka ill-formed) code. All compilers have to do is issue a diagnostic message, and then they can continue compiling, if they so desire.
This means that in general case it is your responsibility to tell warnings that are "just warnings" from warnings that actually indicate genuine errors, especially with such permissive compilers as GCC.
This also means that the actual real-life behavior is a matter of your compiler setup. Ask your compiler to be more restrictive in that regard, if possible. In GCC you might try -pedantic-errors switch for that purpose.
P.S. In my experiments with GCC, -std=c++11 is sufficient to make it generate errors for your code. If you are getting warnings instead, it could be a matter of compiler version.

gcc size_t and sizeof arithmetic conversion to int

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.

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.

Why doesn't narrowing conversion used with curly-brace-delimited initializer cause an error?

I learnt about curly-brace-delimited initializer in The C++ Programming Language, 4th ed. > Chapter 2: A Tour of C++: The Basics.
I am quoting from the book below.
The = form is traditional and dates back to C, but if in doubt, use the general {} -list form (§6.3.5.2).
If nothing else, it saves you from conversions that lose information (narrowing conversions; §10.5):
int i1 = 7.2; // i1 becomes 7
int i2 {7.2}; // error : floating-point to integer conversion
int i3 = {7.2}; // error : floating-point to integer conversion (the = is redundant)
However, I am unable to reproduce these results.
I have the following code.
#include <iostream>
int main()
{
int i1 = 7.2;
int i2 {7.2};
int i3 = {7.2};
std::cout << i1 << "\n";
std::cout << i2 << "\n";
std::cout << i3 << "\n";
}
When I compile and run it, I don't get any error. I get a warning about std=c++11 but no error.
$ g++ init.cpp
init.cpp: In function ‘int main()’:
init.cpp:6:12: warning: extended initializer lists only available with -std=c++11 or -std=gnu++11
int i2 {7.2};
^
$ ./a.out
7
7
7
Further, the warning is only for the second assignment but there is no warning for the third assignment. This seems to indicate that the = is not really redundant as mentioned in the book. If = were redundant, either both the second and third assignments would have produced warnings or both would not have produced warnings.
Then I compile them with the -std=c++11 flag.
$ g++ -std=c++11 init.cpp
init.cpp: In function ‘int main()’:
init.cpp:6:16: warning: narrowing conversion of ‘7.2000000000000002e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
int i2 {7.2};
^
init.cpp:7:18: warning: narrowing conversion of ‘7.2000000000000002e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
int i3 = {7.2};
^
$ ./a.out
7
7
7
Still no error. Only warnings. Although in this case the second and third assignments behave identically with respect to generating warnings.
So my question is: Although the book mentions that the second and third assignments are errors, why doesn't this code fail to compile?
This is ill-formed and there should be diagnostic, however it can either be a warning(which you received) or an error. gcc made this a warning for several versions due to porting issue from C++03:
The standard only requires that "a conforming implementation shall issue at least one diagnostic message" so compiling the program with a warning is allowed. As Andrew said, -Werror=narrowing allows you to make it an error if you want.
G++ 4.6 gave an error but it was changed to a warning intentionally for 4.7 because many people (myself included) found that narrowing conversions where one of the most commonly encountered problems when trying to compile large C++03 codebases as C++11. Previously well-formed code such as char c[] = { i, 0 }; (where i will only ever be within the range of char) caused errors and had to be changed to char c[] = { (char)i, 0 }
but now recent versions of gcc and clang make this an error, see it live for gcc.
For reference the draft C++11 standard section 8.5.4 [dcl.init.list] says:
Otherwise, if the initializer list has a single element, the object or
reference is initialized from that element; if a narrowing conversion
(see below) is required to convert the element to T, the program is
ill-formed. [ Example:
int x1 {2}; // OK
int x2 {2.0}; // error: narrowing
—end example ]
and:
A narrowing conversion is an implicit conversion
from a floating-point type to an integer type, or
[...]
[ Note: As indicated above, such conversions are not allowed at the top level in list-initializations.—end
note ] [ Example:
[...]
int ii = {2.0}; // error: narrows
[...]
So a floating point to integer conversion is a narrowing conversion and is ill-formed.
and section 1.4 Implementation compliance [intro.compliance] says:
Although this International Standard states only requirements on C++ implementations, those requirements
are often easier to understand if they are phrased as requirements on programs, parts of programs, or
execution of programs. Such requirements have the following meaning:
[...]
If a program contains a violation of any diagnosable rule or an occurrence of a construct described in
this Standard as “conditionally-supported” when the implementation does not support that construct,
a conforming implementation shall issue at least one diagnostic message.
[...]
Tells us that only a diagnostic is required.
C++ language does not distinguish "warnings" from "errors". C++ only has diagnostic messages. The warnings you received are diagnostic messages. The language specification does not require compilers to stop compilation when they encounter erroneous (aka ill-formed) code. All compilers have to do is issue a diagnostic message, and then they can continue compiling, if they so desire.
This means that in general case it is your responsibility to tell warnings that are "just warnings" from warnings that actually indicate genuine errors, especially with such permissive compilers as GCC.
This also means that the actual real-life behavior is a matter of your compiler setup. Ask your compiler to be more restrictive in that regard, if possible. In GCC you might try -pedantic-errors switch for that purpose.
P.S. In my experiments with GCC, -std=c++11 is sufficient to make it generate errors for your code. If you are getting warnings instead, it could be a matter of compiler version.

C question: no warning?

main()
{
printf("Hello World.");
}
Why does no warning is produced in gcc compiler even though we declare main() with return type 'int'
Because you're not using the -Wall flag. When you do, you should get:
foo.c:1: warning: return type defaults to ‘int’
foo.c: In function ‘main’:
foo.c:1: warning: implicit declaration of function ‘printf’
foo.c:1: warning: incompatible implicit declaration of built-in function ‘printf’
foo.c:1: warning: control reaches end of non-void function
Did you forget to compile with warnings enabled:
gcc -Wall ...
Your main function return nothing. so modify in void main().
Usually is:
int main() { printf("Hello world"); return 0; }
No warning is produced because that's legal ANSI C89. Functions without a specified return type are implicitly assumed to return int.
If you want to compile as C89, but be warned about using implicit int, you should pass either -Wimplicit-int as a command line argument (or -Wall, which enables that warning, along with a number of others).
If you want to compile as C99, you should pass -std=c99 and -pedantic-errors, which will cause the compiler to issue an error if you use implicit int.