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
28 changed files with 899 additions and 47 deletions
|
@ -55,13 +55,16 @@
|
||||||
|
|
||||||
use core::prelude::*;
|
use core::prelude::*;
|
||||||
|
|
||||||
|
use heap;
|
||||||
|
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::hash::{self, Hash};
|
use core::hash::{self, Hash};
|
||||||
use core::marker::Unsize;
|
use core::marker::{self, Unsize};
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::ops::{CoerceUnsized, Deref, DerefMut};
|
use core::ops::{CoerceUnsized, Deref, DerefMut};
|
||||||
|
use core::ops::{Placer, Boxed, Place, InPlace, BoxPlace};
|
||||||
use core::ptr::Unique;
|
use core::ptr::Unique;
|
||||||
use core::raw::{TraitObject};
|
use core::raw::{TraitObject};
|
||||||
|
|
||||||
|
@ -72,7 +75,7 @@ use core::raw::{TraitObject};
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #![feature(box_heap)]
|
/// # #![feature(box_heap)]
|
||||||
/// #![feature(box_syntax)]
|
/// #![feature(box_syntax, placement_in_syntax)]
|
||||||
/// use std::boxed::HEAP;
|
/// use std::boxed::HEAP;
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
|
@ -83,7 +86,12 @@ use core::raw::{TraitObject};
|
||||||
#[lang = "exchange_heap"]
|
#[lang = "exchange_heap"]
|
||||||
#[unstable(feature = "box_heap",
|
#[unstable(feature = "box_heap",
|
||||||
reason = "may be renamed; uncertain about custom allocator design")]
|
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.
|
/// A pointer type for heap allocation.
|
||||||
///
|
///
|
||||||
|
@ -91,7 +99,97 @@ pub const HEAP: () = ();
|
||||||
#[lang = "owned_box"]
|
#[lang = "owned_box"]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[fundamental]
|
#[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> {
|
impl<T> Box<T> {
|
||||||
/// Allocates memory on the heap and then moves `x` into it.
|
/// 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();
|
/// let y = x.clone();
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[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.
|
/// Copies `source`'s contents into `self` without creating a new allocation.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
|
|
@ -70,6 +70,8 @@
|
||||||
test(no_crate_inject))]
|
test(no_crate_inject))]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
// SNAP d4432b3
|
||||||
|
#![allow(unused_features)] // until feature(placement_in_syntax) is in snap
|
||||||
#![feature(allocator)]
|
#![feature(allocator)]
|
||||||
#![feature(box_syntax)]
|
#![feature(box_syntax)]
|
||||||
#![feature(coerce_unsized)]
|
#![feature(coerce_unsized)]
|
||||||
|
@ -82,6 +84,8 @@
|
||||||
#![feature(no_std)]
|
#![feature(no_std)]
|
||||||
#![feature(nonzero)]
|
#![feature(nonzero)]
|
||||||
#![feature(optin_builtin_traits)]
|
#![feature(optin_builtin_traits)]
|
||||||
|
#![feature(placement_in_syntax)]
|
||||||
|
#![feature(placement_new_protocol)]
|
||||||
#![feature(raw)]
|
#![feature(raw)]
|
||||||
#![feature(staged_api)]
|
#![feature(staged_api)]
|
||||||
#![feature(unboxed_closures)]
|
#![feature(unboxed_closures)]
|
||||||
|
|
|
@ -184,6 +184,14 @@ extern "rust-intrinsic" {
|
||||||
/// elements.
|
/// elements.
|
||||||
pub fn size_of<T>() -> usize;
|
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.
|
/// Moves a value to an uninitialized memory location.
|
||||||
///
|
///
|
||||||
/// Drop glue is not run on the destination.
|
/// 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
|
// *const T -> *const U
|
||||||
impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
|
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,
|
//! Enforces the Rust effect system. Currently there is just one effect,
|
||||||
//! `unsafe`.
|
//! `unsafe`.
|
||||||
use self::UnsafeContext::*;
|
use self::RootUnsafeContext::*;
|
||||||
|
|
||||||
use middle::def;
|
use middle::def;
|
||||||
use middle::ty::{self, Ty};
|
use middle::ty::{self, Ty};
|
||||||
|
@ -21,8 +21,20 @@ use syntax::codemap::Span;
|
||||||
use syntax::visit;
|
use syntax::visit;
|
||||||
use syntax::visit::Visitor;
|
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)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
enum UnsafeContext {
|
enum RootUnsafeContext {
|
||||||
SafeContext,
|
SafeContext,
|
||||||
UnsafeFn,
|
UnsafeFn,
|
||||||
UnsafeBlock(ast::NodeId),
|
UnsafeBlock(ast::NodeId),
|
||||||
|
@ -44,7 +56,8 @@ struct EffectCheckVisitor<'a, 'tcx: 'a> {
|
||||||
|
|
||||||
impl<'a, 'tcx> EffectCheckVisitor<'a, 'tcx> {
|
impl<'a, 'tcx> EffectCheckVisitor<'a, 'tcx> {
|
||||||
fn require_unsafe(&mut self, span: Span, description: &str) {
|
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 => {
|
SafeContext => {
|
||||||
// Report an error.
|
// Report an error.
|
||||||
span_err!(self.tcx.sess, span, E0133,
|
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;
|
let old_unsafe_context = self.unsafe_context;
|
||||||
if is_unsafe_fn {
|
if is_unsafe_fn {
|
||||||
self.unsafe_context = UnsafeFn
|
self.unsafe_context = UnsafeContext::new(UnsafeFn)
|
||||||
} else if is_item_fn {
|
} 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);
|
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("") }`,
|
// external blocks (e.g. `unsafe { println("") }`,
|
||||||
// expands to `unsafe { ... unsafe { ... } }` where
|
// expands to `unsafe { ... unsafe { ... } }` where
|
||||||
// the inner one is compiler generated).
|
// the inner one is compiler generated).
|
||||||
if self.unsafe_context == SafeContext || source == ast::CompilerGenerated {
|
if self.unsafe_context.root == SafeContext || source == ast::CompilerGenerated {
|
||||||
self.unsafe_context = UnsafeBlock(block.id)
|
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);
|
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) {
|
pub fn check_crate(tcx: &ty::ctxt) {
|
||||||
let mut visitor = EffectCheckVisitor {
|
let mut visitor = EffectCheckVisitor {
|
||||||
tcx: tcx,
|
tcx: tcx,
|
||||||
unsafe_context: SafeContext,
|
unsafe_context: UnsafeContext::new(SafeContext),
|
||||||
};
|
};
|
||||||
|
|
||||||
visit::walk_crate(&mut visitor, tcx.map.krate());
|
visit::walk_crate(&mut visitor, tcx.map.krate());
|
||||||
|
|
|
@ -555,6 +555,11 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
self.consume_expr(&**base);
|
self.consume_expr(&**base);
|
||||||
|
if place.is_some() {
|
||||||
|
self.tcx().sess.span_bug(
|
||||||
|
expr.span,
|
||||||
|
"box with explicit place remains after expansion");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::ExprMac(..) => {
|
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,
|
fn maybe_do_stability_check(tcx: &ty::ctxt, id: ast::DefId, span: Span,
|
||||||
cb: &mut FnMut(ast::DefId, Span, &Option<&Stability>)) {
|
cb: &mut FnMut(ast::DefId, Span, &Option<&Stability>)) {
|
||||||
if !is_staged_api(tcx, id) { return }
|
if !is_staged_api(tcx, id) {
|
||||||
if is_internal(tcx, span) { return }
|
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);
|
let ref stability = lookup(tcx, id);
|
||||||
|
debug!("maybe_do_stability_check: \
|
||||||
|
inspecting id={:?} span={:?} of stability={:?}", id, span, stability);
|
||||||
cb(id, span, stability);
|
cb(id, span, stability);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -232,12 +232,13 @@ impl<'tcx> Expectation<'tcx> {
|
||||||
pub struct UnsafetyState {
|
pub struct UnsafetyState {
|
||||||
pub def: ast::NodeId,
|
pub def: ast::NodeId,
|
||||||
pub unsafety: ast::Unsafety,
|
pub unsafety: ast::Unsafety,
|
||||||
|
pub unsafe_push_count: u32,
|
||||||
from_fn: bool
|
from_fn: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnsafetyState {
|
impl UnsafetyState {
|
||||||
pub fn function(unsafety: ast::Unsafety, def: ast::NodeId) -> 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 {
|
pub fn recurse(&mut self, blk: &ast::Block) -> UnsafetyState {
|
||||||
|
@ -249,12 +250,19 @@ impl UnsafetyState {
|
||||||
ast::Unsafety::Unsafe if self.from_fn => *self,
|
ast::Unsafety::Unsafe if self.from_fn => *self,
|
||||||
|
|
||||||
unsafety => {
|
unsafety => {
|
||||||
let (unsafety, def) = match blk.rules {
|
let (unsafety, def, count) = match blk.rules {
|
||||||
ast::UnsafeBlock(..) => (ast::Unsafety::Unsafe, blk.id),
|
ast::PushUnsafeBlock(..) =>
|
||||||
ast::DefaultBlock => (unsafety, self.def),
|
(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,
|
UnsafetyState{ def: def,
|
||||||
unsafety: unsafety,
|
unsafety: unsafety,
|
||||||
|
unsafe_push_count: count,
|
||||||
from_fn: false }
|
from_fn: false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4884,9 +4892,7 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
|
||||||
"move_val_init" => {
|
"move_val_init" => {
|
||||||
(1,
|
(1,
|
||||||
vec!(
|
vec!(
|
||||||
tcx.mk_mut_ref(tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1),
|
tcx.mk_mut_ptr(param(ccx, 0)),
|
||||||
ty::BrAnon(0))),
|
|
||||||
param(ccx, 0)),
|
|
||||||
param(ccx, 0)
|
param(ccx, 0)
|
||||||
),
|
),
|
||||||
tcx.mk_nil())
|
tcx.mk_nil())
|
||||||
|
|
|
@ -203,6 +203,7 @@
|
||||||
test(no_crate_inject, attr(deny(warnings))),
|
test(no_crate_inject, attr(deny(warnings))),
|
||||||
test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))))]
|
test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))))]
|
||||||
|
|
||||||
|
#![cfg_attr(stage0, allow(unused_features))]
|
||||||
#![feature(alloc)]
|
#![feature(alloc)]
|
||||||
#![feature(allow_internal_unstable)]
|
#![feature(allow_internal_unstable)]
|
||||||
#![feature(associated_consts)]
|
#![feature(associated_consts)]
|
||||||
|
@ -234,6 +235,7 @@
|
||||||
#![feature(no_std)]
|
#![feature(no_std)]
|
||||||
#![feature(oom)]
|
#![feature(oom)]
|
||||||
#![feature(optin_builtin_traits)]
|
#![feature(optin_builtin_traits)]
|
||||||
|
#![feature(placement_in_syntax)]
|
||||||
#![feature(rand)]
|
#![feature(rand)]
|
||||||
#![feature(raw)]
|
#![feature(raw)]
|
||||||
#![feature(reflect_marker)]
|
#![feature(reflect_marker)]
|
||||||
|
|
|
@ -810,6 +810,8 @@ pub type SpannedIdent = Spanned<Ident>;
|
||||||
pub enum BlockCheckMode {
|
pub enum BlockCheckMode {
|
||||||
DefaultBlock,
|
DefaultBlock,
|
||||||
UnsafeBlock(UnsafeSource),
|
UnsafeBlock(UnsafeSource),
|
||||||
|
PushUnsafeBlock(UnsafeSource),
|
||||||
|
PopUnsafeBlock(UnsafeSource),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
|
#[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
|
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? {}",
|
debug!("span_allows_unstable: from this expansion? {}, allows unstable? {}",
|
||||||
span_comes_from_this_expansion,
|
span_comes_from_this_expansion,
|
||||||
info.callee.allow_internal_unstable);
|
info.callee.allow_internal_unstable);
|
||||||
|
|
|
@ -591,6 +591,12 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
|
||||||
syntax_expanders.insert(intern("cfg"),
|
syntax_expanders.insert(intern("cfg"),
|
||||||
builtin_normal_expander(
|
builtin_normal_expander(
|
||||||
ext::cfg::expand_cfg));
|
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"),
|
syntax_expanders.insert(intern("trace_macros"),
|
||||||
builtin_normal_expander(
|
builtin_normal_expander(
|
||||||
ext::trace_macros::expand_trace_macros));
|
ext::trace_macros::expand_trace_macros));
|
||||||
|
|
|
@ -33,6 +33,16 @@ use visit;
|
||||||
use visit::Visitor;
|
use visit::Visitor;
|
||||||
use std_inject;
|
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> {
|
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) {
|
fn push_compiler_expansion(fld: &mut MacroExpander, span: Span, expansion_desc: &str) {
|
||||||
fld.cx.bt_push(ExpnInfo {
|
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 {
|
callee: NameAndSpan {
|
||||||
name: expansion_desc.to_string(),
|
name: expansion_desc.to_string(),
|
||||||
format: CompilerExpansion,
|
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,
|
allow_internal_unstable: true,
|
||||||
|
|
||||||
span: None,
|
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
|
// expr_mac should really be expr_ext or something; it's the
|
||||||
// entry-point for all syntax extensions.
|
// entry-point for all syntax extensions.
|
||||||
ast::ExprMac(mac) => {
|
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) => {
|
ast::ExprWhile(cond, body, opt_ident) => {
|
||||||
let cond = fld.fold_expr(cond);
|
let cond = fld.fold_expr(cond);
|
||||||
let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
|
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
|
span: span
|
||||||
}, fld))
|
}, 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
|
/// 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_trace_macros = allow_trace_macros,
|
||||||
fn enable_allow_internal_unstable = allow_internal_unstable,
|
fn enable_allow_internal_unstable = allow_internal_unstable,
|
||||||
fn enable_custom_derive = allow_custom_derive,
|
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),
|
("visible_private_types", "1.0.0", Active),
|
||||||
("slicing_syntax", "1.0.0", Accepted),
|
("slicing_syntax", "1.0.0", Accepted),
|
||||||
("box_syntax", "1.0.0", Active),
|
("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),
|
("on_unimplemented", "1.0.0", Active),
|
||||||
("simd_ffi", "1.0.0", Active),
|
("simd_ffi", "1.0.0", Active),
|
||||||
("allocator", "1.0.0", Active),
|
("allocator", "1.0.0", Active),
|
||||||
|
@ -325,6 +327,9 @@ pub struct Features {
|
||||||
pub allow_trace_macros: bool,
|
pub allow_trace_macros: bool,
|
||||||
pub allow_internal_unstable: bool,
|
pub allow_internal_unstable: bool,
|
||||||
pub allow_custom_derive: 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 simd_ffi: bool,
|
||||||
pub unmarked_api: bool,
|
pub unmarked_api: bool,
|
||||||
pub negate_unsigned: bool,
|
pub negate_unsigned: bool,
|
||||||
|
@ -348,6 +353,9 @@ impl Features {
|
||||||
allow_trace_macros: false,
|
allow_trace_macros: false,
|
||||||
allow_internal_unstable: false,
|
allow_internal_unstable: false,
|
||||||
allow_custom_derive: false,
|
allow_custom_derive: false,
|
||||||
|
allow_placement_in: false,
|
||||||
|
allow_box: false,
|
||||||
|
allow_pushpop_unsafe: false,
|
||||||
simd_ffi: false,
|
simd_ffi: false,
|
||||||
unmarked_api: false,
|
unmarked_api: false,
|
||||||
negate_unsigned: 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> {
|
struct Context<'a> {
|
||||||
features: Vec<&'static str>,
|
features: Vec<&'static str>,
|
||||||
span_handler: &'a SpanHandler,
|
span_handler: &'a SpanHandler,
|
||||||
|
@ -366,6 +404,11 @@ struct Context<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> 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) {
|
fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
|
||||||
let has_feature = self.has_feature(feature);
|
let has_feature = self.has_feature(feature);
|
||||||
debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", feature, span, has_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) {
|
fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
|
||||||
self.context.check_attribute(attr, true);
|
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> {
|
struct PostExpansionVisitor<'a> {
|
||||||
|
@ -754,7 +817,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
|
||||||
match KNOWN_FEATURES.iter()
|
match KNOWN_FEATURES.iter()
|
||||||
.find(|& &(n, _, _)| name == n) {
|
.find(|& &(n, _, _)| name == n) {
|
||||||
Some(&(name, _, Active)) => {
|
Some(&(name, _, Active)) => {
|
||||||
cx.features.push(name);
|
cx.enable_feature(name);
|
||||||
}
|
}
|
||||||
Some(&(_, _, Removed)) => {
|
Some(&(_, _, Removed)) => {
|
||||||
span_handler.span_err(mi.span, "feature has been 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_trace_macros: cx.has_feature("trace_macros"),
|
||||||
allow_internal_unstable: cx.has_feature("allow_internal_unstable"),
|
allow_internal_unstable: cx.has_feature("allow_internal_unstable"),
|
||||||
allow_custom_derive: cx.has_feature("custom_derive"),
|
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"),
|
simd_ffi: cx.has_feature("simd_ffi"),
|
||||||
unmarked_api: cx.has_feature("unmarked_api"),
|
unmarked_api: cx.has_feature("unmarked_api"),
|
||||||
negate_unsigned: cx.has_feature("negate_unsigned"),
|
negate_unsigned: cx.has_feature("negate_unsigned"),
|
||||||
|
|
|
@ -120,6 +120,7 @@ pub mod ext {
|
||||||
pub mod log_syntax;
|
pub mod log_syntax;
|
||||||
pub mod mtwt;
|
pub mod mtwt;
|
||||||
pub mod quote;
|
pub mod quote;
|
||||||
|
pub mod pushpop_safe;
|
||||||
pub mod source_util;
|
pub mod source_util;
|
||||||
pub mod trace_macros;
|
pub mod trace_macros;
|
||||||
|
|
||||||
|
|
|
@ -2612,18 +2612,43 @@ impl<'a> Parser<'a> {
|
||||||
ex = ExprAddrOf(m, e);
|
ex = ExprAddrOf(m, e);
|
||||||
}
|
}
|
||||||
token::Ident(_, _) => {
|
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();
|
return self.parse_dot_or_call_expr();
|
||||||
}
|
}
|
||||||
|
|
||||||
let lo = self.span.lo;
|
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());
|
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)) ){
|
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)) ){
|
if !try!(self.eat(&token::CloseDelim(token::Paren)) ){
|
||||||
let place = try!(self.parse_expr_nopanic());
|
let place = try!(self.parse_expr_nopanic());
|
||||||
try!(self.expect(&token::CloseDelim(token::Paren)));
|
try!(self.expect(&token::CloseDelim(token::Paren)));
|
||||||
|
@ -2634,10 +2659,15 @@ impl<'a> Parser<'a> {
|
||||||
self.span_err(span,
|
self.span_err(span,
|
||||||
&format!("expected expression, found `{}`",
|
&format!("expected expression, found `{}`",
|
||||||
this_token_to_string));
|
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,
|
self.span_suggestion(box_span,
|
||||||
"try using `box()` instead:",
|
"try using `box ()` instead:",
|
||||||
"box()".to_string());
|
format!("box ()"));
|
||||||
self.abort_if_errors();
|
self.abort_if_errors();
|
||||||
}
|
}
|
||||||
let subexpression = try!(self.parse_prefix_expr());
|
let subexpression = try!(self.parse_prefix_expr());
|
||||||
|
@ -2650,6 +2680,7 @@ impl<'a> Parser<'a> {
|
||||||
// Otherwise, we use the unique pointer default.
|
// Otherwise, we use the unique pointer default.
|
||||||
let subexpression = try!(self.parse_prefix_expr());
|
let subexpression = try!(self.parse_prefix_expr());
|
||||||
hi = subexpression.span.hi;
|
hi = subexpression.span.hi;
|
||||||
|
|
||||||
// FIXME (pnkfelix): After working out kinks with box
|
// FIXME (pnkfelix): After working out kinks with box
|
||||||
// desugaring, should be `ExprBox(None, subexpression)`
|
// desugaring, should be `ExprBox(None, subexpression)`
|
||||||
// instead.
|
// instead.
|
||||||
|
|
|
@ -1434,8 +1434,8 @@ impl<'a> State<'a> {
|
||||||
attrs: &[ast::Attribute],
|
attrs: &[ast::Attribute],
|
||||||
close_box: bool) -> io::Result<()> {
|
close_box: bool) -> io::Result<()> {
|
||||||
match blk.rules {
|
match blk.rules {
|
||||||
ast::UnsafeBlock(..) => try!(self.word_space("unsafe")),
|
ast::UnsafeBlock(..) | ast::PushUnsafeBlock(..) => try!(self.word_space("unsafe")),
|
||||||
ast::DefaultBlock => ()
|
ast::DefaultBlock | ast::PopUnsafeBlock(..) => ()
|
||||||
}
|
}
|
||||||
try!(self.maybe_print_comment(blk.span.lo));
|
try!(self.maybe_print_comment(blk.span.lo));
|
||||||
try!(self.ann.pre(self, NodeBlock(blk)));
|
try!(self.ann.pre(self, NodeBlock(blk)));
|
||||||
|
|
|
@ -8,15 +8,18 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
fn main() {
|
// Check that `box EXPR` is feature-gated.
|
||||||
use std::boxed::HEAP;
|
//
|
||||||
|
// 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
|
let x = box 'c'; //~ ERROR box expression syntax is experimental
|
||||||
println!("x: {}", x);
|
println!("x: {}", x);
|
||||||
|
|
||||||
let x = box () 'c'; //~ ERROR box expression syntax is experimental
|
let x = box () 'c'; //~ ERROR box expression syntax is experimental
|
||||||
println!("x: {}", x);
|
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.
|
// except according to those terms.
|
||||||
|
|
||||||
#![feature(box_syntax)]
|
#![feature(box_syntax)]
|
||||||
|
#![feature(placement_in_syntax)]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
box ( () ) 0;
|
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() {
|
fn main() {
|
||||||
box (1 + 1)
|
box (1 + 1)
|
||||||
//~^ HELP try using `box()` instead:
|
//~^ HELP try using `box ()` instead:
|
||||||
//~| SUGGESTION box() (1 + 1)
|
//~| SUGGESTION box () (1 + 1)
|
||||||
; //~ ERROR expected expression, found `;`
|
; //~ ERROR expected expression, found `;`
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ use std::mem::{self, transmute};
|
||||||
mod rusti {
|
mod rusti {
|
||||||
extern "rust-intrinsic" {
|
extern "rust-intrinsic" {
|
||||||
pub fn init<T>() -> T;
|
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.
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
#![allow(warnings)]
|
#![allow(dead_code, unused_variables)]
|
||||||
#![feature(box_syntax, box_heap)]
|
#![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.
|
// Tests that the new `box` syntax works with unique pointers.
|
||||||
|
|
||||||
|
@ -30,4 +35,9 @@ pub fn main() {
|
||||||
let y: Box<isize> = box 2;
|
let y: Box<isize> = box 2;
|
||||||
let b: Box<isize> = box()(1 + 2);
|
let b: Box<isize> = box()(1 + 2);
|
||||||
let c = box()(3 + 4);
|
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…
Add table
Add a link
Reference in a new issue