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.
This commit is contained in:
bors 2015-07-24 10:12:20 +00:00
commit 9413a926fc
28 changed files with 899 additions and 47 deletions

View file

@ -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};
@ -72,7 +75,7 @@ use core::raw::{TraitObject};
///
/// ```
/// # #![feature(box_heap)]
/// #![feature(box_syntax)]
/// #![feature(box_syntax, placement_in_syntax)]
/// use std::boxed::HEAP;
///
/// fn main() {
@ -83,7 +86,12 @@ use core::raw::{TraitObject};
#[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 @@ pub const HEAP: () = ();
#[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

View file

@ -70,6 +70,8 @@
test(no_crate_inject))]
#![no_std]
// SNAP d4432b3
#![allow(unused_features)] // until feature(placement_in_syntax) is in snap
#![feature(allocator)]
#![feature(box_syntax)]
#![feature(coerce_unsized)]
@ -82,6 +84,8 @@
#![feature(no_std)]
#![feature(nonzero)]
#![feature(optin_builtin_traits)]
#![feature(placement_in_syntax)]
#![feature(placement_new_protocol)]
#![feature(raw)]
#![feature(staged_api)]
#![feature(unboxed_closures)]

View file

@ -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 d4432b3
#[cfg(stage0)]
/// Moves a value to an uninitialized memory location.
///
/// Drop glue is not run on the destination.

View file

