Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User-defined placement-box #18233

Closed

Conversation

Projects
None yet
6 participants
@pnkfelix
Copy link
Member

pnkfelix commented Oct 22, 2014

Add support for user-defined placement-box expressions (i.e. box (<place>) <value> for types other than Box<T>).

This is a prototype design that @nikomatsakis and @pnkfelix have been working on. It works by desugaring an instance of placement-box into a protocol where we first create the intermediate storage with a pseudo-out-pointer, then initialize the storage, and then convert the out-pointer into the desired box type.

The code was originally written to handle both box (<place>) <value> and box <value> via the same uniform desugaring, and it is easy to revise support for the latter; but as is, this PR only puts box (<place>) <value> into the desugaring. The reason for this is that the error messages get a lot nastier to handle from the desugared form, and pnkfelix did not want to hold this work up further trying to resolve that.

The final design for this will require an RFC; this code is entirely experimental for now.

Proto RFC: http://discuss.rust-lang.org/t/pre-rfc-placement-box-with-placer-trait/729/5

pnkfelix added some commits Sep 26, 2014

Desugar `box (<place_expr>) <value_expr>` to `Placer` protocol.
Note that this narrows desugaring to only be when actual placer is
provided; `box <value_expr>` and `box () <value_expr>` both still fall
through to the `mk_unary(UnUniq, _)` path.

Many more compile-fail tests would need updating if we were not
restricting desugaring to `box` with explicit placer.
@rust-highfive

This comment has been minimized.

Copy link
Collaborator

rust-highfive commented Oct 22, 2014

warning Warning warning

  • These commits modify unsafe code. Please review it carefully!
@pnkfelix

This comment has been minimized.

Copy link
Member Author

pnkfelix commented Oct 22, 2014

@pnkfelix

This comment has been minimized.

Copy link
Member Author

pnkfelix commented Oct 22, 2014

agh failed make tidy ... will fix pronto.

(okay, fixed now; earlier review request still stands. :)

@pnkfelix

This comment has been minimized.

Copy link
Member Author

pnkfelix commented Oct 22, 2014

(on reflection, I'm not sure why I thought printing from drop would be any better than failing from drop. I will revise those to just call fail as well.)

ExchangeHeapSingleton { _force_singleton: () };

/// This the singleton type used solely for `boxed::HEAP`.
pub struct ExchangeHeapSingleton { _force_singleton: () }

This comment has been minimized.

@thestinger

thestinger Oct 22, 2014

Contributor

The term exchange heap really shouldn't be used. It's not how dynamic allocation works in Rust. It's wrong to refer to this as HEAP at all. The Box<T> type represents a dynamic allocation from the same allocator as other types like Vec<T> and Rc<T>. Rust doesn't draw any distinction between memory on a "heap" and other memory mappings.

@thestinger

This comment has been minimized.

Copy link
Contributor

thestinger commented Oct 22, 2014

The box placement protocol should be designed with the same flexibility as placement new / perfect forwarding in C++. It should be possible to push onto a vector by building the element in-place rather than constructing it and copying it inside. I don't think these concerns are being addressed by the design effort put into box, so I'm against progressing with it further.

@thestinger

This comment has been minimized.

Copy link
Contributor

thestinger commented Oct 22, 2014

There needs to be a clear plan on how this is going to work for use cases like vector or hash table inserts before I'm comfortable with it, because I don't want to see Rust end up with an inadequate language feature that needs to be duplicated.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Oct 23, 2014

@thestinger Good point, that is an important use case. I don't see any trouble expressing something like emplace_back. (In fact, @pnkfelix just pushed a commit showing how it can be done: b293984). What do you think? In any case, as @pnkfelix said, we plan to write an RFC, so it'd be great to get a good list of any other requirements you have in mind so that we can write up how the design handles them.

@thestinger

This comment has been minimized.

Copy link
Contributor

thestinger commented Oct 23, 2014

If it can support emplace_back then I think it's flexible enough to cover most use cases of perfect forwarding / placement new in C++. I can't think of another major use case for it at the moment.

Switched Placer API to take `&mut self` rather than `&self`.
This made some things cleaner (e.g. I got to get rid of the `Cell` in
the `AtomPool` example). But it also highlights some weaknesses in my
current expansion -- namely, my use of top-level functions rather than
methods means that I am currently forced to make sure I have the right
level of borrowing in the PLACER input in `box (PLACER) EXPR`. I will
look into my options there -- it may be that the right thing is just
to switch to method invocations.

@pnkfelix pnkfelix referenced this pull request Oct 26, 2014

Open

How `box` should work #413

@nikomatsakis nikomatsakis referenced this pull request Oct 27, 2014

Closed

placement box #11779

@nikomatsakis nikomatsakis self-assigned this Oct 30, 2014

/// the fact that the `align_of` intrinsic currently requires the
/// input type to be Sized (which I do not think is strictly
/// necessary).
pub struct IntermediateBox<Sized? T>{

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

Since this is public, we'll be committing to the continued existence of this type, at least. I'm ok with this but cc @aturon. We'll probably want to bikeshed the name :)

This comment has been minimized.

@pnkfelix

pnkfelix Nov 6, 2014

Author Member

(Of course there's no way to avoid having this be public, at least not without private trait methods at the very least...)

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

In particular I expect we want to reuse the "agent" term

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

On Thu, Nov 06, 2014 at 07:48:41AM -0800, Felix S Klock II wrote:

(Of course there's no way to avoid having this be public, at least not without private trait methods at the very least...)

Yes. And I think that's ok.

heap::EMPTY as *mut u8
} else {
unsafe {
heap::allocate(size, align)

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

heap::allocate() can return null on failure, we should check for this and panic

fn drop(&mut self) {
if self.size > 0 {
unsafe {
heap::deallocate(self.ptr as *mut u8, self.size, self.align)

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

why the cast here? seems unnecessary.

This comment has been minimized.

@pnkfelix

pnkfelix Nov 6, 2014

Author Member

(agreed, that was probably an artifact of an earlier version of the IntermediateBox structure.)


impl<T: Default> Default for Box<T> {
fn default() -> Box<T> { box Default::default() }
fn default() -> Box<T> {
box (HEAP) { let t : T = Default::default(); t }

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

does it not work to just right box (HEAP) Default::default()?

This comment has been minimized.

@pnkfelix

pnkfelix Nov 6, 2014

Author Member

I think the type-inference applied to the desugaring might reject that, but I'll double check.


/// Some free functions called by expansions of `box <value_expr>` and
/// `box (<place>) <value_expr>`.
pub mod placer {

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

Is there a way for us to mark these macro-artifacts as unstable? cc @aturon

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

This may be a moot point now that UFCS support has landed.

/// order to avoid requiring (or expanding into) declarations like
/// `use std::ops::Placer;` and `use std::ops::PlacementAgent;`

/// Calls `p.make_place()` as work-around for lack of UFCS.

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

We have UFCS now

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

So I imagine we can remove make_place

}

/// Calls `a.pointer()` as work-around for lack of UFCS.
pub unsafe fn pointer<Sized? Data, Owner, A>(a: &A) -> *mut Data

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

Also pointer

}

/// Calls `a.finalize()` as work-around for lack of UFCS.
pub unsafe fn finalize<Sized? Data, Owner, A>(a: A) -> Owner

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

And finalize


/// The first argument, `<place>` in `box (<place>) <value>`, must
/// implement `Placer`.
pub fn parenthesized_input_to_box_must_be_placer<'a, Sized? Data, Owner, A, P>(p: &'a mut P) -> &'a mut P

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

...with UFCS, I suspect we don't need this.

This comment has been minimized.

@pnkfelix

pnkfelix Nov 14, 2014

Author Member

you're right; I had thought we would need e.g. impl Trait to assert that the input placer expression had a suitable type, but we can make the assertion via UFCS. The only question is whether the resulting error messages will be usable. We will see about that.

/// Interface to user-implementations of `box (<placer_expr>) <value_expr>`.
///
/// `box (P) V` effectively expands into:
/// `{ let b = P.make_place(); let v = V; unsafe { b.pointer() = v; b.finalize() } }`

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

Only question is whether b.pointer() should be evaluated before V or not. Also, we should add a FIXME regarding the matter of temporary lifetimes.

This comment has been minimized.

@pnkfelix

pnkfelix Nov 14, 2014

Author Member

I switched b.pointer() to evaluate before V.

Can you refresh my memory as to which matter of temporary lifetimes you were referring to here?

This comment has been minimized.

@pnkfelix

pnkfelix Nov 14, 2014

Author Member

(oh, maybe that was about the "vector should be tied up" note below... which may indeed go away with the revisions to take the placer by value in Placer::make_place(self);)

@@ -501,7 +501,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,TYPER> {
}

ast::ExprBox(ref place, ref base) => {
self.consume_expr(&**place);
place.as_ref().map(|e|self.consume_expr(&**e));

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

The expr place is not consumed, actually, but rather &mut borrowed, no? Probably this doesn't matter because anything with a place that is not None winds up being macro-expanded away, in which case maybe we can just add a call to span_bug

This comment has been minimized.

@pnkfelix

pnkfelix Nov 14, 2014

Author Member

i was just blindly doing a mechanical transformation here. (But now I think we've further revised the protocol so that make_place takes self by-value ... and of course yes, in principle we should never get here except with place = None, so I will just put the asserting span_bug in.)

// implement the Placer trait. Likewise, the actual
// expansion below actually calls free functions in
// `std::ops::placer`, in order to avoid the need for UFCS
// (and also to hopefully produce better error messags).

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

I think just using Placer::place(&mut <place_expr>) and so on will have a similar effect

// mod ptr { pub use ... as write; } }
// ```
//
// as appropriate.

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

So sad. What are the other cases that do similar things? I guess it is backwards compat to fix this, so it's just fine.

This comment has been minimized.

@pnkfelix

pnkfelix Nov 14, 2014

Author Member

You can see the other cases listed with a grep like this:

% ack 'mod std {' lib*/lib.rs
liballoc/lib.rs
129:mod std {

libcollections/lib.rs
114:mod std {

libcore/lib.rs
139:mod std {

librand/lib.rs
488:mod std {

librustrt/lib.rs
171:mod std {

libstd/lib.rs
261:mod std {

libsync/lib.rs
78:mod std {

libunicode/lib.rs
80:mod std {

In libcollections it is even nice enough to point out the motivating cases:

#[cfg(not(test))]
mod std {
    pub use core::fmt;      // necessary for panic!()
    pub use core::option;   // necessary for panic!()
    pub use core::clone;    // deriving(Clone)
    pub use core::cmp;      // deriving(Eq, Ord, etc.)
    pub use hash;           // deriving(Hash)
}
// So for now pnkfelix is leaving out the `force_placer`
// invocation, which means we may get different type
// inference failures that are hard to diagnose, but at
// least we will be able to get something running.

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

odd but hopefully irrelevant

struct EmplaceBackAgent<T> {
vec_ptr: *mut Vec<T>,
offset: uint,
}

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

I feel like this agent should carry a lifetime that ensures that the vector is "tied up" somehow

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

probably it just wants to carry an &mut Vec<T>

This comment has been minimized.

@gereeter

gereeter Nov 6, 2014

Contributor

With the current design, make_place could be called repeatedly, and so that would result in aliasing &muts. However, there are other problems with calling make_place repeatedly. Since reserve_additional reserves space relative to the length of the vector, multiple make_places will only reserve space for a single extra element. The asserts in .pointer prevent unsafety, but it still is an issue. Arguably worse, the implication of the description of .pointer implies that different PlacementAgents should return different pointers, but this is violated by having multiple EmplaceBackAgents on the same Vec.

Personally, I'm in favor of just making make_place take the Placer by value. This shouldn't hurt ergonomics, as HEAP and RC would be Copy.

// we had some space reserved, it did not touch the state of
// the vector itself.
}
}

This comment has been minimized.

@nikomatsakis

nikomatsakis Nov 6, 2014

Contributor

can you move this emplace_back implementation into the auxiliary directory, and transform this test to test that it executes correctly, and add a compile-fail test that tries to do something bogus like:

let mut v: Vec<int> = vec![];
box(v.emplace_back()) { v.push(22); 23 }
@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Nov 6, 2014

It probably makes sense to leave these for follow-up PRs, but it'd be nice to have placer implementations for:

  1. Rc
  2. Arc
  3. &TypedArena (as discussed)
  4. emplace_back, perhaps something with hashtable insert, etc

We should perhaps make a tracker issue (after this lands) for those sorts of things. Not a huge deal as they can be added as we like.

@pnkfelix

This comment has been minimized.

Copy link
Member Author

pnkfelix commented Nov 6, 2014

(added link to Proto RFC on discuss to the description; you can see it here: http://discuss.rust-lang.org/t/pre-rfc-placement-box-with-placer-trait/729/5 )

@steveklabnik

This comment has been minimized.

Copy link
Member

steveklabnik commented Dec 2, 2014

This needs a rebase.

@pnkfelix

This comment has been minimized.

Copy link
Member Author

pnkfelix commented Dec 16, 2014

(closing; I no longer think this is on the 1.0 timeframe)

@pnkfelix pnkfelix closed this Dec 16, 2014

@pnkfelix

This comment has been minimized.

Copy link
Member Author

pnkfelix commented Mar 11, 2015

see #22086

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.