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:
commit
9413a926fc
@ -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
|
||||
|
@ -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)]
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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(..) => {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
94
src/libsyntax/ext/pushpop_safe.rs
Normal file
94
src/libsyntax/ext/pushpop_safe.rs
Normal 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
|
||||
})))
|
||||
}
|
@ -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"),
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)));
|
||||
|
@ -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);
|
||||
}
|
||||
|
27
src/test/compile-fail/feature-gate-placement-expr.rs
Normal file
27
src/test/compile-fail/feature-gate-placement-expr.rs
Normal 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);
|
||||
}
|
14
src/test/compile-fail/feature-gate-pushpop-unsafe.rs
Normal file
14
src/test/compile-fail/feature-gate-pushpop-unsafe.rs
Normal 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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
74
src/test/compile-fail/pushpop-unsafe-rejects.rs
Normal file
74
src/test/compile-fail/pushpop-unsafe-rejects.rs
Normal 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
|
||||
// })
|
||||
|
||||
}
|
@ -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 `;`
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
37
src/test/run-pass/placement-in-syntax.rs
Normal file
37
src/test/run-pass/placement-in-syntax.rs
Normal 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,
|
||||
}
|
||||
};
|
||||
}
|
56
src/test/run-pass/pushpop-unsafe-okay.rs
Normal file
56
src/test/run-pass/pushpop-unsafe-okay.rs
Normal 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));
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user