240

[dcl.attr.noreturn] provides the following example:

[[ noreturn ]] void f() {
    throw "error";
    // OK
}

but I do not understand what is the point of [[noreturn]], because the return type of the function is already void.

So, what is the point of the noreturn attribute? How is it supposed to be used?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • 2
    What is so important about this kind of funciton (that will most likely happen once in a program execution) that deserves such attention? Isn't this an easily detectable situation? – user666412 Aug 26 '15 at 23:01
  • 3
    @MrLister The OP's conflating the concepts of “returning” and “return value”.  Given how they're nearly always used in tandem, I think the confusion is justified. – Slipp D. Thompson Jun 24 '16 at 05:55
  • Good question and answers. But I Love your reference to: https://stackoverflow.com/questions/10538291/what-is-the-point-of-noreturn – QT-1 Jun 28 '22 at 00:22

5 Answers5

278

The [[noreturn]] attribute is supposed to be used for functions that don't return to the caller. That doesn't mean void functions (which do return to the caller - they just don't return a value), but functions where the control flow will not return to the calling function after the function finishes (e.g. functions that exit the application, loop forever or throw exceptions as in your example).

This can be used by compilers to make some optimizations and generate better warnings. For example if f has the [[noreturn]] attribute, the compiler could warn you about g() being dead code when you write f(); g();. Similarly the compiler will know not to warn you about missing return statements after calls to f().

Olivia Stork
  • 4,660
  • 5
  • 27
  • 40
sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • 7
    What about a function such as [`execve`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html) that _shouldn't_ return but _could_? Should it have the _noreturn_ attribute? – djsp Oct 10 '14 at 15:27
  • 30
    No, it shouldn't -- if there is a possibility for control flow to return to the caller, it must not have the `noreturn` attribute. `noreturn` may only be used if your function is guaranteed to do something that terminates the program before control flow can return to the caller -- for example because you call exit(), abort(), assert(0), etc. – RavuAlHemio Nov 11 '14 at 19:56
  • 2
    Does that include returning via exception throw (so to speak), or do thrown exceptions skip `catch`es outside of the `noreturn` function, or is throwing exceptions from within the `noreturn` function disallowed? – Slipp D. Thompson Jun 24 '16 at 05:59
  • 7
    @SlippD.Thompson If a call to a noreturn function is wrapped in a try-block, any code from the catch block on will count as reachable again. – sepp2k Jun 24 '16 at 08:15
  • 2
    @sepp2k Cool. So it's not impossible to return, just abnormal. That's useful. Cheers. – Slipp D. Thompson Jun 24 '16 at 10:09
  • 8
    @SlippD.Thompson no, it's impossible to return. Throwing an exception is not returning, so if every path throws then it's `noreturn`. Handling that exception is not the same as it having returned. Any code within the `try` after the call is still unreachable, and if not `void` then any assignment or use of the return value will not happen. – Jon Hanna Sep 01 '17 at 15:09
  • @JonHanna Yes obviously it's impossible to formally `return`; however I was asking if it was possible to do a general return-type-execution-operation. That's why I said _“returning via exception throw (so to speak)”_. I should've probably have written _“accomplishing (hacky) functionality akin to return using `throw`”_ or just _“exiting the function execution and continuing in the enclosing call using `throw`”_. Everybody here knows that `throw` doesn't have return values, and that it resumes execution at the `catch`. I was asking for return-like capability, not `return`. – Slipp D. Thompson Sep 02 '17 at 00:42
  • @SlippD.Thompson I wouldn't be so sure that "*everyone knows*" -- keep in mind that people _extremely_ new to C++ and programming in general are on this site, too, and it'd be far too easy for them to read your comments and assume that `throw`ing was a type of `return`ing. I'm aware that wasn't your intention, but that's why it's worth explicitly pointing out, even though everyone _who commented_ likely knew the difference. (Also, sorry for necro, but no one else explained so I felt like someone should.) – Nic Jun 13 '18 at 00:20
  • @sepp2k I think it would be worth [edit]ing this answer to explicitly mention how `throw` is treated. While it's implied already, it makes it clearer and costs very little to add the sentences "`throw`ing from a `[[noreturn]]` function is possible, and works as expected -- the only think you can't do is `return`. If you wrap the call in a `try`/`catch`, you can treat it like any other throwing function." (Probably written better, though.) – Nic Jun 13 '18 at 00:23
  • @RavuAlHemio Not exactly. Since the question is marked "C++", and it even has a `throw` inside its example, there comes _exceptional control flow_. Such a function is assumed never returning, and it can also avoid to terminate at the same time. In the ISO C++ parlance, it merely "exits via an exception". (OTOH, with ISO C, there is no such choice.) – FrankHB Feb 02 '20 at 07:53
  • It may be also worth mentioning G++'s `-Wsuggest-attribute=noreturn` warns against functions that only throw without attributes to indicate `noreturn`. This shows (purely) throwing is one of the exact case to be served by `[[noreturn]]`. – FrankHB Feb 02 '20 at 08:02
74

noreturn doesn't tell the compiler that the function doesn't return any value. It tells the compiler that control flow will not return to the caller. This allows the compiler to make a variety of optimizations -- it need not save and restore any volatile state around the call, it can dead-code eliminate any code that would otherwise follow the call, etc.

Stephen Canon
  • 103,815
  • 19
  • 183
  • 269
  • Be careful to use [[noreturn]]. Because if the function contains while loop, and you break the loop unintentionally, the program may act wired. – CuteDoge Feb 23 '21 at 08:28
  • @CuteDoge A compiler should emit a diagnostic in this case. It is not `[[maybe_noreturn]]`. For example a void function returning a value gives a warning. If you have warnings turned off or ignored a diagnostic, you have other problems. – artless noise Sep 15 '22 at 14:23
33

It means that the function will not complete. The control flow will never hit the statement after the call to f():

void g() {
   f();
   // unreachable:
   std::cout << "No! That's impossible" << std::endl;
}

The information can be used by the compiler/optimizer in different ways. The compiler can add a warning that the code above is unreachable, and it can modify the actual code of g() in different ways for example to support continuations.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 3
    gcc/clang [don't give warnings](http://coliru.stacked-crooked.com/view?id=5563e13b0ff475ba4131e93d7c461093-eae62b55f4d064645734b0b710d33419) – TemplateRex Aug 21 '13 at 08:08
  • 4
    @TemplateRex: Compile with `-Wno-return` and you will get a warning. Probably not the one you were expecting but it is probably sufficient to tell you that the compiler has knowledge of what `[[noreturn]]` is and it can take advantage of it. (I am a bit surprised that `-Wunreachable-code` did not kick in...) – David Rodríguez - dribeas Aug 21 '13 at 13:14
  • 3
    @TemplateRex: Sorry `-Wmissing-noreturn`, the warning implies that flow analysis determined that the `std::cout` is not reachable. I don't have a new enough gcc at hand to look at the generated assembly, but I would not be surprised if the call to `operator<<` was dropped – David Rodríguez - dribeas Aug 21 '13 at 13:18
  • now it works, and it's a pretty decent one (essentially: `[[noreturn]]` should be applied transitively towards the outer-most scope) – TemplateRex Aug 21 '13 at 13:20
  • 1
    Here's an [assembly dump](http://coliru.stacked-crooked.com/view?id=c9cacd3fbd9fd577536cd1475bfe013b-9c7308574711704234cea5c23ddac68e) (-S -o - flags in coliru), indeed drops the "unreachable" code. Interestingly enough, [`-O1` is already enough](http://coliru.stacked-crooked.com/view?id=b20ef43ae16e5b1901ab38eb795c81e3-fb3032ec106960b49620c826fab4a73a) to drop that unreachable code without the `[[noreturn]]` hint. – TemplateRex Aug 21 '13 at 13:23
  • 2
    @TemplateRex: All of the code is in the same translation unit and visible, so the compiler can infer the `[[noreturn]]` from the code. If this translation unit only had a declaration of the function that was defined somewhere else, the compiler would not be able to drop that code, as it does not *know* that the function does not return. That is where the attribute should help the compiler. – David Rodríguez - dribeas Aug 21 '13 at 13:46
32

Previous answers correctly explained what noreturn is, but not why it exists. I don't think the "optimization" comments is the main purpose: Functions which do not return are rare and usually do not need to be optimized. Rather I think the main raison d'être of noreturn is to avoid false-positive warnings. For example, consider this code:

int f(bool b){
    if (b) {
        return 7;
    } else {
        abort();
    }
 }

Had abort() not been marked "noreturn", the compiler might have warned about this code having a path where f does not return an integer as expected. But because abort() is marked no return it knows the code is correct.

Nadav Har'El
  • 11,785
  • 1
  • 24
  • 45
  • All the other examples listed use void functions -- how does it work when you have both the [[no return]] directive and a non-void return type? Does the [[no return]] directive only come into play when the compiler gets ready to warn about a possibility of not returning and ignore the warning? For example, does the compiler go: "Okay, here is a non-void function." * continues compiling * "Oh crap, this code might not return! Should I warn the user?* "Nevermind, I see the no-return directive. Carry on" – Raleigh L. Feb 15 '19 at 21:02
  • 7
    The noreturn function in my example is not f(), it's abort(). It doesn't make sense to mark a non-void function noreturn. A function which sometimes returns a value and sometimes returns (a good example is execve()) cannot be marked noreturn. – Nadav Har'El Apr 03 '19 at 12:16
  • 1
    implicit-fallthrough is another such example: https://stackoverflow.com/questions/45129741/gcc-7-wimplicit-fallthrough-warnings-and-portable-way-to-clear-them/52707279#52707279 – Ciro Santilli OurBigBook.com Aug 05 '19 at 12:56
17

Type theoretically speaking, void is what is called in other languages unit or top. Its logical equivalent is True. Any value can be legitimately cast to void (every type is a subtype of void). Think about it as "universe" set; there are no operations in common to all the values in the world, so there are no valid operations on a value of type void. Put it another way, telling you that something belongs to the universe set gives you no information whatsoever - you know it already. So the following is sound:

(void)5;
(void)foo(17); // whatever foo(17) does

But the assignment below is not:

void raise();
void f(int y) {
    int x = y!=0 ? 100/y : raise(); // raise() returns void, so what should x be?
    cout << x << endl;
}

[[noreturn]], on the other hand, is called sometimes empty, Nothing, Bottom or Bot and is the logical equivalent of False. It has no values at all, and an expression of this type can be cast to (i.e is subtype of) any type. This is the empty set. Note that if someone tells you "the value of the expression foo() belongs to the empty set" it is highly informative - it tells you that this expression will never complete its normal execution; it will abort, throw or hang. It is the exact opposite of void.

So the following does not make sense (pseudo-C++, since noreturn is not a first-class C++ type)

void foo();
(noreturn)5; // obviously a lie; the expression 5 does "return"
(noreturn)foo(); // foo() returns void, and therefore returns

But the assignment below is perfectly legitimate, since throw is understood by the compiler to not return:

void f(int y) {
    int x = y!=0 ? 100/y : throw exception();
    cout << x << endl;
}

In a perfect world, you could use noreturn as the return value for the function raise() above:

noreturn raise() { throw exception(); }
...
int x = y!=0 ? 100/y : raise();

Sadly C++ does not allow it, probably for practical reasons. Instead it gives you the ability to use [[ noreturn ]] attribute which helps guiding compiler optimizations and warnings.

Elazar
  • 20,415
  • 4
  • 46
  • 67
  • 8
    Nothing may be cast to `void` and `void` never evaluates to `true` or `false` or anything else. – Clearer Mar 24 '15 at 14:31
  • 3
    Everything may be cast to `void`. and `void` never evaluates to `true` since `bool` is subtype of `void` and not the other way around. Again, everything said here is type-theoretical. nothing C++-centric. `void` is not a 1st-class citizen in the C++ type system. – Elazar Mar 24 '15 at 14:35
  • 8
    When I say `true`, I don't mean "the value `true` of the type `bool`" but the logic sense, see [Curry-Howard correspondence](https://en.wikipedia.org/wiki/Curry%E2%80%93Howard_correspondence) – Elazar Mar 24 '15 at 14:36
  • 11
    Abstract type theory which does not fit the type system of a specific language is irrelevant when discussing the type system of that language. The question, in question :-), is about C++, not type theory. – Clearer Mar 24 '15 at 14:37
  • 2
    Specifically to this question, abstract type-theory gives the perfect answer. C++ does not brutally violates it (since it is pretty much consistent) in this case. – Elazar Mar 24 '15 at 14:38
  • 1
    Why else can you do in C++ stuff like `y!=0 ? x/y : throw exception()` – Elazar Mar 24 '15 at 14:40
  • 1
    Specifically to this answer, it suggests that `void(true)` is allowed in C++. It is not. `y != 0 ? x / y : throw exception()` has nothing to do with how `void` is treated as a type. – Clearer Mar 24 '15 at 18:17
  • 9
    `(void)true;` is perfectly valid, as the answer suggests. `void(true)` is something completely different, syntactically. It is an attempt to create a new object of type `void` by calling a constructor with `true` as an argument; this fails, among other reasons, because `void` is not first class. – Elazar Mar 24 '15 at 18:26
  • 2
    So it is. I'm accustomed to using type(value) casting, not c style casting. – Clearer Mar 24 '15 at 18:28
  • @Clearer What language, exactly, are you used to? – coder543 Jul 06 '15 at 18:22
  • @Elazar, I'm also curious as to which language you're used to. Neither of you make much sense, being unfamiliar with C-style languages which are the cornerstone of modern programming, by many people's standards. – coder543 Jul 06 '15 at 18:23
  • 1
    I used C/C++ notation, just like the OP. – Elazar Jul 06 '15 at 20:41
  • 2
    This answer has nothing to do with the OP's question. – Josh Sanford Feb 17 '17 at 15:03
  • 4
    @JoshSanford it explains the difference between `void` and `noreturn` in type-theoretic term, complementing the (admittedly more useful) practical answers above. – Elazar Feb 18 '17 at 20:55
  • 2
    @Elazar: `void(foo())` is fine—but if it’s an entire statement then it’s a function declaration. – Davis Herring Aug 17 '19 at 05:22
  • Some claims are technically wrong. If a function declared with `[[noreturn]]` returns, the behavior is undefined. This grants implementations the permission to assume the function never returns, without a single instance of the value ever meeting the concept "unit". It's actually a special kind of [_bottom_ type](https://en.wikipedia.org/wiki/Bottom_type). The type `void` is another kind that it "raises" a translation-time exception ("compiling error") on value-returning instead. I'm a bit curious which languages name the case `unit` or `top`. – FrankHB Feb 02 '20 at 07:32
  • 1
    The type `void` can be a unit type only when the use of expressions of `void` type is well-formed (e.g. returning a `void` expression in a function declared `void`) and well-defined (e.g. at least without `[[noreturn]]` in the declaration of that function), which is essentially context-dependent. This means `void` is never a first-class unit type in C++. On the other hand, the type `void*` can be a valid example of the top type, when the standard conversions are fabricated in the typing rules. The type `void*` is never a unit type as it can have more than one well-defined values in a program. – FrankHB Feb 02 '20 at 07:41
  • 1
    Yes, `void` is not first class. But `void*` is neither a unit type nor top. `void` is a non-first-class unit type (top), and `void*` is more like a `ref top`. Structs cannot be meaningfully casted to `void*`, but can be casted to `void` wherever the type system does not decide to (arbitrarily) discriminate that type. – Elazar Feb 02 '20 at 16:53
  • I return void, therefore I am. – DexterHaxxor Feb 21 '20 at 20:01
  • As for now, this is probably the only more-or-less valid answer to the question here. It's a pity, that C++ (even as of c++17) can't yet do `int x = y!=0 ? 100/y : raise()` (for many years, I was sure it can; and I have stumbled upon that only now). That greatly reduces the usefulness of `[[noreturn]]` in C++. – Sasha Oct 15 '22 at 11:39