I am trying to use C++17 if constexpr feature but fail to compile a simple function.
Code:
template <auto B>
int foo()
{
if constexpr(B)
{
return 1;
}
else
{
return 2;
}
} // <- I get an error here
int main()
{
return foo<false>();
}
The error output by compiler:
<source>(12): error #1011: missing return statement at end of non-void function "foo<B>() [with B=false]"
}
Used -std=c++17 -O3 -Wall -Werror compiler flags and icc 19.0.1 compiler.
Is this valid C++17 code?
What is the reason behind this error?
Is this valid C++17 code?
Yes, it's valid. Exactly one return statement will be discarded, while the other will remain. Even if none remain, C++ still allows you to omit a return statement from a function. You get undefined behavior if the function's closing curly brace is reached, but that's a risk only if execution reaches that point.
In your case, execution cannot reach such a point, so UB is not possible.
What is the reason behind this error?
You used -Werror, thus turning the compiler's false positive warning into a hard error. One workaround is to disable this warning around that particular function. This is purely a quality of implementation problem.
Related
Take the following minimum example:
#include <stdio.h>
bool test(){
for (int i = 0; i < 1024; i++)
{
printf("i=%d\n", i);
}
}
int main(){
test();
return 0;
}
where the return statement in the test function is missing. If I run the example like so:
g++ main.cpp -o main && ./main
Then the loop aborts after 1024 iterations. However, if I run the example with optimizations turned on:
g++ -O3 main.cpp -o main && ./main
Then this is optimized and I get an endless loop.
This behavior is consistent across g++ version 10.3.1 and clang++ version 10.0.1. The endless loop does not occur if I add a return statement or change the return type of the function to void.
I am curious: Is this something one would consider a compiler bug? Or is this acceptable, since a missing return statement is undefined behavior and thus we lose all guarantees about what happens in this function?
Your function is declared as bool test(), but your definition never returns anything. That means you've broken the contract with the language and have be put in a time out in undefined behavior land. There, all results are "correct".
You can think of undefined behavior as: It is not defined what output the compiler produces when asked to compile your code.
Actually the "undefined" refers to the observable behavior of the program the compiler creates from your code, but it boils down to the same.
This is not a compiler bug.
You asked the compiler to return a bool from a function without returning a bool from the function. There is simply no way the compiler can do that right, and that isn't the compilers fault.
Found strange behavior that i don't understand:
std::vector<std::string> subdomainVisits(std::vector<std::string> &cpdomains)
{
// return std::vector<std::string>();
}
int main(int argc, char const *argv[])
{
std::vector<std::string> data = { "9001 discuss.leetcode.com" };
auto result = subdomainVisits(data);
return 0;
}
In this case commented return in subdomainVisits function causes Segmentation fault(use gcc version 7.3.0 (Debian 7.3.0-19) ). Uncommenting fix this problem.
Why it happens?
The behaviour of your program as written is undefined.
A non-void function must have an explicit return value on all control paths.
The only exception to this is main, which has an implicit return 0;.
A fair number of compilers will warn you of trivial cases such as the above. Do you not have the warning level set high enough? (Pass -Wall and -Wextra to "turn up" the warning level on gcc.)
Note that the C++ standard does not require a compiler to fail compilation: theoretical computer science (the halting problem) tells us that reachability is impossible to prove.
Is it possible to enforce an error (to break the build) when a function definition has no return in its body?
Consider this function:
int sum(int a, int b) {
int c = a + b;
// And here should be return
};
When I compile with g++ -Wall, I get:
no return statement in function returning non-void [-Wreturn-type]
But I want this to be a hard error rather than a warning.
I'm currently using GCC 4.9.2, but if there's a solution for different version of GCC that would be helpful to know, too.
GCC has the option -Werror to turn all warnings into errors.
If you want to upgrade only a specific warning, you can use -Werror=X, where X is the warning type, without the -W prefix. In your particular case, that would be -Werror=return-type.
Note that this will only report a missing return in a function that returns a value. If you want to enforce that return; must be explicitly written in a function returning void, you may be out of luck.
The following is nonsensical yet compiles cleanly with g++ -Wall -Wextra -Werror -Winit-self (I tested GCC 4.7.2 and 4.9.0):
#include <iostream>
#include <string>
int main()
{
for (int ii = 0; ii < 1; ++ii)
{
const std::string& str = str; // !!
std::cout << str << std::endl;
}
}
The line marked !! results in undefined behavior, yet is not diagnosed by GCC. However, commenting out the for line makes GCC complain:
error: ‘str’ is used uninitialized in this function [-Werror=uninitialized]
I would like to know: why is GCC so easily fooled here? When the code is not in a loop, GCC knows that it is wrong. But put the same code in a simple loop and GCC doesn't understand anymore. This bothers me because we rely quite a lot on the compiler to notify us when we make silly mistakes in C++, yet it fails for a seemingly trivial case.
Bonus trivia:
If you change std::string to int and turn on optimization, GCC will diagnose the error even with the loop.
If you build the broken code with -O3, GCC literally calls the ostream insert function with a null pointer for the string argument. If you thought you were safe from null references if you didn't do any unsafe casting, think again.
I have filed a GCC bug for this: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63203 - I'd still like to get a better understanding here of what went wrong and how it may impact the reliability of similar diagnostics.
I'd still like to get a better understanding here of what went wrong and how it may impact the reliability of similar diagnostics.
Unlike Clang, GCC doesn't have logic to detect self-initialized references, so getting a warning here relies on the code for detecting use of uninitialized variables, which is quite temperamental and unreliable (see Better Uninitialized Warnings for discussion).
With an int the compiler can figure out that you write an uninitialized int to the stream, but with a std::string there are apparently too many layers of abstraction between an expression of type std::string and getting the const char* it contains, and GCC fails to detect the problem.
e.g. GCC does give a warning for a simpler example with less code between the declaration and use of the variable, as long as you enable some optimization:
extern "C" int printf(const char*, ...);
struct string {
string() : data(99) { }
int data;
void print() const { printf("%d\n", data); }
};
int main()
{
for (int ii = 0; ii < 1; ++ii)
{
const string& str = str; // !!
str.print();
}
}
d.cc: In function ‘int main()’:
d.cc:6:43: warning: ‘str’ is used uninitialized in this function [-Wuninitialized]
void print() const { printf("%d\n", data); }
^
d.cc:13:19: note: ‘str’ was declared here
const string& str = str; // !!
^
I suspect this kind of missing diagnostic is only likely to affect a handful of diagnostics which rely on heuristics to detect problems. These would be the ones that give a warning of the form "may be used uninitialized" or "may violate strict aliasing rules", and probably the "array subscript is above array bounds" warning. Those warnings are not 100% accurate and "complicated" logic like loops(!) can cause the compiler to give up trying to analyse the code and fail to give a diagnostic.
IMHO the solution would be to add checking for self-initialized references at the point of initialization, and not rely on detecting it is uninitialized later when it gets used.
You claim it's undefined behavior, but when I compile the two cases to assembly, I definitely see the function-scoped variable not being initialized on the stack, and the block-scoped variable getting set to NULL.
That's as much of an answer as you're getting from me. I downloaded the C++ spec to definitively settle this, but fell into a Lovecraftian-type fugue when I gazed upon it, to preserve my fragile sanity...
I strongly suspect the block-scoped case is not actually undefined.
lint produces some warning like:
foo.c XXX Warning 534: Ignoring return value of function bar()
From the lint manual
534 Ignoring return value of function
'Symbol' (compare with Location) A
function that returns a value is
called just for side effects as, for
example, in a statement by itself or
the left-hand side of a comma
operator. Try: (void) function(); to
call a function and ignore its return
value. See also the fvr, fvo and fdr
flags in §5.5 "Flag Options".
I want to get this warning, if there exists any, during compilation. Is there any option in gcc/g++ to achieve this? I had turned on -Wall but that apparently did not detect this.
Since C++17 you can use the [[nodiscard]] attribute.
Example:
[[nodiscard]] int bar() {
return 42;
}
Thanks to WhirlWind and paxdiablo for the answer and comment. Here is my attempt to put the pieces together into a complete (?) answer.
-Wunused-result is the relevant gcc option. And it is turned on by default. Quoting from gcc warning options page:
-Wno-unused-result
Do not warn if a caller of a function marked with attribute warn_unused_result (see
Variable Attributes) does not use its return value. The default is -Wunused-result
So, the solution is to apply the warn_unused_result attribute on the function.
Here is a full example. The contents of the file unused_result.c
int foo() { return 3; }
int bar() __attribute__((warn_unused_result));
int bar() { return 5; }
int main()
{
foo();
bar(); /* line 9 */
return 0;
}
and corresponding compilation result:
$gcc unused_result.c
unused_result.c: In function ‘main’:
unused_result.c:9: warning: ignoring return value of ‘bar’, declared with attribute warn_unused_result
Note again that it is not necessary to have -Wunused-result since it is default. One may be tempted to explicitly mention it to communicate the intent. Though that is a noble intent, but after analyzing the situation, my choice, however, would be against that. Because, having -Wunused-result in the compile options may generate a false sense of security/satisfaction which is not true unless the all the functions in the code base are qualified with warn_unused_result.
-Wunused-result should do this for you. This isn't one of the warnings -Wall turns on:
http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
The function has to have the warn_unused_result attribute applied to it (Thanks paxdiablo).
The answers about using __attribute__((warn_unused_result)) are correct. GCC isn't so good at this functionality, though! Be aware: it will not warn for non-POD types. That means, for example, if you return a class with a destructor (or a class with instance variables with destructors) you'll never see a warning about ignoring the result.
Relevant bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66177
Example where it fails:
struct Error {
~Error();
};
__attribute__((warn_unused_result)) Error test();
int main()
{
test();
return 0;
}
So, don't rely on this for return types which aren't pretty simple.
I solved the problem like this:
#define ignore_result(x) if (x) {}
then instead of (void)foo() use ignore_result(foo())
Then the code compiles with -Wall just fine.