Skip to content

Commit 9413a92

Browse files
committed
Auto merge of #27215 - pnkfelix:fsk-placer-take-5-just-in, r=nikomatsakis
Macro desugaring of `in PLACE { BLOCK }` into "simpler" expressions following the in-development "Placer" protocol. Includes Placer API that one can override to integrate support for `in` into one's own type. (See [RFC 809].) [RFC 809]: https://github.com/rust-lang/rfcs/blob/master/text/0809-box-and-in-for-stdlib.md Part of #22181 Replaced PR #26180. Turns on the `in PLACE { BLOCK }` syntax, while leaving in support for the old `box (PLACE) EXPR` syntax (since we need to support that at least until we have a snapshot with support for `in PLACE { BLOCK }`. (Note that we are not 100% committed to the `in PLACE { BLOCK }` syntax. In particular I still want to play around with some other alternatives. Still, I want to get the fundamental framework for the protocol landed so we can play with implementing it for non `Box` types.) ---- Also, this PR leaves out support for desugaring-based `box EXPR`. We will hopefully land that in the future, but for the short term there are type-inference issues injected by that change that we want to resolve separately.
2 parents 607f74d + d066a7b commit 9413a92

28 files changed

+899
-47
lines changed

Diff for: src/liballoc/boxed.rs

+103-6
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,16 @@
5555

5656
use core::prelude::*;
5757

58+
use heap;
59+
5860
use core::any::Any;
5961
use core::cmp::Ordering;
6062
use core::fmt;
6163
use core::hash::{self, Hash};
62-
use core::marker::Unsize;
64+
use core::marker::{self, Unsize};
6365
use core::mem;
6466
use core::ops::{CoerceUnsized, Deref, DerefMut};
67+
use core::ops::{Placer, Boxed, Place, InPlace, BoxPlace};
6568
use core::ptr::Unique;
6669
use core::raw::{TraitObject};
6770

@@ -72,7 +75,7 @@ use core::raw::{TraitObject};
7275
///
7376
/// ```
7477
/// # #![feature(box_heap)]
75-
/// #![feature(box_syntax)]
78+
/// #![feature(box_syntax, placement_in_syntax)]
7679
/// use std::boxed::HEAP;
7780
///
7881
/// fn main() {
@@ -83,15 +86,110 @@ use core::raw::{TraitObject};
8386
#[lang = "exchange_heap"]
8487
#[unstable(feature = "box_heap",
8588
reason = "may be renamed; uncertain about custom allocator design")]
86-
pub const HEAP: () = ();
89+
pub const HEAP: ExchangeHeapSingleton =
90+
ExchangeHeapSingleton { _force_singleton: () };
91+
92+
/// This the singleton type used solely for `boxed::HEAP`.
93+
#[derive(Copy, Clone)]
94+
pub struct ExchangeHeapSingleton { _force_singleton: () }
8795