@ -1266,3 +1266,120 @@ 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).
#[unstable(feature = "placement_new_protocol")]
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.
#[unstable(feature = "placement_new_protocol")]
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`.
#[unstable(feature = "placement_new_protocol")]
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>`.
#[unstable(feature = "placement_new_protocol")]
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: 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`.
#[unstable(feature = "placement_new_protocol")]
pub trait BoxPlace<Data: ?Sized> : Place<Data> {
/// Creates a globally fresh place.
fn make_place() -> Self;
}

View file

@ -10,7 +10,7 @@
//! Enforces the Rust effect system. Currently there is just one effect,
//! `unsafe`.
use self::UnsafeContext::*;
use self::RootUnsafeContext::*;
use middle::def;
use middle::ty::{self, Ty};
@ -21,8 +21,20 @@ use syntax::codemap::Span;
use syntax::visit;
use syntax::visit::Visitor;
#[derive(Copy, Clone)]
struct UnsafeContext {
push_unsafe_count: usize,
root: RootUnsafeContext,
}
impl UnsafeContext {
fn new(root: RootUnsafeContext) -> UnsafeContext {
UnsafeContext { root: root, push_unsafe_count: 0 }
}
}
#[derive(Copy, Clone, PartialEq)]
enum UnsafeContext {
enum RootUnsafeContext {
SafeContext,
UnsafeFn,
UnsafeBlock(ast::NodeId),
@ -44,7 +56,8 @@ struct EffectCheckVisitor<'a, 'tcx: 'a> {
impl<'a, 'tcx> EffectCheckVisitor<'a, 'tcx> {
fn require_unsafe(&mut self, span: Span, description: &str) {
match self.unsafe_context {
if self.unsafe_context.push_unsafe_count > 0 { return; }
match self.unsafe_context.root {
SafeContext => {
// Report an error.
span_err!(self.tcx.sess, span, E0133,
@ -75,9 +88,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
let old_unsafe_context = self.unsafe_context;
if is_unsafe_fn {
self.unsafe_context = UnsafeFn
self.unsafe_context = UnsafeContext::new(UnsafeFn)
} else if is_item_fn {
self.unsafe_context = SafeContext
self.unsafe_context = UnsafeContext::new(SafeContext)
}
visit::walk_fn(self, fn_kind, fn_decl, block, span);
@ -105,10 +118,18 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
// external blocks (e.g. `unsafe { println("") }`,
// expands to `unsafe { ... unsafe { ... } }` where
// the inner one is compiler generated).
if self.unsafe_context == SafeContext || source == ast::CompilerGenerated {
self.unsafe_context = UnsafeBlock(block.id)
if self.unsafe_context.root == SafeContext || source == ast::CompilerGenerated {
self.unsafe_context.root = UnsafeBlock(block.id)
}
}
ast::PushUnsafeBlock(..) => {
self.unsafe_context.push_unsafe_count =
self.unsafe_context.push_unsafe_count.checked_add(1).unwrap();
}
ast::PopUnsafeBlock(..) => {
self.unsafe_context.push_unsafe_count =
self.unsafe_context.push_unsafe_count.checked_sub(1).unwrap();
}
}
visit::walk_block(self, block);
@ -162,7 +183,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
pub fn check_crate(tcx: &ty::ctxt) {
let mut visitor = EffectCheckVisitor {
tcx: tcx,
unsafe_context: SafeContext,
unsafe_context: UnsafeContext::new(SafeContext),
};
visit::walk_crate(&mut visitor, tcx.map.krate());

View file

@ -555,6 +555,11 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
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(..) => {

View file

@ -543,9 +543,19 @@ pub fn check_pat(tcx: &ty::ctxt, pat: &ast::Pat,
fn maybe_do_stability_check(tcx: &ty::ctxt, id: ast::DefId, span: Span,
cb: &mut FnMut(ast::DefId, Span, &Option<&Stability>)) {
if !is_staged_api(tcx, id) { return }
if is_internal(tcx, span) { return }
if !is_staged_api(tcx, id) {
debug!("maybe_do_stability_check: \
skipping id={:?} since it is not staged_api", id);
return;
}
if is_internal(tcx, span) {
debug!("maybe_do_stability_check: \
skipping span={:?} since it is internal", span);
return;
}
let ref stability = lookup(tcx, id);
debug!("maybe_do_stability_check: \
inspecting id={:?} span={:?} of stability={:?}", id, span, stability);
cb(id, span, stability);
}

View file

@ -232,12 +232,13 @@ impl<'tcx> Expectation<'tcx> {
pub struct UnsafetyState {
pub def: ast::NodeId,
pub unsafety: ast::Unsafety,
pub unsafe_push_count: u32,
from_fn: bool
}
impl UnsafetyState {
pub fn function(unsafety: ast::Unsafety, def: ast::NodeId) -> UnsafetyState {
UnsafetyState { def: def, unsafety: unsafety, from_fn: true }
UnsafetyState { def: def, unsafety: unsafety, unsafe_push_count: 0, from_fn: true }
}
pub fn recurse(&mut self, blk: &ast::Block) -> UnsafetyState {
@ -249,13 +250,20 @@ impl UnsafetyState {
ast::Unsafety::Unsafe if self.from_fn => *self,
unsafety => {
let (unsafety, def) = match blk.rules {
ast::UnsafeBlock(..) => (ast::Unsafety::Unsafe, blk.id),
ast::DefaultBlock => (unsafety, self.def),
let (unsafety, def, count) = match blk.rules {
ast::PushUnsafeBlock(..) =>
(unsafety, blk.id, self.unsafe_push_count.checked_add(1).unwrap()),
ast::PopUnsafeBlock(..) =>
(unsafety, blk.id, self.unsafe_push_count.checked_sub(1).unwrap()),
ast::UnsafeBlock(..) =>
(ast::Unsafety::Unsafe, blk.id, self.unsafe_push_count),
ast::DefaultBlock =>
(unsafety, self.def, self.unsafe_push_count),
};
UnsafetyState{ def: def,
unsafety: unsafety,
from_fn: false }
unsafety: unsafety,
unsafe_push_count: count,
from_fn: false }
}
}
}
@ -4884,9 +4892,7 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
"move_val_init" => {
(1,
vec!(
tcx.mk_mut_ref(tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1),
ty::BrAnon(0))),
param(ccx, 0)),
tcx.mk_mut_ptr(param(ccx, 0)),
param(ccx, 0)
),
tcx.mk_nil())

View file

@ -203,6 +203,7 @@
test(no_crate_inject, attr(deny(warnings))),
test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))))]
#![cfg_attr(stage0, allow(unused_features))]
#![feature(alloc)]
#![feature(allow_internal_unstable)]
#![feature(associated_consts)]
@ -234,6 +235,7 @@
#![feature(no_std)]
#![feature(oom)]
#![feature(optin_builtin_traits)]
#![feature(placement_in_syntax)]
#![feature(rand)]
#![feature(raw)]
#![feature(reflect_marker)]

View file

@ -810,6 +810,8 @@ pub type SpannedIdent = Spanned<Ident>;
pub enum BlockCheckMode {
DefaultBlock,
UnsafeBlock(UnsafeSource),
PushUnsafeBlock(UnsafeSource),
PopUnsafeBlock(UnsafeSource),
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]

View file

@ -980,6 +980,10 @@ impl CodeMap {
mac_span.lo <= span.lo && span.hi <= mac_span.hi
});
debug!("span_allows_unstable: span: {:?} call_site: {:?} callee: {:?}",
(span.lo, span.hi),
(info.call_site.lo, info.call_site.hi),
info.callee.span.map(|x| (x.lo, x.hi)));
debug!("span_allows_unstable: from this expansion? {}, allows unstable? {}",
span_comes_from_this_expansion,
info.callee.allow_internal_unstable);

View file

@ -591,6 +591,12 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
syntax_expanders.insert(intern("cfg"),
builtin_normal_expander(
ext::cfg::expand_cfg));
syntax_expanders.insert(intern("push_unsafe"),
builtin_normal_expander(
ext::pushpop_safe::expand_push_unsafe));
syntax_expanders.insert(intern("pop_unsafe"),
builtin_normal_expander(
ext::pushpop_safe::expand_pop_unsafe));
syntax_expanders.insert(intern("trace_macros"),
builtin_normal_expander(
ext::trace_macros::expand_trace_macros));

View file

@ -33,6 +33,16 @@ use visit;
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 {
@ -40,13 +50,26 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
callee: NameAndSpan {
name: expansion_desc.to_string(),
format: CompilerExpansion,
// This does *not* mean code generated after
// `push_compiler_expansion` is automatically exempt
// from stability lints; must also tag such code with
// an appropriate span from `fld.cx.backtrace()`.
allow_internal_unstable: true,
span: None,
},
});
}
e.and_then(|ast::Expr {id, node, span}| match node {
// Sets the expn_id so that we can use unstable methods.
fn allow_unstable(fld: &mut MacroExpander, span: Span) -> Span {
Span { expn_id: fld.cx.backtrace(), ..span }
}
let expr_span = e.span;
return 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 +94,118 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
})
}
// 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 = Place::pointer(&mut place);
// push_unsafe!({
// std::intrinsics::move_val_init(raw_place, pop_unsafe!( EXPR ));
// InPlace::finalize(place)
// })
// Ensure feature-gate is enabled
feature_gate::check_for_placement_in(
fld.cx.ecfg.features,
&fld.cx.parse_sess.span_diagnostic,
expr_span);
push_compiler_expansion(fld, expr_span, "placement-in expansion");
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 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 p_ptr = fld.cx.expr_ident(span, p_ptr_ident);
let make_place = ["ops", "Placer", "make_place"];
let place_pointer = ["ops", "Place", "pointer"];
let move_val_init = ["intrinsics", "move_val_init"];
let inplace_finalize = ["ops", "InPlace", "finalize"];
let make_call = |fld: &mut MacroExpander, p, args| {
// We feed in the `expr_span` because codemap's span_allows_unstable
// allows the call_site span to inherit the `allow_internal_unstable`
// setting.
let span_unstable = allow_unstable(fld, expr_span);
let path = mk_core_path(fld, span_unstable, p);
let path = fld.cx.expr_path(path);
let expr_span_unstable = allow_unstable(fld, span);
fld.cx.expr_call(expr_span_unstable, 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)
};
// pop_unsafe!(EXPR));
let pop_unsafe_expr = pop_unsafe_expr(fld.cx, value_expr, value_span);
// push_unsafe!({
// ptr::write(p_ptr, pop_unsafe!(<value_expr>));
// InPlace::finalize(place)
// })
let expr = {
let call_move_val_init = StmtSemi(make_call(
fld, &move_val_init, vec![p_ptr, pop_unsafe_expr]), ast::DUMMY_NODE_ID);
let call_move_val_init = codemap::respan(value_span, call_move_val_init);
let call = make_call(fld, &inplace_finalize, vec![agent]);
Some(push_unsafe_expr(fld.cx, vec![P(call_move_val_init)], call, span))
};
let block = fld.cx.block_all(span, vec![s1, s2, s3], expr);
let result = fld.cx.expr_block(block);
fld.cx.bt_pop();
result
}
// 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);
@ -360,7 +495,26 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
span: span
}, fld))
}
})
});
fn push_unsafe_expr(cx: &mut ExtCtxt, stmts: Vec<P<ast::Stmt>>,
expr: P<ast::Expr>, span: Span)
-> P<ast::Expr> {
let rules = ast::PushUnsafeBlock(ast::CompilerGenerated);
cx.expr_block(P(ast::Block {
rules: rules, span: span, id: ast::DUMMY_NODE_ID,
stmts: stmts, expr: Some(expr),
}))
}
fn pop_unsafe_expr(cx: &mut ExtCtxt, expr: P<ast::Expr>, span: Span)
-> P<ast::Expr> {
let rules = ast::PopUnsafeBlock(ast::CompilerGenerated);
cx.expr_block(P(ast::Block {
rules: rules, span: span, id: ast::DUMMY_NODE_ID,
stmts: vec![], expr: Some(expr),
}))
}
}
/// Expand a (not-ident-style) macro invocation. Returns the result
@ -1504,6 +1658,7 @@ impl<'feat> ExpansionConfig<'feat> {
fn enable_trace_macros = allow_trace_macros,
fn enable_allow_internal_unstable = allow_internal_unstable,
fn enable_custom_derive = allow_custom_derive,
fn enable_pushpop_unsafe = allow_pushpop_unsafe,
}
}

View file

@ -0,0 +1,94 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*
* The compiler code necessary to support the `push_unsafe!` and
* `pop_unsafe!` macros.
*
* This is a hack to allow a kind of "safety hygiene", where a macro
* can generate code with an interior expression that inherits the
* safety of some outer context.
*
* For example, in:
*
* ```rust
* fn foo() { push_unsafe!( { EXPR_1; pop_unsafe!( EXPR_2 ) } ) }
* ```
*
* the `EXPR_1` is considered to be in an `unsafe` context,
* but `EXPR_2` is considered to be in a "safe" (i.e. checked) context.
*
* For comparison, in:
*
* ```rust
* fn foo() { unsafe { push_unsafe!( { EXPR_1; pop_unsafe!( EXPR_2 ) } ) } }
* ```
*
* both `EXPR_1` and `EXPR_2` are considered to be in `unsafe`
* contexts.
*
*/
use ast;
use codemap::Span;
use ext::base::*;
use ext::base;
use ext::build::AstBuilder;
use feature_gate;
use ptr::P;
enum PushPop { Push, Pop }
pub fn expand_push_unsafe<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> Box<base::MacResult+'cx> {
expand_pushpop_unsafe(cx, sp, tts, PushPop::Push)
}
pub fn expand_pop_unsafe<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> Box<base::MacResult+'cx> {
expand_pushpop_unsafe(cx, sp, tts, PushPop::Pop)
}
fn expand_pushpop_unsafe<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree],
pp: PushPop) -> Box<base::MacResult+'cx> {
feature_gate::check_for_pushpop_syntax(
cx.ecfg.features, &cx.parse_sess.span_diagnostic, sp);
let mut exprs = match get_exprs_from_tts(cx, sp, tts) {
Some(exprs) => exprs.into_iter(),
None => return DummyResult::expr(sp),
};
let expr = match (exprs.next(), exprs.next()) {
(Some(expr), None) => expr,
_ => {
let msg = match pp {
PushPop::Push => "push_unsafe! takes 1 arguments",
PushPop::Pop => "pop_unsafe! takes 1 arguments",
};
cx.span_err(sp, msg);
return DummyResult::expr(sp);
}
};
let source = ast::UnsafeSource::CompilerGenerated;
let check_mode = match pp {
PushPop::Push => ast::BlockCheckMode::PushUnsafeBlock(source),
PushPop::Pop => ast::BlockCheckMode::PopUnsafeBlock(source),
};
MacEager::expr(cx.expr_block(P(ast::Block {
stmts: vec![],
expr: Some(expr),
id: ast::DUMMY_NODE_ID,
rules: check_mode,
span: sp
})))
}

View file

@ -80,6 +80,8 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
("visible_private_types", "1.0.0", Active),
("slicing_syntax", "1.0.0", Accepted),
("box_syntax", "1.0.0", Active),
("placement_in_syntax", "1.0.0", Active),
("pushpop_unsafe", "1.2.0", Active),
("on_unimplemented", "1.0.0", Active),
("simd_ffi", "1.0.0", Active),
("allocator", "1.0.0", Active),
@ -325,6 +327,9 @@ pub struct Features {
pub allow_trace_macros: bool,
pub allow_internal_unstable: bool,
pub allow_custom_derive: bool,
pub allow_placement_in: bool,
pub allow_box: bool,
pub allow_pushpop_unsafe: bool,
pub simd_ffi: bool,
pub unmarked_api: bool,
pub negate_unsigned: bool,
@ -348,6 +353,9 @@ impl Features {
allow_trace_macros: false,
allow_internal_unstable: false,
allow_custom_derive: false,
allow_placement_in: false,
allow_box: false,
allow_pushpop_unsafe: false,
simd_ffi: false,
unmarked_api: false,
negate_unsigned: false,
@ -358,6 +366,36 @@ impl Features {
}
}
const EXPLAIN_BOX_SYNTAX: &'static str =
"box expression syntax is experimental; you can call `Box::new` instead.";
const EXPLAIN_PLACEMENT_IN: &'static str =
"placement-in expression syntax is experimental and subject to change.";
const EXPLAIN_PUSHPOP_UNSAFE: &'static str =
"push/pop_unsafe macros are experimental and subject to change.";
pub fn check_for_box_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) {
if let Some(&Features { allow_box: true, .. }) = f {
return;
}
emit_feature_err(diag, "box_syntax", span, EXPLAIN_BOX_SYNTAX);
}
pub fn check_for_placement_in(f: Option<&Features>, diag: &SpanHandler, span: Span) {
if let Some(&Features { allow_placement_in: true, .. }) = f {
return;
}
emit_feature_err(diag, "placement_in_syntax", span, EXPLAIN_PLACEMENT_IN);
}
pub fn check_for_pushpop_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) {
if let Some(&Features { allow_pushpop_unsafe: true, .. }) = f {
return;
}
emit_feature_err(diag, "pushpop_unsafe", span, EXPLAIN_PUSHPOP_UNSAFE);
}
struct Context<'a> {
features: Vec<&'static str>,
span_handler: &'a SpanHandler,
@ -366,6 +404,11 @@ struct Context<'a> {
}
impl<'a> Context<'a> {
fn enable_feature(&mut self, feature: &'static str) {
debug!("enabling feature: {}", feature);
self.features.push(feature);
}
fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
let has_feature = self.has_feature(feature);
debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", feature, span, has_feature);
@ -488,6 +531,26 @@ impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
self.context.check_attribute(attr, true);
}
fn visit_expr(&mut self, e: &ast::Expr) {
// Issue 22181: overloaded-`box` and placement-`in` are
// implemented via a desugaring expansion, so their feature
// gates go into MacroVisitor since that works pre-expansion.
//
// Issue 22234: we also check during expansion as well.
// But we keep these checks as a pre-expansion check to catch
// uses in e.g. conditionalized code.
if let ast::ExprBox(None, _) = e.node {
self.context.gate_feature("box_syntax", e.span, EXPLAIN_BOX_SYNTAX);
}
if let ast::ExprBox(Some(_), _) = e.node {
self.context.gate_feature("placement_in_syntax", e.span, EXPLAIN_PLACEMENT_IN);
}
visit::walk_expr(self, e);
}
}
struct PostExpansionVisitor<'a> {
@ -754,7 +817,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
match KNOWN_FEATURES.iter()
.find(|& &(n, _, _)| name == n) {
Some(&(name, _, Active)) => {
cx.features.push(name);
cx.enable_feature(name);
}
Some(&(_, _, Removed)) => {
span_handler.span_err(mi.span, "feature has been removed");
@ -787,6 +850,9 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
allow_trace_macros: cx.has_feature("trace_macros"),
allow_internal_unstable: cx.has_feature("allow_internal_unstable"),
allow_custom_derive: cx.has_feature("custom_derive"),
allow_placement_in: cx.has_feature("placement_in_syntax"),
allow_box: cx.has_feature("box_syntax"),
allow_pushpop_unsafe: cx.has_feature("pushpop_unsafe"),
simd_ffi: cx.has_feature("simd_ffi"),
unmarked_api: cx.has_feature("unmarked_api"),
negate_unsigned: cx.has_feature("negate_unsigned"),

View file

@ -120,6 +120,7 @@ pub mod ext {
pub mod log_syntax;
pub mod mtwt;
pub mod quote;
pub mod pushpop_safe;
pub mod source_util;
pub mod trace_macros;

View file

@ -2612,18 +2612,43 @@ impl<'a> Parser<'a> {
ex = ExprAddrOf(m, e);
}
token::Ident(_, _) => {
if !self.check_keyword(keywords::Box) {
if !self.check_keyword(keywords::Box) && !self.check_keyword(keywords::In) {
return self.parse_dot_or_call_expr();
}
let lo = self.span.lo;
let box_hi = self.span.hi;
let keyword_hi = self.span.hi;
let is_in = self.token.is_keyword(keywords::In);
try!(self.bump());
// Check for a place: `box(PLACE) EXPR`.
if is_in {
let place = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL));
let blk = try!(self.parse_block());
hi = blk.span.hi;
let blk_expr = self.mk_expr(blk.span.lo, blk.span.hi, ExprBlock(blk));
ex = ExprBox(Some(place), blk_expr);
return Ok(self.mk_expr(lo, hi, ex));
}
// FIXME (#22181) Remove `box (PLACE) EXPR` support
// entirely after next release (enabling `(box (EXPR))`),
// since it will be replaced by `in PLACE { EXPR }`, ...
//
// ... but for now: check for a place: `box(PLACE) EXPR`.
if try!(self.eat(&token::OpenDelim(token::Paren)) ){
// Support `box() EXPR` as the default.
// SNAP d4432b3
// Enable this warning after snapshot ...
//
// let box_span = mk_sp(lo, self.last_span.hi);
// self.span_warn(
// box_span,
// "deprecated syntax; use the `in` keyword now \
// (e.g. change `box (<expr>) <expr>` to \
// `in <expr> { <expr> }`)");
// Continue supporting `box () EXPR` (temporarily)
if !try!(self.eat(&token::CloseDelim(token::Paren)) ){
let place = try!(self.parse_expr_nopanic());
try!(self.expect(&token::CloseDelim(token::Paren)));
@ -2634,10 +2659,15 @@ impl<'a> Parser<'a> {
self.span_err(span,
&format!("expected expression, found `{}`",
this_token_to_string));
let box_span = mk_sp(lo, box_hi);
// Spanning just keyword avoids constructing
// printout of arg expression (which starts
// with parenthesis, as established above).
let box_span = mk_sp(lo, keyword_hi);
self.span_suggestion(box_span,
"try using `box()` instead:",
"box()".to_string());
"try using `box ()` instead:",
format!("box ()"));
self.abort_if_errors();
}
let subexpression = try!(self.parse_prefix_expr());
@ -2650,6 +2680,7 @@ impl<'a> Parser<'a> {
// Otherwise, we use the unique pointer default.
let subexpression = try!(self.parse_prefix_expr());
hi = subexpression.span.hi;
// FIXME (pnkfelix): After working out kinks with box
// desugaring, should be `ExprBox(None, subexpression)`
// instead.

View file

@ -1434,8 +1434,8 @@ impl<'a> State<'a> {
attrs: &[ast::Attribute],
close_box: bool) -> io::Result<()> {
match blk.rules {
ast::UnsafeBlock(..) => try!(self.word_space("unsafe")),
ast::DefaultBlock => ()
ast::UnsafeBlock(..) | ast::PushUnsafeBlock(..) => try!(self.word_space("unsafe")),
ast::DefaultBlock | ast::PopUnsafeBlock(..) => ()
}
try!(self.maybe_print_comment(blk.span.lo));
try!(self.ann.pre(self, NodeBlock(blk)));

View file

@ -8,15 +8,18 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn main() {
use std::boxed::HEAP;
// Check that `box EXPR` is feature-gated.
//
// See also feature-gate-placement-expr.rs
//
// (Note that the two tests are separated since the checks appear to
// be performed at distinct phases, with an abort_if_errors call
// separating them.)
fn main() {
let x = box 'c'; //~ ERROR box expression syntax is experimental
println!("x: {}", x);
let x = box () 'c'; //~ ERROR box expression syntax is experimental
println!("x: {}", x);
let x = box (HEAP) 'c'; //~ ERROR box expression syntax is experimental
println!("x: {}", x);
}

View file

@ -0,0 +1,27 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Check that `in PLACE { EXPR }` is feature-gated.
//
// See also feature-gate-box-expr.rs
//
// (Note that the two tests are separated since the checks appear to
// be performed at distinct phases, with an abort_if_errors call
// separating them.)
fn main() {
use std::boxed::HEAP;
let x = box (HEAP) 'c'; //~ ERROR placement-in expression syntax is experimental
println!("x: {}", x);
let x = in HEAP { 'c' }; //~ ERROR placement-in expression syntax is experimental
println!("x: {}", x);
}

View file

@ -0,0 +1,14 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn main() {
let c = push_unsafe!('c'); //~ ERROR push/pop_unsafe macros are experimental
let c = pop_unsafe!('c'); //~ ERROR push/pop_unsafe macros are experimental
}

View file

@ -9,8 +9,10 @@
// except according to those terms.
#![feature(box_syntax)]
#![feature(placement_in_syntax)]
fn main() {
box ( () ) 0;
//~^ ERROR: only the exchange heap is currently supported
//~^ ERROR: the trait `core::ops::Placer<_>` is not implemented
//~| ERROR: the trait `core::ops::Placer<_>` is not implemented
}

View file

@ -0,0 +1,74 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Basic sanity check for `push_unsafe!(EXPR)` and
// `pop_unsafe!(EXPR)`: we can call unsafe code when there are a
// positive number of pushes in the stack, or if we are within a
// normal `unsafe` block, but otherwise cannot.
#![feature(pushpop_unsafe)]
static mut X: i32 = 0;
unsafe fn f() { X += 1; return; }
fn g() { unsafe { X += 1_000; } return; }
fn main() {
push_unsafe!( {
f(); pop_unsafe!({
f() //~ ERROR: call to unsafe function
})
} );
push_unsafe!({
f();
pop_unsafe!({
g();
f(); //~ ERROR: call to unsafe function
})
} );
push_unsafe!({
g(); pop_unsafe!({
unsafe {
f();
}
f(); //~ ERROR: call to unsafe function
})
});
// Note: For implementation simplicity the compiler just
// ICE's if you underflow the push_unsafe stack.
//
// Thus all of the following cases cause an ICE.
//
// (The "ERROR" notes are from an earlier version
// that used saturated arithmetic rather than checked
// arithmetic.)
// pop_unsafe!{ g() };
//
// push_unsafe!({
// pop_unsafe!(pop_unsafe!{ g() })
// });
//
// push_unsafe!({
// g();
// pop_unsafe!(pop_unsafe!({
// f() // ERROR: call to unsafe function
// }))
// });
//
// pop_unsafe!({
// f(); // ERROR: call to unsafe function
// })
}

View file

@ -12,7 +12,7 @@
fn main() {
box (1 + 1)
//~^ HELP try using `box()` instead:
//~| SUGGESTION box() (1 + 1)
//~^ HELP try using `box ()` instead:
//~| SUGGESTION box () (1 + 1)
; //~ ERROR expected expression, found `;`
}

View file

@ -20,7 +20,7 @@ use std::mem::{self, transmute};
mod rusti {
extern "rust-intrinsic" {
pub fn init<T>() -> T;
pub fn move_val_init<T>(dst: &mut T, src: T);
pub fn move_val_init<T>(dst: *mut T, src: T);
}
}

View file

@ -13,8 +13,13 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
#![allow(warnings)]
#![allow(dead_code, unused_variables)]
#![feature(box_syntax, box_heap)]
#![feature(placement_in_syntax)]
// during check-pretty, the expanded code needs to opt into these
// features
#![feature(placement_new_protocol, core_intrinsics)]
// Tests that the new `box` syntax works with unique pointers.
@ -30,4 +35,9 @@ pub fn main() {
let y: Box<isize> = box 2;
let b: Box<isize> = box()(1 + 2);
let c = box()(3 + 4);
let s: Box<Structure> = box Structure {
x: 3,
y: 4,
};
}

View file

@ -0,0 +1,37 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(dead_code, unused_variables)]
#![feature(box_heap)]
#![feature(placement_in_syntax)]
// Tests that the new `in` syntax works with unique pointers.
//
// Compare with new-box-syntax.rs
use std::boxed::{Box, HEAP};
struct Structure {
x: isize,
y: isize,
}
pub fn main() {
let x: Box<isize> = in HEAP { 2 };
let b: Box<isize> = in HEAP { 1 + 2 };
let c = in HEAP { 3 + 4 };
let s: Box<Structure> = in HEAP {
Structure {
x: 3,
y: 4,
}
};
}

View file

@ -0,0 +1,56 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Basic sanity check for `push_unsafe!(EXPR)` and
// `pop_unsafe!(EXPR)`: we can call unsafe code when there are a
// positive number of pushes in the stack, or if we are within a
// normal `unsafe` block, but otherwise cannot.
// ignore-pretty because the `push_unsafe!` and `pop_unsafe!` macros
// are not integrated with the pretty-printer.
#![feature(pushpop_unsafe)]
static mut X: i32 = 0;
unsafe fn f() { X += 1; return; }
fn g() { unsafe { X += 1_000; } return; }
fn check_reset_x(x: i32) -> bool {
#![allow(unused_parens)] // dont you judge my style choices!
unsafe {
let ret = (x == X);
X = 0;
ret
}
}
fn main() {
// double-check test infrastructure
assert!(check_reset_x(0));
unsafe { f(); }
assert!(check_reset_x(1));
assert!(check_reset_x(0));
{ g(); }
assert!(check_reset_x(1000));
assert!(check_reset_x(0));
unsafe { f(); g(); g(); }
assert!(check_reset_x(2001));
push_unsafe!( { f(); pop_unsafe!( g() ) } );
assert!(check_reset_x(1_001));
push_unsafe!( { g(); pop_unsafe!( unsafe { f(); f(); } ) } );
assert!(check_reset_x(1_002));
unsafe { push_unsafe!( { f(); pop_unsafe!( { f(); f(); } ) } ); }
assert!(check_reset_x(3));
push_unsafe!( { f(); push_unsafe!( { pop_unsafe!( { f(); f(); f(); } ) } ); } );
assert!(check_reset_x(4));
}