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

desugaring-based box placement-in (take-4 branch) #26180

Closed
wants to merge 36 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6a82cca
Added `Box<_>` type annotations where necessary for overloaded-box.
pnkfelix Mar 6, 2015
1d86250
prototype Placer protocol for unstable overloaded-box and placement-in.
pnkfelix Jan 27, 2015
f773341
Add feature-gates for desugaring-based `box` and placement-`in`.
pnkfelix Feb 12, 2015
ca73eb8
Accommodate error message explosion from box-desugaring in some tests.
pnkfelix Feb 16, 2015
a7ef6f4
Update tests for desugaring box and placement-in.
pnkfelix Feb 16, 2015
a807ef4
Issue 22450: Address desugaring-box problems in [pretty] run-pass tes…
pnkfelix Feb 17, 2015
fa0ec8c
Workaround issue 22462 by moving static `value` into its own module.
pnkfelix Feb 17, 2015
6988850
update alloc::boxed post rebase.
pnkfelix Jun 4, 2015
3ec60e8
update liballoc/lib.rs post rebase.
pnkfelix Jun 4, 2015
6243ce3
Hack for "unsafety hygiene" -- `push_unsafe!` and `pop_unsafe!` track…
pnkfelix Jun 5, 2015
0530a53
Change signature for `move_val_init` intrinsic to take `*mut T` for `…
pnkfelix Jun 5, 2015
c364f04
Avoid feature-warnings on stage0.
pnkfelix Jun 5, 2015
70343c8
Revise placement-in expansion to use `push/pop_unsafe` and `move_val_…
pnkfelix Jun 5, 2015
69056cb
call `push_compiler_expansion` during the `box` and placement-`in` ex…
pnkfelix Jun 5, 2015
3ab0ebe
turn on use of sugaring based `box EXPR` in parser.
pnkfelix Jun 5, 2015
bfe8884
Instrument `rustc::middle::stability::maybe_do_stability_checking`
pnkfelix Jun 5, 2015
74676b2
Add `Box::new` in spots where new box protocol needs help with infere…
pnkfelix Jun 5, 2015
a8ef3c4
Allow unstable code to be injected by `box` and placement-`in` expans…
pnkfelix Jun 5, 2015
5d805b0
librustc: Replace `box` with `Box::new` to assist inference for box p…
pnkfelix Jun 6, 2015
4d1635d
librustc_driver: Replace `box` with `Box::new` to assist inference fo…
pnkfelix Jun 6, 2015
7d77257
librustc_lint: Replace `box` with `Box::new` to assist inference for …
pnkfelix Jun 6, 2015
0ec5b63
librustc_trans: Replace `box` with `Box::new` to assist inference for…
pnkfelix Jun 6, 2015
52229f7
librustc_llvm: Replace `box` with `Box::new` to assist inference for …
pnkfelix Jun 6, 2015
91cf845
librustdoc: Replace `box` with `Box::new` to assist inference for box…
pnkfelix Jun 6, 2015
19020ef
switch to `box expr` form in liblog.
pnkfelix Jun 6, 2015
fa89d84
Add `Box<_>` annotations to run-pass tests for box protocol.
pnkfelix Jun 6, 2015
62650f9
Switched to `Box::new` in run-fail test.
pnkfelix Jun 6, 2015
474c00c
Accommodate box protocol in compile-fail tests.
pnkfelix Jun 6, 2015
30db8cb
add some (potentially unnecessary) `#[inline] annotations in `alloc::…
pnkfelix Jun 6, 2015
36ebfeb
accommodate `box` protocol in `run-pass-valgrind`.
pnkfelix Jun 6, 2015
6caa526
Remove dependences on `box`-protocol from run-pass-fulldeps.
pnkfelix Jun 6, 2015
4d80e03
Fix `box`-protocol for run-make tests.
pnkfelix Jun 6, 2015
1b8813d
Placate tidy, mostly in test suite code.
pnkfelix Jun 8, 2015
ef6d5e3
Placate box protocol by switching to `Box::new` in various test code.
pnkfelix Jun 8, 2015
afd9b94
Revise lang_item demo to something unrelated to Box impl
pnkfelix Jun 9, 2015
a61490c
revert back to the old syntax while landing the protocol alone.
pnkfelix Jun 9, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 77 additions & 35 deletions src/doc/trpl/lang-items.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,69 +11,111 @@ it exists. The marker is the attribute `#[lang = "..."]` and there are
various different values of `...`, i.e. various different 'lang
items'.

