-6

I've started using CLang recently to compile embedded C++ ARM programs.

Prior to this I used GCC and C, almost exclusively for embedded work.

I've noticed that when I have a method that returns a value, and I forget the return statement, the program core dumps. There is no error printed other than "msleep error -1" from one of my device drivers. This is on FreeBSD.

I would expect that forgetting the return statement would just result in garbage being returned from the function, not a core dump.

EDIT: I'm returning a bool, not a pointer or object or anything complicated. The program crashes even when the return value doesn't matter.

What is going on?

E.G.:

bool MyClass::DummyFunc() {
  <do some stuff and forget the return value>     
}

Elsewhere:

if(pMyObj->DummyFunc()) {
  print ("Hey, it's true!\n");
} else {
  print ("Darn, it's false!\n");
}

That code should not crash, regardless of the return value.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
NXT
  • 1,855
  • 2
  • 18
  • 36
  • 2
    You should consider compiling with -Wall or -Wreturn-type if you are prone to making this kind of mistake. – kfsone Sep 04 '16 at 04:07

3 Answers3

5

From second hand sources because I don't want to pay for the C++ standard, it apparently says:

Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function.

This is C++, when you do something undefined you should expect it to crash your computer and eat your laundry, unless the implementation says otherwise.

Trevor Merrifield
  • 4,541
  • 2
  • 21
  • 24
  • 1
    You can use N4140 or N3936, which are freely available documents and identical to the published standard apart from minor editorial changes and typo corrections – M.M Sep 04 '16 at 00:26
  • 2
    @NXT From that remark I'm not sure whether you are in denial about the meaning of undefined behavior, or if you are just curious about what makes this happen in the bowels of Clang. If it's the latter you might have better luck opening a new question that makes this intention clear, with steps to reproduce the behavior on our own systems. – Trevor Merrifield Sep 04 '16 at 02:08
4

I would expect that forgetting the return statement would just result in garbage being returned from the function, not a core dump.

What is going on?

Your expectation is wrong, is what's going on.

The compiler — an incredibly complicated piece of machinery — is free to assume that your function returns a value, because you promised that it did.

Then you broke that promise.

This is why we shall not make assumptions about implementation details, particularly when writing programs whose behaviour is undefined. You cannot make a direct comparison between C++ code and "some data goes into this register and there is this call stack that does precisely this thing".

There are near-infinite ways that this can break, because in the process of converting your C++ code to a well-optimised program readable by your computer, the compiler makes a series of extremely complicated optimisations, and any one of them can be snapped in half by your violation of the standard's rules. Attempting to determine exactly what happened on any particular run of your program would require the following knowledge:

  • CPU make, model and version
  • operating system make, model and version
  • compiler make, model and version
  • all compiler flags
  • 10 years' experience with the compiler's source code
  • time machine to inspect the state of every bit of memory in your computer at the time of your program's execution.

It's really just not worth it.

Fortunately, in certain cases, we can additionally rationalise about this case without betraying the abstraction: for example, consider what happens when you promised you'd return a std::string, but didn't, and then that non-existent std::string goes out of scope. The destructor is being invoked on random nonsense, and that's not going to go well.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • I'm returning a bool. It shouldn't matter what the value actually is. – NXT Sep 04 '16 at 00:40
  • 1
    @NXT: There is no value. You didn't return anything. – Lightness Races in Orbit Sep 04 '16 at 00:40
  • @NXT Your assumptions are faulty – M.M Sep 04 '16 at 00:41
  • *something* was left in the accumulator, or however they do it on ARM. It shouldn't matter what. – NXT Sep 04 '16 at 00:41
  • 2
    @NXT: You're doing it again! Doesn't matter what's "in the accumulator", or whether anything's "in the accumulator", or whether an "accumulator" even exists on the platform in question. Your assumption that "it shouldn't matter what" is utterly without basis or support of fact. You are not programming a CPU; you are writing a C++ program. C++ is an abstraction. You broke the rules of that abstraction. Period. – Lightness Races in Orbit Sep 04 '16 at 00:45
4

Forgetting the return value results in the control flow reaching the end of a function. The C and C++ standards both describe this case. Note that main is an exception to this case and is described separately.

Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function C++11 Draft pg 136

My experience with clang has been that the compiler will treat instances of "undefined behavior" as will not happen and optimize away the paths. Which is legal, since the behavior is undefined. Often clang will instead emit an illegal instruction along the omitted path so that the code will crash if the "impossible" happens, which is likely your case here. In fact, the compiler could then determine that calling DummyFunc() results in undefined behavior and therefore cannot happen, and instead start optimizing away the calling bodies.

gcc is far "friendlier" and tries to generate something nice, such as returning 0.

Note, both compilers are correct and are producing valid code according to the standard.

Brian
  • 2,693
  • 5
  • 26
  • 27
  • That's an *interesting* design choice... That might explain the odd backtraces I saw in GDB. Thanks. – NXT Sep 04 '16 at 12:11
  • LLVM has a series of blog posts about this choice - http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html. – Brian Sep 04 '16 at 12:29
  • I don't see how it's "_interesting_" if by "_interesting_" you mean somehow unusual or bizarre. Again, you need to take a course in abstract languages, because you are fundamentally misunderstanding what it means to compile a program written in one. – Lightness Races in Orbit Sep 04 '16 at 13:41
  • @Brian, those articles were really interesting, thank you. – NXT Sep 04 '16 at 17:34