> There's no way to give a return value the lifetime of the caller, not the callee.
That is, in the general case, not physically possible, and this sort of thing only works in GC languages because they return GC pointers to dynamically allocated data. It certainly doesn't exist in C or C++ (unless I completely misunderstood what semantics you wanted).
AFAIK there is no implementation of anything (not even JITs) which can allocate on the caller's stack. The closest anything gets is Forth where the call stack and data stack are separate and returning just leaves data on the stack for caller to read.
Without a split stack, returning something larger than a register (or two) is done by passing a pointer to space on the caller's stack, to the callee.
There have been very specific schemes proposed for `-> [T]` in Rust, e.g. the slice is left of the callee's stack, and a pointer to it returned, so the caller can allocate that much stack space and memmove'd it up.
But that wouldn't help with "allocating" on the caller's caller's stack because moving the data would invalidate references to it (and if you can have a reference to something, you can use it in ways the compiler can't trace it, unlike a GC).
Well, in that sense, Rust does return value optimization too, and it has been doing so basically since forever. Because Rust has no copy constructor, RVO in Rust does not need any explicit support from the language specification.
Right. Just think of the return value as an additional mutable formal parameter, and function return values as syntactic sugar for that additional parameter. The caller allocates space for the actual parameter.
var hreturn;
h(x, hreturn);
var greturn;
g(hreturn, greturn);
// hreturn is no longer live
var y;
h(greturn, y);
// greturn is no longer live
Looked at this way, a function can return any fixed-size type without a copy. Some languages, mostly those in the Pascal/Modula family, did this
explicitly. Instead of having a return statement, within a function, the name of the function was the return value, and code could assign to it, or pass it by reference to another function.
That is, in the general case, not physically possible, and this sort of thing only works in GC languages because they return GC pointers to dynamically allocated data. It certainly doesn't exist in C or C++ (unless I completely misunderstood what semantics you wanted).
AFAIK there is no implementation of anything (not even JITs) which can allocate on the caller's stack. The closest anything gets is Forth where the call stack and data stack are separate and returning just leaves data on the stack for caller to read.
Without a split stack, returning something larger than a register (or two) is done by passing a pointer to space on the caller's stack, to the callee.
There have been very specific schemes proposed for `-> [T]` in Rust, e.g. the slice is left of the callee's stack, and a pointer to it returned, so the caller can allocate that much stack space and memmove'd it up.
But that wouldn't help with "allocating" on the caller's caller's stack because moving the data would invalidate references to it (and if you can have a reference to something, you can use it in ways the compiler can't trace it, unlike a GC).