For example, `Box` pointers require two lang items, one for allocation
and one for deallocation. A freestanding program that uses the `Box`
sugar for dynamic allocations via `malloc` and `free`:
For example, some traits in the standard library have special
treatment. One is `Copy`: there is a `copy` lang-item attached to the
`Copy` trait, and the Rust compiler treats this specially in two ways:

```rust
#![feature(lang_items, box_syntax, start, no_std, libc)]
#![no_std]
1. If `x` has type that implements `Copy`, then `x` can be freely
copied by the assignment operator:
```rust,ignore
let y = x;
let z = x;
```

extern crate libc;
2. If a type tries to implement `Copy`, the Rust compiler will
ensure that duplicating a value of that type will not cause any
noncopyable type to be duplicated.

extern {
fn abort() -> !;
}
For example, this code will be rejected:

#[lang = "owned_box"]
pub struct Box<T>(*mut T);
```rust,ignore
#[derive(Clone)]
struct ThisTypeIsNotCopy(Box<i32>);

#[lang = "exchange_malloc"]
unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
let p = libc::malloc(size as libc::size_t) as *mut u8;
#[derive(Clone)]
struct TryToMakeThisCopy { okay: i32, not_okay: ThisTypeIsNotCopy }

// malloc failed
if p as usize == 0 {
abort();
}
// This attempt to implement `Copy` will fail.
impl Copy for TryToMakeThisCopy { }
```

p
}
#[lang = "exchange_free"]
unsafe fn deallocate(ptr: *mut u8, _size: usize, _align: usize) {
libc::free(ptr as *mut libc::c_void)
The above two properties are both special qualities of the `Copy`
trait that other traits do not share, and thus they are associated
with whatever trait is given the `copy` lang item.

Here is a freestanding program that provides its own definition of the
`copy` lang item, that is slightly different than the definition in
the Rust standard library:

```
#![feature(lang_items, intrinsics, start, no_std)]
#![no_std]

#[lang = "copy"]
pub trait MyCopy {
// Empty.
}

#[start]
fn main(argc: isize, argv: *const *const u8) -> isize {
let x = box 1;
struct C(i32, i32);
impl MyCopy for C { }

0
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
let mut x = C(3, 4);
let mut y = x;
let mut z = x;
x.0 = 5;
y.0 = 6;
z.0 = 7;

#[link(name="c")]
extern { fn printf(f: *const u8, ...); }

let template = b"x: C(%d, %d) y: C(%d, %d), z: C(%d, %d)\n\0";
unsafe { printf(template as *const u8, x.0, x.1, y.0, y.1, z.0, z.1); }
return 0;
}

// Again, these functions and traits are used by the compiler, and are
// normally provided by libstd.

#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
#[lang = "eh_personality"] extern fn eh_personality() {}
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }

#[lang="sized"] pub trait Sized: PhantomFn {}
#[lang="phantom_fn"] pub trait PhantomFn {}
```

This compiles successfully. When we run the above code, it prints:
```text
x: C(5, 4) y: C(6, 4), z: C(7, 4)
```
So we can freely copy instances of `C`, since it implements `MyCopy`.

Note the use of `abort`: the `exchange_malloc` lang item is assumed to
return a valid pointer, and so needs to do the check internally.
A potentially interesting detail about the above program is that it
differs from the Rust standard library in more than just the name
`MyCopy`. The `std::marker::Copy` extends `std::clone::Clone`,
ensuring that every type that implements `Copy` has a `clone` method.
The `MyCopy` trait does *not* extend `Clone`; these values have no
`clone` methods.

Other features provided by lang items include:

- overloadable operators via traits: the traits corresponding to the
`==`, `<`, dereferencing (`*`) and `+` (etc.) operators are all
marked with lang items; those specific four are `eq`, `ord`,
`deref`, and `add` respectively.
- stack unwinding and general failure; the `eh_personality`, `fail`
and `fail_bounds_checks` lang items.
- stack unwinding and general failure; the `eh_personality`, `panic`
`panic_fmt`, and `panic_bounds_check` lang items.
- the traits in `std::marker` used to indicate types of
various kinds; lang items `send`, `sync` and `copy`.
- the marker types and variance indicators found in
`std::marker`; lang items `covariant_type`,
`contravariant_lifetime`, etc.
- matching with string literal patterns; the `str_eq` lang item.

Lang items are loaded lazily by the compiler; e.g. if one never uses
`Box` then there is no need to define functions for `exchange_malloc`
and `exchange_free`. `rustc` will emit an error when an item is needed
but not found in the current crate or any that it depends on.
array indexing (`a[i]`) then there is no need to define a function for
`panic_bounds_check`. `rustc` will emit an error when an item is
needed but not found in the current crate or any that it depends on.
125 changes: 115 additions & 10 deletions src/liballoc/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,43 +55,149 @@

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};

/// A value that represents the heap. This is the default place that the `box`
/// keyword allocates into when no place is supplied.
// FIXME (#22181): Put in the new placement-in syntax once that lands.

/// A value that represents the heap. This is the place that the `box`
/// keyword allocates into.
///
/// The following two examples are equivalent:
///
/// ```
/// # #![feature(box_heap)]
/// # #![feature(box_heap, core)]
/// #![feature(box_syntax)]
/// #![feature(placement_in_syntax)]
/// use std::boxed::HEAP;
///
/// fn main() {
/// let foo = box(HEAP) 5;
/// let foo = box 5;
/// let foo = box (HEAP) { 5 };
/// let foo: Box<_> = box 5;
/// }
/// ```
#[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.
///
/// See the [module-level documentation](../../std/boxed/index.html) for more.
#[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> {
// FIXME: what about unsized T?
#[inline]
fn pointer(&mut self) -> *mut T { self.ptr as *mut T }
}

#[inline]
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> {
#[inline]
fn make_place() -> IntermediateBox<T> { make_place() }
}

impl<T> InPlace<T> for IntermediateBox<T> {
type Owner = Box<T>;
#[inline]
unsafe fn finalize(self) -> Box<T> { finalize(self) }
}

impl<T> Boxed for Box<T> {
type Data = T;
type Place = IntermediateBox<T>;
#[inline]
unsafe fn finalize(b: IntermediateBox<T>) -> Box<T> { finalize(b) }
}

impl<T> Placer<T> for ExchangeHeapSingleton {
type Place = IntermediateBox<T>;

#[inline]
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.
Expand Down Expand Up @@ -199,8 +305,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
Expand Down
4 changes: 4 additions & 0 deletions src/liballoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@
test(no_crate_inject))]
#![no_std]

// SNAP ba0e1cd
#![allow(unused_features)] // for placement_in_syntax

#![feature(allocator)]
#![feature(box_syntax)]
#![feature(coerce_unsized)]
Expand All @@ -84,6 +87,7 @@
#![feature(optin_builtin_traits)]
#![feature(raw)]
#![feature(staged_api)]
#![feature(placement_in_syntax)]
#![feature(unboxed_closures)]
#![feature(unique)]
#![feature(unsafe_no_drop_flag, filling_drop)]
Expand Down
8 changes: 8 additions & 0 deletions src/libcore/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,14 @@ extern "rust-intrinsic" {
/// elements.
pub fn size_of<T>() -> usize;

#[cfg(not(stage0))]
/// Moves a value to an uninitialized memory location.
///
/// Drop glue is not run on the destination.
pub fn move_val_init<T>(dst: *mut T, src: T);

// SNAP ba0e1cd
#[cfg(stage0)]
/// Moves a value to an uninitialized memory location.
///
/// Drop glue is not run on the destination.
Expand Down
Loading