Skip to content

clang++ should always emit @llvm.trap() when flowing off the end of a non-void function #75797

@Eisenwave

Description

@Eisenwave

Currently, when optimizations are enabled, flowing off the end of a function in C++ mode emits unreachable. This means that a function such as:

int get(int x) {
    if (x == 0) { }
    else std::abort();
}

... is equivalent to simply calling abort(), which is caused by emitting unreachable at the end of the function, and having SimplifyCFG eliminate the x == 0 branch. This behavior does not reflect developer intent, and it's possible to fall through into other code sections as a result of simply treating this as optimizable UB.

On debug builds, @llvm.trap() is emitted at the end of the function, which generates a ud2 pseudo-instruction on x86-64. This should also be done with optimizations enabled.

Even with no warning flags enabled, clang emits

<source>:6:1: warning: non-void function does not return a value in all control paths [-Wreturn-type]
    6 | }
      | ^

Flowing off the end of a function is a common developer mistake and basically never intentional. It should not be hidden by the optimizer because it may result in security vulnerabilities such as CVE-2014-9296.

If a developer actually wants the current behavior, they can simply write:

int get(int x) {
    if (x == 0) {
        __builtin_unreachable(); // or, since C++23
        std::unreachable(); // or, alternatively
        [[assume(false)]];
    }
    else std::abort();
}

There is zero motivation for violating programmer intent and emitting security vulnerabilities here. This offers no optimization opportunities that could not be trivially obtained with more explicit code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions