Removal of all unstable placement features

I'm not proposing that the compiler would make decisions based on the body of push, but based on some attribute or other marker on the declaration of push. I know that's vague, but I wanted to leave open the space of possible designs.

Here's a strawman for a more specific design:

Add a new trait as an alternative for the Fn trait for functions (or function-like objects) that want to allow constructing their arguments in place – call them "placer functions", and call the trait PlacerFn. (There probably also need to be PlacerFnMut and PlacerFnOnce.) In the simplest case, implementations would manually impl this trait (XX: but how to make this work with method syntax?). However, there might be some potential to allow wrapper functions that delegate to placer functions to be written as normal functions with an attribute attached, as long as they satisfy certain conditions and have the compiler generate an impl automatically.

A placer function receives its arguments in stages, through a series of calls: that way, given a function like Vec::insert(&mut self, index: usize, element: T), the implementation can receive the index before returning a place in which to construct the element (so it could shift existing elements forward, and return a pointer to the newly vacated &vec[index]). As it happens, when these kinds of functions have more than one argument, they usually take the value as their last argument, so we can simplify things by requiring arguments to be received in order from left to right; this also avoids messing with the order of evaluation on the caller side.

Here's a possible definition of the PlacerFnOnce trait:

// These names all suck and should be bikeshedded
trait PlacerFnOnce<UpfrontArgs, PlacedArg, RemainingArgs> {
    type Output;
    type Place: Place<PlacedArg> + PlacerOrNormalFnOnce<RemainingArgs, Output=Output>;
    extern "rust-call" fn provide_arguments(self, UpfrontArgs) -> Self::Place;
}

Whereas the Fn trait has one generic parameter, Args, representing a tuple of all the arguments, this has three, splitting the argument list into three parts:

  1. UpfrontArgs is a tuple of any arguments that don't need to be constructed in place, but should be passed up front, by value – such as the index parameter from Vec::insert.
  2. PlacedArg is the argument that needs to be constructed in place.
  3. RemainingArgs is a tuple of the rest of the arguments, if any, which will be provided in a subsequent call. (There always needs to be a subsequent call, even if there are no more arguments: the call will also act as the equivalent of InPlace::finalize, signaling that the in-place construction has completed successfully.)

[An alternative design could stick with a single Args parameter and use associated types to determine how to split it into parts; this would help avoid ambiguity, but would make the trait definition look a bit more complicated.]

The return value of provide_arguments must impl two traits: Place<PlacedArg>, which is the same as the existing, soon-to-be-deprecated Place trait (i.e. it provides a pointer to place the argument into), and PlacerOrNormalFnOnce, for the subsequent call.

PlacerOrNormalFnOnce is a trait that has blanket impls for types that impl either PlacerFnOnce or the normal FnOnce. (This can be achieved with specialization, but would require the lattice rule, or some other mechanism, to handle cases where someone tries to impl both PlacerFnOnce and FnOnce on the same type.) The compiler could also use this trait to decide which of PlacerFnOnce or FnOnce to use for a function call in the first place.

So the function call desugaring takes three steps:

  1. Call provide_arguments with the upfront arguments to obtain a Place;
  2. Get the pointer from the Place and construct the argument in place;
  3. Do a recursive call on the Place object itself with the remaining arguments. (This is always done by-value through PlacerOrNormalFnOnce, even if the original call was through PlacerFn or PlacerFnMut, so that the callee can take ownership of the Place.)

If the Place itself impls PlacerFnOnce, that indicates that there's another argument somewhere in the list that needs to be constructed in place, so it goes through the whole process again. On the other hand, if the Place impls normal FnOnce, that's the end of the line: it will be called with the remaining arguments and produce the final function return value.

Similarly to the existing <- behavior, if evaluation of an in-place argument diverges (i.e. it panics, executes break or return, etc.), then the Place will be dropped instead of being passed by value into a method call; the Drop impl should handle reverting the container to its previous state.

Also note that the PlacerFn* traits would not be usable as trait objects, at least not unless you provided the Place associated type up front as part of the trait object type. That's probably not the end of the world; I'd love to see an expansion of object safety in general, but that's a quite separate topic.

TL;DR: basically the same thing under the hood as the existing Placer, but implicit in function calls.