8896
/// A pointer type for heap allocation.
8997
///
9098
/// See the [module-level documentation](../../std/boxed/index.html) for more.
9199
#[lang = "owned_box"]
92100
#[stable(feature = "rust1", since = "1.0.0")]
93101
#[fundamental]
94-
pub struct Box<T>(Unique<T>);
102+
pub struct Box<T: ?Sized>(Unique<T>);
103+
104+
/// `IntermediateBox` represents uninitialized backing storage for `Box`.
105+
///
106+
/// FIXME (pnkfelix): Ideally we would just reuse `Box<T>` instead of
107+
/// introducing a separate `IntermediateBox<T>`; but then you hit
108+
/// issues when you e.g. attempt to destructure an instance of `Box`,
109+
/// since it is a lang item and so it gets special handling by the
110+
/// compiler. Easier just to make this parallel type for now.
111+
///
112+
/// FIXME (pnkfelix): Currently the `box` protocol only supports
113+
/// creating instances of sized types. This IntermediateBox is
114+
/// designed to be forward-compatible with a future protocol that
115+
/// supports creating instances of unsized types; that is why the type
116+
/// parameter has the `?Sized` generalization marker, and is also why
117+
/// this carries an explicit size. However, it probably does not need
118+
/// to carry the explicit alignment; that is just a work-around for
119+
/// the fact that the `align_of` intrinsic currently requires the
120+
/// input type to be Sized (which I do not think is strictly
121+
/// necessary).
122+
#[unstable(feature = "placement_in", reason = "placement box design is still being worked out.")]
123+
pub struct IntermediateBox<T: ?Sized>{
124+
ptr: *mut u8,
125+
size: usize,
126+
align: usize,
127+
marker: marker::PhantomData<*mut T>,
128+
}
129+
130+
impl<T> Place<T> for IntermediateBox<T> {
131+
fn pointer(&mut self) -> *mut T {
132+
unsafe { ::core::mem::transmute(self.ptr) }
133+
}
134+
}
135+
136+
unsafe fn finalize<T>(b: IntermediateBox<T>) -> Box<T> {
137+
let p = b.ptr as *mut T;
138+
mem::forget(b);
139+
mem::transmute(p)
140+
}
141+
142+
fn make_place<T>() -> IntermediateBox<T> {
143+
let size = mem::size_of::<T>();
144+
let align = mem::align_of::<T>();
145+
146+
let p = if size == 0 {
147+
heap::EMPTY as *mut u8
148+
} else {
149+
let p = unsafe {
150+
heap::allocate(size, align)
151+
};
152+
if p.is_null() {
153+
panic!("Box make_place allocation failure.");
154+
}
155+
p
156+
};
157+
158+
IntermediateBox { ptr: p, size: size, align: align, marker: marker::PhantomData }
159+
}
160+
161+
impl<T> BoxPlace<T> for IntermediateBox<T> {
162+
fn make_place() -> IntermediateBox<T> { make_place() }
163+
}
164+
165+
impl<T> InPlace<T> for IntermediateBox<T> {
166+
type Owner = Box<T>;
167+
unsafe fn finalize(self) -> Box<T> { finalize(self) }
168+
}
169+
170+
impl<T> Boxed for Box<T> {
171+
type Data = T;
172+
type Place = IntermediateBox<T>;
173+
unsafe fn finalize(b: IntermediateBox<T>) -> Box<T> { finalize(b) }
174+
}
175+
176+
impl<T> Placer<T> for ExchangeHeapSingleton {
177+
type Place = IntermediateBox<T>;
178+
179+
fn make_place(self) -> IntermediateBox<T> {
180+
make_place()
181+
}
182+
}
183+
184+
impl<T: ?Sized> Drop for IntermediateBox<T> {
185+
fn drop(&mut self) {
186+
if self.size > 0 {
187+
unsafe {
188+
heap::deallocate(self.ptr, self.size, self.align)
189+
}
190+
}
191+
}
192+
}
95193

96194
impl<T> Box<T> {
97195
/// Allocates memory on the heap and then moves `x` into it.
@@ -199,8 +297,7 @@ impl<T: Clone> Clone for Box<T> {
199297
/// let y = x.clone();
200298
/// ```
201299
#[inline]
202-
fn clone(&self) -> Box<T> { box {(**self).clone()} }
203-
300+
fn clone(&self) -> Box<T> { box (HEAP) {(**self).clone()} }
204301
/// Copies `source`'s contents into `self` without creating a new allocation.
205302
///
206303
/// # Examples

Diff for: src/liballoc/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@
7070
test(no_crate_inject))]
7171
#![no_std]
7272

73+
// SNAP d4432b3
74+
#![allow(unused_features)] // until feature(placement_in_syntax) is in snap
7375
#![feature(allocator)]
7476
#![feature(box_syntax)]
7577
#![feature(coerce_unsized)]
@@ -82,6 +84,8 @@
8284
#![feature(no_std)]
8385
#![feature(nonzero)]
8486
#![feature(optin_builtin_traits)]
87+
#![feature(placement_in_syntax)]
88+
#![feature(placement_new_protocol)]
8589
#![feature(raw)]
8690
#![feature(staged_api)]
8791
#![feature(unboxed_closures)]

Diff for: src/libcore/intrinsics.rs

