Created
February 8, 2015 22:26
-
-
Save pnkfelix/abee2f0cbdca677b6f01 to your computer and use it in GitHub Desktop.
Illustration of type-inference failure when coercing a `box <expr>` to a trait object without using `<expr> as Box<Trait>`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(box_syntax)] | |
use std::cell::RefCell; | |
macro_rules! box_ { | |
($value:expr) => { { | |
let mut place = ::uni_protocol::BoxPlace::make_place(); | |
let raw_place = ::uni_protocol::Place::pointer(&mut place); | |
let value = $value; | |
unsafe { | |
::std::ptr::write(raw_place, value); | |
::uni_protocol::Boxed::finalize(place) | |
} | |
} } | |
} | |
#[cfg(works)] | |
pub fn combine_substructure<'a, F>(f: F) -> RefCell<CombineSubstructureFunc<'a>> | |
where F: FnMut(&mut ExtCtxt, Span, &Substructure) -> P<Expr>, F: 'a | |
{ | |
RefCell::new(box_!( f ) as CombineSubstructureFunc) | |
} | |
#[cfg(not(works))] | |
pub fn combine_substructure<'a, F>(f: F) -> RefCell<CombineSubstructureFunc<'a>> | |
where F: FnMut(&mut ExtCtxt, Span, &Substructure) -> P<Expr>, F: 'a | |
{ | |
RefCell::new(box_!( f )) | |
} | |
fn main() { } | |
pub struct ExtCtxt; | |
pub struct Span; | |
pub struct Substructure; | |
pub struct Expr; | |
pub struct P<T> { _m: std::marker::InvariantType<T> } | |
pub type CombineSubstructure<'a> = FnMut(&mut ExtCtxt, Span, &Substructure) -> P<Expr> + 'a; | |
pub type CombineSubstructureFunc<'a> = Box<CombineSubstructure<'a>>; | |
pub mod uni_protocol { | |
/// 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. | |
/// | |
/// 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; | |
} | |
/// Core trait for the `in (PLACE) EXPR` form. | |
/// | |
/// `in (PLACE) EXPR` effectively desugars into: | |
/// | |
/// ``` | |
/// 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) | |
/// } | |
/// ``` | |
/// | |
/// 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` | |
/// trait). | |
/// | |
/// Values for types implementing this trait must either be | |
/// transient intermediates 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; | |
} | |
/// Specialization of `Place` trait supporting `box EXPR`. | |
pub trait BoxPlace<Data: ?Sized> : Place<Data> { | |
/// Creates a globally fresh place. | |
fn make_place() -> Self; | |
} | |
/// Core trait for the `box EXPR` form. | |
/// | |
/// `box EXPR` effectively desugars into: | |
/// | |
/// ``` | |
/// let mut place = BoxPlace::make_place(); | |
/// let raw_place = BoxPlace::pointer(&mut place); | |
/// let value = $value; | |
/// 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.) */ | |
type Place; /* should be bounded by BoxPlace<Self::Data> */ | |
/// Converts filled place into final value, shifting | |
/// deallocation/cleanup responsibilities (if any remain), | |
/// over to returned instance of `Owner` and forgetting | |
/// `filled`. | |
unsafe fn finalize(filled: Self::Place) -> Self; | |
} | |
} | |
pub struct HEAP; | |
mod impl_box_for_box { | |
#[cfg(old)] | |
use box_protocol as proto; | |
use uni_protocol as proto; | |
use std::mem; | |
use super::HEAP; | |
struct BoxPlace<T> { fake_box: Option<Box<T>> } | |
fn make_place<T>() -> BoxPlace<T> { | |
let t: T = unsafe { mem::zeroed() }; | |
BoxPlace { fake_box: Some(Box::new(t)) } | |
} | |
unsafe fn finalize<T>(mut filled: BoxPlace<T>) -> Box<T> { | |
let mut ret = None; | |
mem::swap(&mut filled.fake_box, &mut ret); | |
ret.unwrap() | |
} | |
impl<'a, T> proto::Placer<T> for HEAP { | |
type Place = BoxPlace<T>; | |
fn make_place(self) -> BoxPlace<T> { make_place() } | |
} | |
impl<T> proto::Place<T> for BoxPlace<T> { | |
fn pointer(&mut self) -> *mut T { | |
match self.fake_box { | |
Some(ref mut b) => &mut **b as *mut T, | |
None => panic!("impossible"), | |
} | |
} | |
} | |
impl<T> proto::BoxPlace<T> for BoxPlace<T> { | |
fn make_place() -> BoxPlace<T> { make_place() } | |
} | |
impl<T> proto::InPlace<T> for BoxPlace<T> { | |
type Owner = Box<T>; | |
unsafe fn finalize(self) -> Box<T> { finalize(self) } | |
} | |
impl<T> proto::Boxed for Box<T> { | |
type Data = T; | |
type Place = BoxPlace<T>; | |
unsafe fn finalize(filled: BoxPlace<T>) -> Self { finalize(filled) } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment