cassemblyc11function-callnoreturn

Why does "noreturn" function return?


I read this question about noreturn attribute, which is used for functions that don't return to the caller.

Then I have made a program in C.

#include <stdio.h>
#include <stdnoreturn.h>

noreturn void func()
{
        printf("noreturn func\n");
}

int main()
{
        func();
}

And generated assembly of the code using this:

.LC0:
        .string "func"
func:
        pushq   %rbp
        movq    %rsp, %rbp
        movl    $.LC0, %edi
        call    puts
        nop
        popq    %rbp
        ret   // ==> Here function return value.
main:
        pushq   %rbp
        movq    %rsp, %rbp
        movl    $0, %eax
        call    func

Why does function func() return after providing noreturn attribute?


Solution

  • The function specifiers in C are a hint to the compiler, the degree of acceptance is implementation defined.

    First of all, _Noreturn function specifier (or, noreturn, using <stdnoreturn.h>) is a hint to the compiler about a theoretical promise made by the programmer that this function will never return. Based on this promise, compiler can make certain decisions, perform some optimizations for the code generation.

    IIRC, if a function specified with noreturn function specifier eventually returns to its caller, either

    the behaviour is undefined. You MUST NOT return from the function.

    To make it clear, using noreturn function specifier does not stop a function form returning to its caller. It is a promise made by the programmer to the compiler to allow it some more degree of freedom to generate optimized code.

    Now, in case, you made a promise earlier and later, choose to violate this, the result is UB. Compilers are encouraged, but not required, to produce warnings when a _Noreturn function appears to be capable of returning to its caller.

    According to chapter §6.7.4, C11, Paragraph 8

    A function declared with a _Noreturn function specifier shall not return to its caller.

    and, the paragraph 12, (Note the comments!!)

    EXAMPLE 2
    _Noreturn void f () {
    abort(); // ok
    }
    _Noreturn void g (int i) { // causes undefined behavior if i <= 0
    if (i > 0) abort();
    }
    

    For C++, the behaviour is quite similar. Quoting from chapter §7.6.4, C++14, paragraph 2 (emphasis mine)

    If a function f is called where f was previously declared with the noreturn attribute and f eventually returns, the behavior is undefined. [ Note: The function may terminate by throwing an exception. —end note ]

    [ Note: Implementations are encouraged to issue a warning if a function marked [[noreturn]] might return. —end note ]

    3 [ Example:

    [[ noreturn ]] void f() {
    throw "error"; // OK
    }
    [[ noreturn ]] void q(int i) { // behavior is undefined if called with an argument <= 0
    if (i > 0)
    throw "positive";
    }
    

    —end example ]