+8
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,14 @@ extern "rust-intrinsic" {
184184
/// elements.
185185
pub fn size_of<T>() -> usize;
186186

187+
#[cfg(not(stage0))]
188+
/// Moves a value to an uninitialized memory location.
189+
///
190+
/// Drop glue is not run on the destination.
191+
pub fn move_val_init<T>(dst: *mut T, src: T);
192+
193+
// SNAP d4432b3
194+
#[cfg(stage0)]
187195
/// Moves a value to an uninitialized memory location.
188196
///
189197
/// Drop glue is not run on the destination.

Diff for: src/libcore/ops.rs

+117
Original file line numberDiff line numberDiff line change
@@ -1266,3 +1266,120 @@ impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *mut T {}
12661266

12671267
// *const T -> *const U
12681268
impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
1269+
1270+
/// Both `in (PLACE) EXPR` and `box EXPR` desugar into expressions
1271+
/// that allocate an intermediate "place" that holds uninitialized
1272+
/// state. The desugaring evaluates EXPR, and writes the result at
1273+
/// the address returned by the `pointer` method of this trait.
1274+
///
1275+
/// A `Place` can be thought of as a special representation for a
1276+
/// hypothetical `&uninit` reference (which Rust cannot currently
1277+
/// express directly). That is, it represents a pointer to
1278+
/// uninitialized storage.
1279+
///
1280+
/// The client is responsible for two steps: First, initializing the
1281+
/// payload (it can access its address via `pointer`). Second,
1282+
/// converting the agent to an instance of the owning pointer, via the
1283+
/// appropriate `finalize` method (see the `InPlace`.
1284+
///
1285+
/// If evaluating EXPR fails, then the destructor for the
1286+
/// implementation of Place to clean up any intermediate state
1287+
/// (e.g. deallocate box storage, pop a stack, etc).
1288+
#[unstable(feature = "placement_new_protocol")]
1289+
pub trait Place<Data: ?Sized> {
1290+
/// Returns the address where the input value will be written.
1291+
/// Note that the data at this address is generally uninitialized,
1292+
/// and thus one should use `ptr::write` for initializing it.
1293+
fn pointer(&mut self) -> *mut Data;
1294+
}
1295+
1296+
/// Interface to implementations of `in (PLACE) EXPR`.
1297+
///
1298+
/// `in (PLACE) EXPR` effectively desugars into:
1299+
///
1300+
/// ```rust,ignore
1301+
/// let p = PLACE;
1302+
/// let mut place = Placer::make_place(p);
1303+
/// let raw_place = Place::pointer(&mut place);
1304+
/// let value = EXPR;
1305+
/// unsafe {
1306+
/// std::ptr::write(raw_place, value);
1307+
/// InPlace::finalize(place)
1308+
/// }
1309+
/// ```
1310+
///
1311+
/// The type of `in (PLACE) EXPR` is derived from the type of `PLACE`;
1312+
/// if the type of `PLACE` is `P`, then the final type of the whole
1313+
/// expression is `P::Place::Owner` (see the `InPlace` and `Boxed`
1314+
/// traits).
1315+
///
1316+
/// Values for types implementing this trait usually are transient
1317+
/// intermediate values (e.g. the return value of `Vec::emplace_back`)
1318+
/// or `Copy`, since the `make_place` method takes `self` by value.
1319+
#[unstable(feature = "placement_new_protocol")]
1320+
pub trait Placer<Data: ?Sized> {
1321+
/// `Place` is the intermedate agent guarding the
1322+
/// uninitialized state for `Data`.
1323+
type Place: InPlace<Data>;
1324+
1325+
/// Creates a fresh place from `self`.
1326+
fn make_place(self) -> Self::Place;
1327+
}
1328+
1329+
/// Specialization of `Place` trait supporting `in (PLACE) EXPR`.
1330+
#[unstable(feature = "placement_new_protocol")]
1331+
pub trait InPlace<Data: ?Sized>: Place<Data> {
1332+
/// `Owner` is the type of the end value of `in (PLACE) EXPR`
1333+
///
1334+
/// Note that when `in (PLACE) EXPR` is solely used for
1335+
/// side-effecting an existing data-structure,
1336+
/// e.g. `Vec::emplace_back`, then `Owner` need not carry any
1337+
/// information at all (e.g. it can be the unit type `()` in that
1338+
/// case).
1339+
type Owner;
1340+
1341+
/// Converts self into the final value, shifting
1342+
/// deallocation/cleanup responsibilities (if any remain), over to
1343+
/// the returned instance of `Owner` and forgetting self.
1344+
unsafe fn finalize(self) -> Self::Owner;
1345+
}
1346+
1347+
/// Core trait for the `box EXPR` form.
1348+
///
1349+
/// `box EXPR` effectively desugars into:
1350+
///
1351+
/// ```rust,ignore
1352+
/// let mut place = BoxPlace::make_place();
1353+
/// let raw_place = Place::pointer(&mut place);
1354+
/// let value = EXPR;
1355+
/// unsafe {
1356+
/// ::std::ptr::write(raw_place, value);
1357+
/// Boxed::finalize(place)
1358+
/// }
1359+
/// ```
1360+
///
1361+
/// The type of `box EXPR` is supplied from its surrounding
1362+
/// context; in the above expansion, the result type `T` is used
1363+
/// to determine which implementation of `Boxed` to use, and that
1364+
/// `<T as Boxed>` in turn dictates determines which
1365+
/// implementation of `BoxPlace` to use, namely:
1366+
/// `<<T as Boxed>::Place as BoxPlace>`.
1367+
#[unstable(feature = "placement_new_protocol")]
1368+
pub trait Boxed {
1369+
/// The kind of data that is stored in this kind of box.
1370+
type Data; /* (`Data` unused b/c cannot yet express below bound.) */
1371+
/// The place that will negotiate the storage of the data.
1372+
type Place: BoxPlace<Self::Data>;
1373+
1374+
/// Converts filled place into final owning value, shifting
1375+
/// deallocation/cleanup responsibilities (if any remain), over to
1376+
/// returned instance of `Self` and forgetting `filled`.
1377+
unsafe fn finalize(filled: Self::Place) -> Self;
1378+
}
1379+
1380+
/// Specialization of `Place` trait supporting `box EXPR`.
1381+
#[unstable(feature = "placement_new_protocol")]
1382+
pub trait BoxPlace<Data: ?Sized> : Place<Data> {
1383+
/// Creates a globally fresh place.
1384+
fn make_place() -> Self;
1385+
}

Diff for: src/librustc/middle/effect.rs

+29-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
//! Enforces the Rust effect system. Currently there is just one effect,
1212
//! `unsafe`.
13-
use self::UnsafeContext::*;
13+
use self::RootUnsafeContext::*;
1414

1515
use middle::def;
1616
use middle::ty::{self, Ty};
@@ -21,8 +21,20 @@ use syntax::codemap::Span;
2121
use syntax::visit;
2222
use syntax::visit::Visitor;
2323

24+
#[derive(Copy, Clone)]
25+
struct UnsafeContext {
26+
push_unsafe_count: usize,
27+
root: RootUnsafeContext,
28+
}
29+
30+
impl UnsafeContext {
31+
fn new(root: RootUnsafeContext) -> UnsafeContext {
32+
UnsafeContext { root: root, push_unsafe_count: 0 }
33+
}
34+
}
35+
2436
#[derive(Copy, Clone, PartialEq)]
25-
enum UnsafeContext {
37+
enum RootUnsafeContext {
2638
SafeContext,
2739
UnsafeFn,
2840
UnsafeBlock(ast::NodeId),
@@ -44,7 +56,8 @@ struct EffectCheckVisitor<'a, 'tcx: 'a> {
4456

4557
impl<'a, 'tcx> EffectCheckVisitor<'a, 'tcx> {
4658
fn require_unsafe(&mut self, span: Span, description: &str) {
47-
match self.unsafe_context {
59+
if self.unsafe_context.push_unsafe_count > 0 { return; }
60+
match self.unsafe_context.root {
4861
SafeContext => {
4962
// Report an error.
5063
span_err!(self.tcx.sess, span, E0133,
@@ -75,9 +88,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
7588

7689
let old_unsafe_context = self.unsafe_context;
7790
if is_unsafe_fn {
78-
self.unsafe_context = UnsafeFn
91+
self.unsafe_context = UnsafeContext::new(UnsafeFn)
7992
} else if is_item_fn {
80-
self.unsafe_context = SafeContext
93+
self.unsafe_context = UnsafeContext::new(SafeContext)
8194
}
8295

8396
visit::walk_fn(self, fn_kind, fn_decl, block, span);
@@ -105,10 +118,18 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
105118
// external blocks (e.g. `unsafe { println("") }`,
106119
// expands to `unsafe { ... unsafe { ... } }` where
107120
// the inner one is compiler generated).
108-
if self.unsafe_context == SafeContext || source == ast::CompilerGenerated {
109-
self.unsafe_context = UnsafeBlock(block.id)
121+
if self.unsafe_context.root == SafeContext || source == ast::CompilerGenerated {
122+
self.unsafe_context.root = UnsafeBlock(block.id)
110123
}
111124
}
125+
ast::PushUnsafeBlock(..) => {
126+
self.unsafe_context.push_unsafe_count =
127+
self.unsafe_context.push_unsafe_count.checked_add(1).unwrap();
128+
}
129+
ast::PopUnsafeBlock(..) => {
130+
self.unsafe_context.push_unsafe_count =
131+
self.unsafe_context.push_unsafe_count.checked_sub(1).unwrap();
132+
}
112133
}
113134

114135
visit::walk_block(self, block);
@@ -162,7 +183,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
162183
pub fn check_crate(tcx: &ty::ctxt) {
163184
let mut visitor = EffectCheckVisitor {
164185
tcx: tcx,
165-
unsafe_context: SafeContext,
186+
unsafe_context: UnsafeContext::new(SafeContext),
166187
};
167188

168189
visit::walk_crate(&mut visitor, tcx.map.krate());

Diff for: src/librustc/middle/expr_use_visitor.rs

+5
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,11 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
555555
None => {}
556556
}
557557
self.consume_expr(&**base);
558+
if place.is_some() {
559+
self.tcx().sess.span_bug(
560+
expr.span,
561+
"box with explicit place remains after expansion");
562+
}
558563
}
559564

560565
ast::ExprMac(..) => {

0 commit comments

Comments
 (0)