Clang 13 guarantees tail calls within C++ lifetime constraints
musttail turns a tail-call optimization opportunity into a checked requirement. Its destructor and signature restrictions show where that guarantee meets C++ object lifetimes.
A recursive algorithm that depends on bounded stack growth needs a stronger promise than an optimizer's willingness to eliminate a call frame. Optimization settings can change, and apparently minor edits can make a tail call unavailable. Clang 13 introduces a statement attribute that requires the compiler to provide the tail call or diagnose the failure.
The LLVM 13.0.0 release is published on October 1. Its Clang release notes introduce [[clang::musttail]] for C++ and separately raise the supported GNU libstdc++ floor to 4.8.3. These are concrete changes to a C++ toolchain, with different consequences for source semantics and library compatibility.
I consider the lifetime restrictions on musttail particularly relevant to C++. RAII is the language's strongest argument for me, but a destructor can require work after a callee returns. A guaranteed transfer out of the caller has to respect that obligation instead of treating every syntactic return call as equivalent.
The attribute belongs to the return statement
The versioned Clang 13 attribute reference specifies musttail on a return statement whose result is a function call. It is not a property attached to a function declaration. The particular call is where the program requests the guarantee.
For example, compatible signatures allow a wrapper to express the requirement:
int next_step(int value);
int current_step(int value)
{
[[clang::musttail]] return next_step(value);
}
The documented guarantee applies even when optimization is disabled. The release notes explain that the compiler emits an error if it cannot guarantee the tail call. That distinguishes the attribute from a request that the compiler may ignore while still accepting the program.
For a recursive cycle, the property prevents the annotated call from adding unbounded stack growth through repeated ordinary frame creation. It does not prove that every other call in the cycle has the same behavior. A stack argument for the whole algorithm still depends on the relevant call paths and where the guarantee applies.
The reference requires matching calling conventions and the same number of arguments. Return and argument types must satisfy its similarity rules, including an implicit this parameter where applicable. A wrapper cannot assume that converting a result after the call is compatible with tail transfer merely because the source conversion is short.
Even a function returning void must use a return statement for the annotated call under the documented rule. The absence of a returned value does not move the attribute onto an ordinary call statement. That syntax identifies the control transfer the compiler must guarantee, including when there is no numeric or object result to propagate.
Object destruction constrains the transfer
Variables in scope, including arguments and the return value, must be trivially destructible under the documented requirements. This is a substantive restriction in a language where a local object's destructor commonly releases a resource. A nontrivial destructor represents behavior the caller must preserve.
The restriction makes the intended scope of the facility clearer. A dispatch routine passing simple state between handlers can fit the model. A function retaining a lock-owning object in scope has an additional lifetime obligation. Whether the call is textually the final expression does not remove that obligation.
This is an implication of C++'s object model rather than a reason to abandon scope-bound cleanup. The appropriate question is whether the desired control transfer and the live objects' destruction requirements are compatible. musttail makes the conflict diagnosable when the program asks for a guarantee it cannot receive.
Variadic functions and old-style K&R C declarations are excluded as well. The argument interface must be known in the way required by the tail-call implementation. This limits where the attribute can cross an existing C-compatible boundary, even when the program otherwise compiles and links through that interface.
The reference also warns about virtual calls implemented through thunks. A thunk is not generally guaranteed to tail-call the eventual implementation, so a recursive cycle involving it can still grow the stack. The distinction matters because enforcing one transfer does not enforce every additional transfer hidden behind the target's implementation.
I prefer that explicit limitation to a general claim that the attribute makes recursion safe. The useful guarantee names a call and its supported conditions. C++ programs still need to account for destructors and intermediate calls when applying it to a larger execution pattern.
Stack information answers another question
Clang 13 adds -fstack-usage, which produces a .su file per input source containing frame-size information for defined functions. A frame-size report and a tail-call guarantee provide different information. Frame size describes a function's local stack requirement, while repeated non-tail calls can multiply the number of simultaneously active frames.
This distinction helps interpret the new tools together. A small frame does not establish a small total stack requirement for unbounded recursion. A guaranteed tail call can prevent growth at the annotated transition, but other live calls and storage requirements remain relevant. Neither observation alone describes every possible path through a recursive program.
Library compatibility remains independent
The release notes say Clang no longer retains workarounds for GNU libstdc++ versions older than 4.8.3. That library floor is separate from recognizing C++ syntax. Choosing a newer front end while keeping an older library installation must satisfy the supported combination, rather than assuming the compiler version describes every component in use.
Clang's versioned user manual explicitly describes the compiler as one component of a complete toolchain. A language feature and a library facility therefore require different evidence of support. musttail is provided by the compiler, while standard-library functionality depends on the selected implementation and headers.
That separation is useful when assessing this release's C++ progress. The concrete tail-call facility has documented restrictions and a diagnostic guarantee. The libstdc++ floor defines a supported library boundary. A project can evaluate each change against the corresponding source or build assumption instead of treating a release number as a promise that every C++20 or draft C++23 facility is complete.