3 ms·
> 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 thi
by eddyb 9y ago
> 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).
- pjmlp 9y agoYou can surely allocate on callers stack on C++, that is how return value optimization works, and was made explicit on ANSI C++17 standard.
- sanxiyn 9y agoWell, 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.
- Animats 9y agoRight. 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.
- Animats 9y agoTo be more explicit y = f(g(h(x))); is really 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.
- masklinn 9y agoThat's not allocating on the callers's stack though, that's just initialising there. The caller does the allocation. And Rust does RVO, in fact it's important for "placement new" structures until the dedicated syntax lands.
- gpderetta 9y agoIIRC ada can return variable sized objects on the stack.