Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> 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).



You can surely allocate on callers stack on C++, that is how return value optimization works, and was made explicit on ANSI C++17 standard.


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.


That'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.


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.


To 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.


IIRC ada can return variable sized objects on the stack.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: