prototype Placer protocol for unstable overloaded-box and placement-in.
This commit is contained in:
parent
1829fa5199
commit
866250c6d4
@ -55,13 +55,16 @@
|
||||
|
||||
use core::prelude::*;
|
||||
|
||||
use heap;
|
||||
|
||||
use core::any::Any;
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::hash::{self, Hash};
|
||||
use core::marker::Unsize;
|
||||
use core::marker::{self, Unsize};
|
||||
use core::mem;
|
||||
use core::ops::{CoerceUnsized, Deref, DerefMut};
|
||||
use core::ops::{Placer, Boxed, Place, InPlace, BoxPlace};
|
||||
use core::ptr::Unique;
|
||||
use core::raw::{TraitObject};
|
||||
|
||||
@ -83,7 +86,12 @@
|
||||
#[lang = "exchange_heap"]
|
||||
#[unstable(feature = "box_heap",
|
||||
reason = "may be renamed; uncertain about custom allocator design")]
|
||||
pub const HEAP: () = ();
|
||||
pub const HEAP: ExchangeHeapSingleton =
|
||||
ExchangeHeapSingleton { _force_singleton: () };
|
||||
|
||||
/// This the singleton type used solely for `boxed::HEAP`.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ExchangeHeapSingleton { _force_singleton: () }
|
||||
|
||||
/// A pointer type for heap allocation.
|
||||
///
|
||||
@ -91,7 +99,97 @@
|
||||
#[lang = "owned_box"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[fundamental]
|
||||
pub struct Box<T>(Unique<T>);
|
||||
pub struct Box<T: ?Sized>(Unique<T>);
|
||||
|
||||
/// `IntermediateBox` represents uninitialized backing storage for `Box`.
|
||||
///
|
||||
/// FIXME (pnkfelix): Ideally we would just reuse `Box<T>` instead of
|
||||
/// introducing a separate `IntermediateBox<T>`; but then you hit
|
||||
/// issues when you e.g. attempt to destructure an instance of `Box`,
|
||||
/// since it is a lang item and so it gets special handling by the
|
||||
/// compiler. Easier just to make this parallel type for now.
|
||||
///
|
||||
/// FIXME (pnkfelix): Currently the `box` protocol only supports
|
||||
/// creating instances of sized types. This IntermediateBox is
|
||||
/// designed to be forward-compatible with a future protocol that
|
||||
/// supports creating instances of unsized types; that is why the type
|
||||
/// parameter has the `?Sized` generalization marker, and is also why
|
||||
/// this carries an explicit size. However, it probably does not need
|
||||
/// to carry the explicit alignment; that is just a work-around for
|
||||
/// the fact that the `align_of` intrinsic currently requires the
|
||||
/// input type to be Sized (which I do not think is strictly
|
||||
/// necessary).
|
||||
#[unstable(feature = "placement_in", reason = "placement box design is still being worked out.")]
|
||||
pub struct IntermediateBox<T: ?Sized>{
|
||||
ptr: *mut u8,
|
||||
size: usize,
|
||||
align: usize,
|
||||
marker: marker::PhantomData<*mut T>,
|
||||
}
|
||||
|
||||
impl<T> Place<T> for IntermediateBox<T> {
|
||||
fn pointer(&mut self) -> *mut T {
|
||||
unsafe { ::core::mem::transmute(self.ptr) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn finalize<T>(b: IntermediateBox<T>) -> Box<T> {
|
||||
let p = b.ptr as *mut T;
|
||||
mem::forget(b);
|
||||
mem::transmute(p)
|
||||
}
|
||||
|
||||
fn make_place<T>() -> IntermediateBox<T> {
|
||||
let size = mem::size_of::<T>();
|
||||
let align = mem::align_of::<T>();
|
||||
|
||||
let p = if size == 0 {
|
||||
heap::EMPTY as *mut u8
|
||||
} else {
|
||||
let p = unsafe {
|
||||
heap::allocate(size, align)
|
||||
};
|
||||
if p.is_null() {
|
||||
panic!("Box make_place allocation failure.");
|
||||
}
|
||||
p
|
||||
};
|
||||
|
||||
IntermediateBox { ptr: p, size: size, align: align, marker: marker::PhantomData }
|
||||
}
|
||||
|
||||
impl<T> BoxPlace<T> for IntermediateBox<T> {
|
||||
fn make_place() -> IntermediateBox<T> { make_place() }
|
||||
}
|
||||
|
||||
impl<T> InPlace<T> for IntermediateBox<T> {
|
||||
type Owner = Box<T>;
|
||||
unsafe fn finalize(self) -> Box<T> { finalize(self) }
|
||||
}
|
||||
|
||||
impl<T> Boxed for Box<T> {
|
||||
type Data = T;
|
||||
type Place = IntermediateBox<T>;
|
||||
unsafe fn finalize(b: IntermediateBox<T>) -> Box<T> { finalize(b) }
|
||||
}
|
||||
|
||||
impl<T> Placer<T> for ExchangeHeapSingleton {
|
||||
type Place = IntermediateBox<T>;
|
||||
|
||||
fn make_place(self) -> IntermediateBox<T> {
|
||||
make_place()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for IntermediateBox<T> {
|
||||
fn drop(&mut self) {
|
||||
if self.size > 0 {
|
||||
unsafe {
|
||||
heap::deallocate(self.ptr, self.size, self.align)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Box<T> {
|
||||
/// Allocates memory on the heap and then moves `x` into it.
|
||||
@ -199,8 +297,7 @@ impl<T: Clone> Clone for Box<T> {
|
||||
/// let y = x.clone();
|
||||
/// ```
|
||||
#[inline]
|
||||
fn clone(&self) -> Box<T> { box {(**self).clone()} }
|
||||
|
||||
fn clone(&self) -> Box<T> { box (HEAP) {(**self).clone()} }
|
||||
/// Copies `source`'s contents into `self` without creating a new allocation.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -1266,3 +1266,115 @@ impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *mut T {}
|
||||
|
||||
// *const T -> *const U
|
||||
impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
|
||||
|
||||
/// Both `in (PLACE) EXPR` and `box EXPR` desugar into expressions
|
||||
/// that allocate an intermediate "place" that holds uninitialized
|
||||
/// state. The desugaring evaluates EXPR, and writes the result at
|
||||
/// the address returned by the `pointer` method of this trait.
|
||||
///
|
||||
/// A `Place` can be thought of as a special representation for a
|
||||
/// hypothetical `&uninit` reference (which Rust cannot currently
|
||||
/// express directly). That is, it represents a pointer to
|
||||
/// uninitialized storage.
|
||||
///
|
||||
/// The client is responsible for two steps: First, initializing the
|
||||
/// payload (it can access its address via `pointer`). Second,
|
||||
/// converting the agent to an instance of the owning pointer, via the
|
||||
/// appropriate `finalize` method (see the `InPlace`.
|
||||
///
|
||||
/// If evaluating EXPR fails, then the destructor for the
|
||||
/// implementation of Place to clean up any intermediate state
|
||||
/// (e.g. deallocate box storage, pop a stack, etc).
|
||||
pub trait Place<Data: ?Sized> {
|
||||
/// Returns the address where the input value will be written.
|
||||
/// Note that the data at this address is generally uninitialized,
|
||||
/// and thus one should use `ptr::write` for initializing it.
|
||||
fn pointer(&mut self) -> *mut Data;
|
||||
}
|
||||
|
||||
/// Interface to implementations of `in (PLACE) EXPR`.
|
||||
///
|
||||
/// `in (PLACE) EXPR` effectively desugars into:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// let p = PLACE;
|
||||
/// let mut place = Placer::make_place(p);
|
||||
/// let raw_place = Place::pointer(&mut place);
|
||||
/// let value = EXPR;
|
||||
/// unsafe {
|
||||
/// std::ptr::write(raw_place, value);
|
||||
/// InPlace::finalize(place)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The type of `in (PLACE) EXPR` is derived from the type of `PLACE`;
|
||||
/// if the type of `PLACE` is `P`, then the final type of the whole
|
||||
/// expression is `P::Place::Owner` (see the `InPlace` and `Boxed`
|
||||
/// traits).
|
||||
///
|
||||
/// Values for types implementing this trait usually are transient
|
||||
/// intermediate values (e.g. the return value of `Vec::emplace_back`)
|
||||
/// or `Copy`, since the `make_place` method takes `self` by value.
|
||||
pub trait Placer<Data: ?Sized> {
|
||||
/// `Place` is the intermedate agent guarding the
|
||||
/// uninitialized state for `Data`.
|
||||
type Place: InPlace<Data>;
|
||||
|
||||
/// Creates a fresh place from `self`.
|
||||
fn make_place(self) -> Self::Place;
|
||||
}
|
||||
|
||||
/// Specialization of `Place` trait supporting `in (PLACE) EXPR`.
|
||||
pub trait InPlace<Data: ?Sized>: Place<Data> {
|
||||
/// `Owner` is the type of the end value of `in (PLACE) EXPR`
|
||||
///
|
||||
/// Note that when `in (PLACE) EXPR` is solely used for
|
||||
/// side-effecting an existing data-structure,
|
||||
/// e.g. `Vec::emplace_back`, then `Owner` need not carry any
|
||||
/// information at all (e.g. it can be the unit type `()` in that
|
||||
/// case).
|
||||
type Owner;
|
||||
|
||||
/// Converts self into the final value, shifting
|
||||
/// deallocation/cleanup responsibilities (if any remain), over to
|
||||
/// the returned instance of `Owner` and forgetting self.
|
||||
unsafe fn finalize(self) -> Self::Owner;
|
||||
}
|
||||
|
||||
/// Core trait for the `box EXPR` form.
|
||||
///
|
||||
/// `box EXPR` effectively desugars into:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// let mut place = BoxPlace::make_place();
|
||||
/// let raw_place = Place::pointer(&mut place);
|
||||
/// let value = EXPR;
|
||||
/// unsafe {
|
||||
/// ::std::ptr::write(raw_place, value);
|
||||
/// Boxed::finalize(place)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The type of `box EXPR` is supplied from its surrounding
|
||||
/// context; in the above expansion, the result type `T` is used
|
||||
/// to determine which implementation of `Boxed` to use, and that
|
||||
/// `<T as Boxed>` in turn dictates determines which
|
||||
/// implementation of `BoxPlace` to use, namely:
|
||||
/// `<<T as Boxed>::Place as BoxPlace>`.
|
||||
pub trait Boxed {
|
||||
/// The kind of data that is stored in this kind of box.
|
||||
type Data; /* (`Data` unused b/c cannot yet express below bound.) */
|
||||
/// The place that will negotiate the storage of the data.
|
||||
type Place; /* should be bounded by BoxPlace<Self::Data> */
|
||||
|
||||
/// Converts filled place into final owning value, shifting
|
||||
/// deallocation/cleanup responsibilities (if any remain), over to
|
||||
/// returned instance of `Self` and forgetting `filled`.
|
||||
unsafe fn finalize(filled: Self::Place) -> Self;
|
||||
}
|
||||
|
||||
/// Specialization of `Place` trait supporting `box EXPR`.
|
||||
pub trait BoxPlace<Data: ?Sized> : Place<Data> {
|
||||
/// Creates a globally fresh place.
|
||||
fn make_place() -> Self;
|
||||
}
|
||||
|
@ -555,6 +555,11 @@ pub fn walk_expr(&mut self, expr: &ast::Expr) {
|
||||
None => {}
|
||||
}
|
||||
self.consume_expr(&**base);
|
||||
if place.is_some() {
|
||||
self.tcx().sess.span_bug(
|
||||
expr.span,
|
||||
"box with explicit place remains after expansion");
|
||||
}
|
||||
}
|
||||
|
||||
ast::ExprMac(..) => {
|
||||
|
@ -33,6 +33,16 @@
|
||||
use visit::Visitor;
|
||||
use std_inject;
|
||||
|
||||
// Given suffix ["b","c","d"], returns path `::std::b::c::d` when
|
||||
// `fld.cx.use_std`, and `::core::b::c::d` otherwise.
|
||||
fn mk_core_path(fld: &mut MacroExpander,
|
||||
span: Span,
|
||||
suffix: &[&'static str]) -> ast::Path {
|
||||
let mut idents = vec![fld.cx.ident_of_std("core")];
|
||||
for s in suffix.iter() { idents.push(fld.cx.ident_of(*s)); }
|
||||
fld.cx.path_global(span, idents)
|
||||
}
|
||||
|
||||
pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
|
||||
fn push_compiler_expansion(fld: &mut MacroExpander, span: Span, expansion_desc: &str) {
|
||||
fld.cx.bt_push(ExpnInfo {
|
||||
@ -47,6 +57,7 @@ fn push_compiler_expansion(fld: &mut MacroExpander, span: Span, expansion_desc:
|
||||
}
|
||||
|
||||
e.and_then(|ast::Expr {id, node, span}| match node {
|
||||
|
||||
// expr_mac should really be expr_ext or something; it's the
|
||||
// entry-point for all syntax extensions.
|
||||
ast::ExprMac(mac) => {
|
||||
@ -71,6 +82,109 @@ fn push_compiler_expansion(fld: &mut MacroExpander, span: Span, expansion_desc:
|
||||
})
|
||||
}
|
||||
|
||||
// Desugar ExprBox: `in (PLACE) EXPR`
|
||||
ast::ExprBox(Some(placer), value_expr) => {
|
||||
// to:
|
||||
//
|
||||
// let p = PLACE;
|
||||
// let mut place = Placer::make_place(p);
|
||||
// let raw_place = InPlace::pointer(&mut place);
|
||||
// let value = EXPR;
|
||||
// unsafe {
|
||||
// std::ptr::write(raw_place, value);
|
||||
// InPlace::finalize(place)
|
||||
// }
|
||||
|
||||
let value_span = value_expr.span;
|
||||
let placer_span = placer.span;
|
||||
|
||||
let placer_expr = fld.fold_expr(placer);
|
||||
let value_expr = fld.fold_expr(value_expr);
|
||||
|
||||
let placer_ident = token::gensym_ident("placer");
|
||||
let agent_ident = token::gensym_ident("place");
|
||||
let value_ident = token::gensym_ident("value");
|
||||
let p_ptr_ident = token::gensym_ident("p_ptr");
|
||||
|
||||
let placer = fld.cx.expr_ident(span, placer_ident);
|
||||
let agent = fld.cx.expr_ident(span, agent_ident);
|
||||
let value = fld.cx.expr_ident(span, value_ident);
|
||||
let p_ptr = fld.cx.expr_ident(span, p_ptr_ident);
|
||||
|
||||
let make_place = ["ops", "Placer", "make_place"];
|
||||
let place_pointer = ["ops", "Place", "pointer"];
|
||||
let ptr_write = ["ptr", "write"];
|
||||
let inplace_finalize = ["ops", "InPlace", "finalize"];
|
||||
|
||||
let make_call = |fld: &mut MacroExpander, p, args| {
|
||||
let path = mk_core_path(fld, placer_span, p);
|
||||
let path = fld.cx.expr_path(path);
|
||||
fld.cx.expr_call(span, path, args)
|
||||
};
|
||||
|
||||
let stmt_let = |fld: &mut MacroExpander, bind, expr| {
|
||||
fld.cx.stmt_let(placer_span, false, bind, expr)
|
||||
};
|
||||
let stmt_let_mut = |fld: &mut MacroExpander, bind, expr| {
|
||||
fld.cx.stmt_let(placer_span, true, bind, expr)
|
||||
};
|
||||
|
||||
// let placer = <placer_expr> ;
|
||||
let s1 = stmt_let(fld, placer_ident, placer_expr);
|
||||
|
||||
// let mut place = Placer::make_place(placer);
|
||||
let s2 = {
|
||||
let call = make_call(fld, &make_place, vec![placer]);
|
||||
stmt_let_mut(fld, agent_ident, call)
|
||||
};
|
||||
|
||||
// let p_ptr = Place::pointer(&mut place);
|
||||
let s3 = {
|
||||
let args = vec![fld.cx.expr_mut_addr_of(placer_span, agent.clone())];
|
||||
let call = make_call(fld, &place_pointer, args);
|
||||
stmt_let(fld, p_ptr_ident, call)
|
||||
};
|
||||
|
||||
// let value = <value_expr>;
|
||||
let s4 = fld.cx.stmt_let(value_span, false, value_ident, value_expr);
|
||||
|
||||
// unsafe { ptr::write(p_ptr, value); InPlace::finalize(place) }
|
||||
let expr = {
|
||||
let call_ptr_write = StmtSemi(make_call(
|
||||
fld, &ptr_write, vec![p_ptr, value]), ast::DUMMY_NODE_ID);
|
||||
let call_ptr_write = codemap::respan(value_span, call_ptr_write);
|
||||
|
||||
let call = make_call(fld, &inplace_finalize, vec![agent]);
|
||||
Some(fld.cx.expr_block(P(ast::Block {
|
||||
stmts: vec![P(call_ptr_write)],
|
||||
expr: Some(call),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
rules: ast::UnsafeBlock(ast::CompilerGenerated),
|
||||
span: span,
|
||||
})))
|
||||
};
|
||||
|
||||
let block = fld.cx.block_all(span, vec![s1, s2, s3, s4], expr);
|
||||
fld.cx.expr_block(block)
|
||||
}
|
||||
|
||||
// Issue #22181:
|
||||
// Eventually a desugaring for `box EXPR`
|
||||
// (similar to the desugaring above for `in PLACE BLOCK`)
|
||||
// should go here, desugaring
|
||||
//
|
||||
// to:
|
||||
//
|
||||
// let mut place = BoxPlace::make_place();
|
||||
// let raw_place = Place::pointer(&mut place);
|
||||
// let value = $value;
|
||||
// unsafe {
|
||||
// ::std::ptr::write(raw_place, value);
|
||||
// Boxed::finalize(place)
|
||||
// }
|
||||
//
|
||||
// But for now there are type-inference issues doing that.
|
||||
|
||||
ast::ExprWhile(cond, body, opt_ident) => {
|
||||
let cond = fld.fold_expr(cond);
|
||||
let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
|
||||
|
Loading…
Reference in New Issue
Block a user