Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upRFC for placement box with Placer trait for overloading. #470
Conversation
pnkfelix
added some commits
Oct 29, 2014
This comment has been minimized.
This comment has been minimized.
bill-myers
commented
Nov 18, 2014
|
I'm not sure placement box is in general a good design. The big issue is that the only thing that it solves is elimination of a temporary, but only in a specific case and in a way that does not easily compose. For example, let's say that you want to create a mechanism to box T into a Box<Option> by using Some or a mechanism to box two values in a pair. The natural composable way to do so is this:
The problem is that this results in creating temporaries again, even if we have placement box... and the only way to avoid this is an extremely ugly hack with unsafe code that implements the placement box traits to do this simple construction operation. Also it seems it's completely impossible to box more than one object (for instance, by boxing as a pair) without temporaries even with hacks in this RFC. This RFC also provides an example of "emplace back" that takes 54 lines of code (!) for something that should take 1 line. IMHO any design, such as the one in this RFC, that doesn't allow such composability easily should probably be avoided. I think the simplest solution is to just remove the box keyword, use Box::new and rely on inlining and the optimizer to remove the temporaries, with an annotation on heap allocation functions that tells LLVM that constant propagation, move elimination and similar can be done across heap allocation. |
This comment has been minimized.
This comment has been minimized.
|
@bill-myers you make good points, but I can do without hyperbole like this:
A lot of the code in that example can be removed and is only there for expository purposes (or for testing in my prototype). I do (and did) acknowledge that the code there is overcomplicated by the protocol's use of two separate traits; see discussion in alternatives section Having said that, the composability problem is an interesting one. I'm not sure a solution exists that is (1.) safe, (2.) composable in the way you outline, and (3.) guaranteed to avoid a temporary. This is not just about the stdlib's heap allocation functions; it is also about user-defined memory abstractions, like arenas. But maybe you are suggesting that even the latter should receive the annotations to convince LLVM that it is safe to do optimizations across their procedure call boundary? |
This comment has been minimized.
This comment has been minimized.
|
I just remembered: @nikomatsakis told me a while back that a number of the types I wrote as type parameters should instead be associated types. I will try to update the RFC accordingly tomorrow or Thursday. |
pnkfelix
referenced this pull request
Nov 18, 2014
Closed
Require public trait items to be marked `pub` #227
This comment has been minimized.
This comment has been minimized.
Relying on the implementation details of the optimiser seems unfortunate. Also, we would need to make it happen even without optimisations (otherwise, say, |
This comment has been minimized.
This comment has been minimized.
reem
commented
Nov 19, 2014
|
A lot of the apparent complexity in this RFC appears to come from a lack of a safe way to talk about "out" pointers, or pointers that are not initialized and are meant to be initialized before use, "move" pointers that take ownership of the thing they are referencing without moving or boxing it, and the lack of HKT. This whole thing could easily be written as just: pub trait Placer {
type Place<*>; // Theoretical HKT syntax for a * -> * type.
fn place<T>(&mut self) -> &out T;
fn finalize<T>(&mut self, &move T) -> Place<T>;
}(or something similar) if we had Here's a sample implementation of // Box
pub struct BoxPlacer;
impl Placer for BoxPlacer {
type Place = Box;
fn place<T>(&mut self) -> &out T {
let size = mem::size_of::<T>();
let align = mem::align_of::<T>();
if size == 0 {
(heap::EMPTY as *mut u8).into_out()
} else {
let p = unsafe {
// into_out does a NULL check
// this implementation could be made *safe* if there was a heap::allocate_out
heap::allocate(size, align).into_out()
};
}
}
fn finalize<T>(&mut self, val: &move T) -> Box<T> {
unsafe { mem::transmute::<&move T, Box<T>>(val) }
}
}
// And emplace back
pub struct Emplacer(&mut Vec<()>);
impl<T> Placer for Emplacer<T> {
type Place = // some standard `* -> ()` HKT goes here
fn place<T>(&mut self) -> &out T {
// Grows the Vec if needed and gets an &out reference to a spot past the end, implementation detail.
self.0.place_back()
}
fn finalize<T>(&mut self) {}
}For the record, I have clearly not given this nearly as much thought as @pnkfelix has in this RFC, I just think it's worth questioning whether future language features could vastly simplify the situation here, and, if they could, whether it's worth adding this feature now or just reserving the necessary syntax. |
This comment has been minimized.
This comment has been minimized.
|
@reem ah I think your comment is a much much more coherent description of what I was thinking of when discussing |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
reem
commented
Nov 19, 2014
|
I think the major hole in the implementation I proposed above is that it doesn't handle exceptions while evaluating Something like: pub trait OutHandle<T> {
fn out(self) -> &out T;
} |
nrc
assigned
pnkfelix
Nov 20, 2014
This comment has been minimized.
This comment has been minimized.
|
@bill-myers, can you please expand on why tuple placement is not possible without allocating temporaries? |
This comment has been minimized.
This comment has been minimized.
|
I completely agree with @reem regarding (As an aside, the current return value optimization also kinda creeps me out (despite my background functional programming), and I wonder if something a bit more explicit but still ergonomic could be done instead.)
Once-closure that takes a
The double try is a bit unfortunate, but what if : let b: PBox<T> = try!(box (place) try!(run_code()));becomes: let b: PBox<T> = try!(place.make_box::<_>(|once: ptr: &out T| -> R<T> {
ptr = try!(run_code());
// Just need some way to add the Ok(())
},));Granted, I punt on the |
This comment has been minimized.
This comment has been minimized.
|
So conversation here has slowed down, but I want to add my current thoughts. Let's leave aside the particulars of the protocol for the moment. The high-level question is this: for 1.0, we want to have an in-place allocation / construction syntax, or if we want to start without one and bring one in later. The argument in favor of adding the syntax now is probably two-fold. First, there is a consistency argument. If we were to remove this syntax, it would mean that (for the time being) the The argument against adding the syntax is that it is clearly not necessary for 1.0 and we may be able to concoct a better protocol in the future. It would also allow us to introduce the We had this discussion in a recent meeting (last Tuesday) and the general feeling was to lean towards removing box from the 1.0 release. That said, it's important to recognize that this too requires some effort (first, measurement to see its impact, but also just editing the existing code). |
This comment has been minimized.
This comment has been minimized.
reem
commented
Dec 15, 2014
|
@nikomatsakis Why could we not maintain the status quo and simply special-case I am for waiting for post-1.0 to implement extensible |
This comment has been minimized.
This comment has been minimized.
|
closing in favor of the more conservative #809 |
pnkfelix commentedNov 18, 2014
Summary
Add user-defined placement
inexpression (more succinctly, "an in expression"), an operator analogous to "placement new" in C++. This provides a way for a user to specify (1.) how the backing storage for some datum should be allocated, (2.) that the allocation should be ordered before the evaluation of the datum, and (3.) that the datum should preferably be stored directly into the backing storage (rather than allocating temporary storage on the stack and then copying the datum from the stack into the backing storage).This RFC does not suggest any change to the non-placement box expression special form (
box <value-expr>); a future RFC is planned that will suggest changes to that form based on preliminary feedback to this RFC, but that is orthogonal to this RFC's goals.rendered