I have a function which shows some message, properly finishes the program and finally calls exit(-1);. Here is an example of usage:
Data SomeFunction()
{
Data data;
if (some_condition) {
// filling the data
return data;
}
ShowErrorAndExit("Some message");
//return data - I don't want it here!
}
The problem is, the compiler says:
warning: control reaches end of non-void function
However, this is not true, because the end of this function will never be reached. I don't want to add an unneeded return at the end of function; what else can I do?
ShowErrorAndExit should be labeled [[noreturn]]. This will remove the warning.
Example:
[[noreturn]] void ShowErrorAndExit(std::string message) {...}
Invert the logic:
Data SomeFunction() {
if (!some_condition)
ShowErrorAndExit("Some message");
Data data;
// filling in the data
return data;
}
You can (and should) declare your ShowErrorAndExit function with the [[noreturn]] attribute (link). This will allow the compiler (if it's reasonably sane) to understand that your function never reaches the control point for which it issues the warning.
Something like this:
[[ noreturn ]] void ShowErrorAndExit(const char* message);
You may trick the compiler to think the return is always reached:
Data SomeFunction()
{
Data data;
if (! some_condition)
ShowErrorAndExit("Some message");
// fill the data
return data;
}
You can ignore the warning or, if your compiler provides a way to do so, disable it. Another option is to put a throw at the end of the function with a comment indicating that it's never supposed to execute.
You can use std::optional, it lets you return your type Data or nothing at all a.k.a.std::nullopt.
Look here: https://en.cppreference.com/w/cpp/utility/optional
Related
I am writing a very simple compiler where users are allowed to define functions that return either void, int or char. However, users' function may be malformed. They may not return a value for a function that does not return void, or return a value for a function that returns void as declared. Currently my compiler is unable to detect this kind of errors and fails to generate proper code for function that returns void as this class of functions can return without a return; (they return implicitly). These two problems have cost me quite some time to phrase them out clearly. See the example code below:
// Problem A: detect implicit return.
void Foo(int Arg) {
if (Arg)
return;
else {
Arg = 1;
// Foo returns here! How can I know!
}
}
// Problem B: detect "forgotten return".
int Bar(int Arg) {
if (Arg > 1) {
return 1;
}
// this is an error: control flow reaches end at non-void function!
// How can I know!
}
I think the more general question may be: how can I tell the control flow reaches end at some point in a function? By saying reach end I mean the it reaches a point after which the function has no code to execute. If I can detect the end of control flow, I can look for a return at this point and either report an error if the function ought to return something or generate an explicit return for a void function. If I enumerate all such points of a function, I can ensure that the function is fully checked or complemented.
I see this problem as a well-solved one in compiler engineering since modern C/C++ can do that pretty well. Is LLVM can offer any API to do this? Or is there simple algorithm to achieve this? Thanks very much.
Edit: I am currently using LLVM and have BasicBlock emitted already. I hope a guide in doing this in LLVM specifically.
Edit: In this question we assume that the return type declared in the function prototype always matches that of its return stmt. I primarily focus on the absence of a required return.
The answer is simple. After all BB's of a function are emitted, loop over them and pick up those ends without a Terminator (see the llvm document for what is a Terminator Instruction). Assuming the emission of all kinds of control flow statements (While, For, etc.) follows the rule (One BB is ended by one and only one Terminator), the only possible explanation of these rule-breakers is that they miss a Return IR in the end. If the current function return void, append a ReturnVoid to them. Otherwise, this is an error, report it.
The reasoning is largely correct as it rely on the well-formed property of LLVM's BB and it is easy to implement, cheap to run. Here is the code:
/// Generate body for a Function.
void visitFuncDef(FuncDef *FD) {
// Unrelated code omitted...
/// Generate the body
for (Stmt *S : FD->stmts) {
visitStmt(S);
}
/// Check for well-formness of all BBs. In particular, look for
/// any unterminated BB and try to add a Return to it.
for (BasicBlock &BB : *Fn) {
Instruction *Terminator = BB.getTerminator();
if (Terminator != nullptr) continue; /// Well-formed
if (Fn->getReturnType()->isVoidTy()) {
/// Make implicit return of void Function explicit.
Builder.SetInsertPoint(&BB);
Builder.CreateRetVoid();
} else {
// How to attach source location?
EM.Error("control flow reaches end of non-void function");
// No source location, make errors short
return;
}
}
/// Verify the function body
String ErrorMsg;
llvm::raw_string_ostream OS(ErrorMsg);
if (llvm::verifyFunction(*Fn, &OS)) {
EM.Error(ErrorMsg);
}
}
I wish to understand what fetch().text and assert(0) do below. I am not familiar with a function like fetch() that can refers to a member of the return type, i.e. fetch().text. Is this somehow enabled by the use of assert(0)?
class SimpleS{
struct internal_element {
const char *text;
};
class SimpleE {
public:
SimpleE() {
}
const char* text() const {
return fetch().text;
}
void set_text(const char *text) {
fetch().text = text;
}
private:
internal_element& fetch() const {
... // some code
assert(0);
}
}
The assertion has nothing to do with it. What's happening here is that fetch() returns a reference to an internal_element. That enables you to refer to members of that struct in the returned value:
fetch().text
refers to the internal_element::text member of the internal_element object returned by fetch().
As to why there's an assert(0) in there, no idea. You didn't give us the code. Usually, when a function ends with such an assert is because the programmer wants to catch cases where he didn't cover some possibility. For example:
if (condition)
//...
else if (condition)
//...
// We should have covered all possible conditions above and already
// returned. If we actually get here, then we did something wrong.
assert(0);
The assert(0); will always fail. I suppose its purpose is to make sure that this method is never called in the first place.
(Assuming, of course, there is no condition for the assert(0); being run)
If the ... // some code in your code has a conditional return, assert(0) will never be called. assert(0) basically causes an exception and the program will die if the exception is not being caught in the calling function.
return fetch().text
calls the function fetch. This presumably returns an object of class internal_element. It then accesses the text member of this returned object and returns it. It's effectively equivalen to:
internal_element temp = fetch();
return temp.text;
In below code (which you shared), programmer got condition which make function return, thats what the // some code supposed to do...but if one of the the conditions is not true (and it return) control reaches end of function it means there is fatal error, so is the purpose of assert(0) at the end of function...
internal_element& fetch() const {
... // some code
assert(0);
}
Say I have the following function:
Thingy& getThingy(int id)
{
for ( int i = 0; i < something(); ++i )
{
// normal execution guarantees that the Thingy we're looking for exists
if ( thingyArray[i].id == id )
return thingyArray[i];
}
// If we got this far, then something went horribly wrong and we can't recover.
// This function terminates the program.
fatalError("The sky is falling!");
// Execution will never reach this point.
}
Compilers will typically complain at this, saying that "not all control paths return a value". Which is technically true, but the control paths that don't return a value abort the program before the function ends, and are therefore semantically correct. Is there a way to tell the compiler (VS2010 in my case, but I'm curious about others as well) that a certain control path is to be ignored for the purposes of this check, without suppressing the warning completely or returning a nonsensical dummy value at the end of the function?
You can annotate the function fatalError (its declaration) to let the compiler know it will never return.
In C++11, this would be something like:
[[noreturn]] void fatalError(std::string const&);
Pre C++11, you have compiler specific attributes, such as GCC's:
void fatalError(std::string const&) __attribute__((noreturn));
or Visual Studio's:
__declspec(noreturn) void fatalError(std::string const&);
Why don't you throw an exception? That would solve the problem and it would force the calling method to deal with the exception.
If you did manage to haggle the warning out some way or other, you are still left with having to do something with the function that calls getThingy(). What happens when getThingy() fails? How will the caller know? What you have here is an exception (conceptually) and your design should reflect that.
You can use a run time assertion in lieu of your fatalError routine. This would just look like:
Thingy& getThingy(int id)
{
for ( int i = 0; i < something(); ++i )
{
if ( thingyArray[i].id == id )
return thingyArray[i];
}
// Clean up and error condition reporting go here.
assert(false);
}
I have a logger class. Call it MyLogger. I may use it in a function like this:
void MyFunc(MyLogger& oLogger)
{
//Do stuff
oLogger.Log("In MyFunc : Some Error");
//Do something else
oLogger.Log("In MyFunc : Some other error");
}
Now, I want to prepend "In MyFunc" to the logs if the log comes from inside MyFunc. Similarly for other functions...
Because this is tiresome, I tried something like this:
void MyLogger::PushPrependString(const char*)
{
//Store prepend string in stack and set it as current prepend string.
}
void MyLogger::PopPrependString()
{
//Pop the most recent prepend string.
}
Now, I can use these two functions like this:
void MyFunc(MyLogger& oLogger)
{
oLogger.PushPrependString("In MyFunc : ");
//Do stuff
oLogger.Log("Some Error");
//Do something else
oLogger.Log("Some other error");
oLogger.PopPrependString();
}
The trouble is, if there are multiple returns in a function, this becomes ugly. Is there any way around this? Is this a common problem? Is there any preprocessor macro like __FILE__ or __LINE__ for getting the name of the function a line appears in? Any comments would be appreciated. Thanks.
"The trouble is, if there are multiple returns in a function, this becomes ugly. Is there any way around this?"
Yes, just use an object with constructor (calls PushPrependString) and destructor (calls PopPrependString).
class LogPrefix
{
private:
MyLogger* logger_;
LogPrefix( LogPrefix const& ); // No such.
LogPrefix& operator=( LogPrefix const& ); // No such.
public:
LogPrefix( MyLogger& logger, char const s[] )
: logger_( &logger )
{
logger_->PushPrependString( s );
}
~LogPrefix()
{
logger_->PopPrependString();
}
};
Disclaimer: off the cuff code, not touched by compiler's hands...
"Is this a common problem?"
Yes.
"Is there any preprocessor macro like FILE or LINE for getting the name of the function a line appears in?"
Not in C++98. Various compilers offer various extensions that do that. IIRC C++0x adopts the C99 scheme, which unfortunately just provides static strings.
Cheers & hth.
RAII - Resource Acquisition Is Initialization.
In this case, you create an object on entry to the function that identifies the current function to the logging system; when the function exits (by any return or by exception thrown or by exception not caught), the object will be destroyed, and the destructor changes what is printed in future by the logging system.
In C99, and maybe in some C++ compilers such as G++, there is a predefined variable, __func__ containing the function name. The C++ equivalent is more complex, I believe.
I have to return to the previous level of the recursion. is the syntax like below right?
void f()
{
// some code here
//
return;
}
Yes, you can return from a void function.
Interestingly, you can also return void from a void function. For example:
void foo()
{
return void();
}
As expected, this is the same as a plain return;. It may seem esoteric, but the reason is for template consistency:
template<class T>
T default_value()
{
return T();
}
Here, default_value returns a default-constructed object of type T, and because of the ability to return void, it works even when T = void.
Sure. You just shouldn't be returning an actual value.
Yes, you can use that code to return from the function. (I have to be very verbose here to make Stack Overflow not say that my answer is too short)
Yes, that will return from the function to the previous level of recursion. This is going to be very basic explanation, but when you call a function you are creating a new call stack. In a recursive function you are simply adding to that call stack. By returning from a function, whether you return a value or not, you are moving the stack pointer back to the previous function on the stack. It's sort of like a stack of plates. You keep putting plates on it, but than returning moves the top plate.
You could also verify this by using a debugger. Just put a few break points in your code and step through it. You can verify yourself that it works.
The simple answer to this is YES! C++ recognise void method as a function with no return. It basically tells the compiler that whatever happens, once you see the return; break and leave the method....
Yes, sometimes you may wish to return void() instead of just nothing.
Consider a void function that wants to call some pass-through void functions without a bunch of if-else.
return
InputEvent == E_Pressed ? Controller->Grip() :
InputEvent == E_Released ? Controller->Release() :
InputEvent == E_Touched ? Controller->Touch() : void();
You shouldn't have to have the return there, the program will return to the previous function by itself, go into debug mode and step through and you can see it yourself.
On the other hand i don't think having a return there will harm the program at all.
As everyone else said, yes you can. In this example, return is not necessary and questionably serves a purpose. I think what you are referring to is an early return in the middle of a function. You can do that too however it is bad programming practice because it leads to complicated control flow (not single-entry single-exit), along with statements like break. Instead, just skip over the remainder of the function using conditionals like if/else().