From 1829fa5199bae5a192c771807c532badce14be37 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 5 Jun 2015 08:31:27 +0200 Subject: [PATCH 01/27] Hack for "unsafety hygiene" -- `push_unsafe!` and `pop_unsafe!`. Even after expansion, the generated expressions still track depth of such pushes (i.e. how often you have "pushed" without a corresponding "pop"), and we add a rule that in a context with a positive `push_unsafe!` depth, it is effectively an `unsafe` block context. (This way, we can inject code that uses `unsafe` features, but still contains within it a sub-expression that should inherit the outer safety checking setting, outside of the injected code.) This is a total hack; it not only needs a feature-gate, but probably should be feature-gated forever (if possible). ignore-pretty in test/run-pass/pushpop-unsafe-okay.rs --- src/librustc/middle/effect.rs | 37 ++++++-- src/librustc_typeck/check/mod.rs | 20 ++-- src/libsyntax/ast.rs | 2 + src/libsyntax/ext/base.rs | 6 ++ src/libsyntax/ext/expand.rs | 1 + src/libsyntax/ext/pushpop_safe.rs | 94 +++++++++++++++++++ src/libsyntax/feature_gate.rs | 11 +++ src/libsyntax/lib.rs | 1 + src/libsyntax/print/pprust.rs | 4 +- .../feature-gate-pushpop-unsafe.rs | 14 +++ .../compile-fail/pushpop-unsafe-rejects.rs | 71 ++++++++++++++ src/test/run-pass/pushpop-unsafe-okay.rs | 56 +++++++++++ 12 files changed, 301 insertions(+), 16 deletions(-) create mode 100644 src/libsyntax/ext/pushpop_safe.rs create mode 100644 src/test/compile-fail/feature-gate-pushpop-unsafe.rs create mode 100644 src/test/compile-fail/pushpop-unsafe-rejects.rs create mode 100644 src/test/run-pass/pushpop-unsafe-okay.rs diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs index b2a064bf86c6a..a2e42245bbef8 100644 --- a/src/librustc/middle/effect.rs +++ b/src/librustc/middle/effect.rs @@ -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.saturating_add(1); + } + ast::PopUnsafeBlock(..) => { + self.unsafe_context.push_unsafe_count = + self.unsafe_context.push_unsafe_count.saturating_sub(1); + } } 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()); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index fdeed657e071b..d6afd72d3b4db 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -231,12 +231,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 { @@ -248,13 +249,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.saturating_add(1)), + ast::PopUnsafeBlock(..) => + (unsafety, blk.id, self.unsafe_push_count.saturating_sub(1)), + 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 } } } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index a0059d33bed84..fba9db401dbb1 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -810,6 +810,8 @@ pub type SpannedIdent = Spanned; pub enum BlockCheckMode { DefaultBlock, UnsafeBlock(UnsafeSource), + PushUnsafeBlock(UnsafeSource), + PopUnsafeBlock(UnsafeSource), } #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 499562edc0cc9..409ae86db35d4 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -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)); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 53befc092da88..b540e0adeea87 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1504,6 +1504,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, } } diff --git a/src/libsyntax/ext/pushpop_safe.rs b/src/libsyntax/ext/pushpop_safe.rs new file mode 100644 index 0000000000000..fee445cd31af3 --- /dev/null +++ b/src/libsyntax/ext/pushpop_safe.rs @@ -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 or the MIT license +// , 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 { + feature_gate::check_for_pushpop_syntax( + cx.ecfg.features, &cx.parse_sess.span_diagnostic, sp); + expand_pushpop_unsafe(cx, sp, tts, PushPop::Push) +} + +pub fn expand_pop_unsafe<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) + -> Box { + feature_gate::check_for_pushpop_syntax( + cx.ecfg.features, &cx.parse_sess.span_diagnostic, sp); + 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 { + 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 + }))) +} diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index ab8cf9ae6b64f..69d120cff601c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -80,6 +80,7 @@ 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), + ("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 +326,7 @@ pub struct Features { pub allow_trace_macros: bool, pub allow_internal_unstable: bool, pub allow_custom_derive: bool, + pub allow_pushpop_unsafe: bool, pub simd_ffi: bool, pub unmarked_api: bool, pub negate_unsigned: bool, @@ -348,6 +350,7 @@ impl Features { allow_trace_macros: false, allow_internal_unstable: false, allow_custom_derive: false, + allow_pushpop_unsafe: false, simd_ffi: false, unmarked_api: false, negate_unsigned: false, @@ -358,6 +361,13 @@ impl Features { } } +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, @@ -787,6 +797,7 @@ fn check_crate_inner(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_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"), diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index d93af5da13c1e..5424c0b214a4e 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -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; diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 6693eed6aced5..448857389da61 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -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))); diff --git a/src/test/compile-fail/feature-gate-pushpop-unsafe.rs b/src/test/compile-fail/feature-gate-pushpop-unsafe.rs new file mode 100644 index 0000000000000..e317b4c7d4d2a --- /dev/null +++ b/src/test/compile-fail/feature-gate-pushpop-unsafe.rs @@ -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 or the MIT license +// , 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 +} diff --git a/src/test/compile-fail/pushpop-unsafe-rejects.rs b/src/test/compile-fail/pushpop-unsafe-rejects.rs new file mode 100644 index 0000000000000..b009a670da1e1 --- /dev/null +++ b/src/test/compile-fail/pushpop-unsafe-rejects.rs @@ -0,0 +1,71 @@ +// 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 or the MIT license +// , 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. + +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 I have chosen to just have + // the stack do "saturated pop", but perhaps we would prefer to + // have cases like these two here be errors: + + pop_unsafe!{ g() }; + + push_unsafe!({ + pop_unsafe!(pop_unsafe!{ g() }) + }); + + + // Okay, back to examples that do error, even in the presence of + // "saturated pop" + + push_unsafe!({ + g(); + pop_unsafe!(pop_unsafe!({ + f() //~ ERROR: call to unsafe function + })) + }); + + pop_unsafe!({ + f(); //~ ERROR: call to unsafe function + }) + +} diff --git a/src/test/run-pass/pushpop-unsafe-okay.rs b/src/test/run-pass/pushpop-unsafe-okay.rs new file mode 100644 index 0000000000000..fc402d4136888 --- /dev/null +++ b/src/test/run-pass/pushpop-unsafe-okay.rs @@ -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 or the MIT license +// , 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)); +} From 866250c6d4ca63fb56b7d58e55f8949337663998 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 27 Jan 2015 01:22:12 +0100 Subject: [PATCH 02/27] prototype Placer protocol for unstable overloaded-box and placement-in. --- src/liballoc/boxed.rs | 107 ++++++++++++++++++++-- src/libcore/ops.rs | 112 +++++++++++++++++++++++ src/librustc/middle/expr_use_visitor.rs | 5 ++ src/libsyntax/ext/expand.rs | 114 ++++++++++++++++++++++++ 4 files changed, 333 insertions(+), 5 deletions(-) diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index d9653cecc73cf..4b571a43627f4 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -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}; @@ -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(Unique); +pub struct Box(Unique); + +/// `IntermediateBox` represents uninitialized backing storage for `Box`. +/// +/// FIXME (pnkfelix): Ideally we would just reuse `Box` instead of +/// introducing a separate `IntermediateBox`; 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{ + ptr: *mut u8, + size: usize, + align: usize, + marker: marker::PhantomData<*mut T>, +} + +impl Place for IntermediateBox { + fn pointer(&mut self) -> *mut T { + unsafe { ::core::mem::transmute(self.ptr) } + } +} + +unsafe fn finalize(b: IntermediateBox) -> Box { + let p = b.ptr as *mut T; + mem::forget(b); + mem::transmute(p) +} + +fn make_place() -> IntermediateBox { + let size = mem::size_of::(); + let align = mem::align_of::(); + + 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 BoxPlace for IntermediateBox { + fn make_place() -> IntermediateBox { make_place() } +} + +impl InPlace for IntermediateBox { + type Owner = Box; + unsafe fn finalize(self) -> Box { finalize(self) } +} + +impl Boxed for Box { + type Data = T; + type Place = IntermediateBox; + unsafe fn finalize(b: IntermediateBox) -> Box { finalize(b) } +} + +impl Placer for ExchangeHeapSingleton { + type Place = IntermediateBox; + + fn make_place(self) -> IntermediateBox { + make_place() + } +} + +impl Drop for IntermediateBox { + fn drop(&mut self) { + if self.size > 0 { + unsafe { + heap::deallocate(self.ptr, self.size, self.align) + } + } + } +} impl Box { /// Allocates memory on the heap and then moves `x` into it. @@ -199,8 +297,7 @@ impl Clone for Box { /// let y = x.clone(); /// ``` #[inline] - fn clone(&self) -> Box { box {(**self).clone()} } - + fn clone(&self) -> Box { box (HEAP) {(**self).clone()} } /// Copies `source`'s contents into `self` without creating a new allocation. /// /// # Examples diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index c2a9b8c8308cd..18e4e282f2223 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -1266,3 +1266,115 @@ impl, U: ?Sized> CoerceUnsized<*const U> for *mut T {} // *const T -> *const U impl, 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). +pub trait Place { + /// 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. +pub trait Placer { + /// `Place` is the intermedate agent guarding the + /// uninitialized state for `Data`. + type Place: InPlace; + + /// Creates a fresh place from `self`. + fn make_place(self) -> Self::Place; +} + +/// Specialization of `Place` trait supporting `in (PLACE) EXPR`. +pub trait InPlace: Place { + /// `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 +/// `` in turn dictates determines which +/// implementation of `BoxPlace` to use, namely: +/// `<::Place as BoxPlace>`. +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; /* should be bounded by BoxPlace */ + + /// 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`. +pub trait BoxPlace : Place { + /// Creates a globally fresh place. + fn make_place() -> Self; +} diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 54fc2daff2b8e..f27a96545ddfa 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -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(..) => { diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index b540e0adeea87..3a72a50b5caec 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -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, fld: &mut MacroExpander) -> P { fn push_compiler_expansion(fld: &mut MacroExpander, span: Span, expansion_desc: &str) { fld.cx.bt_push(ExpnInfo { @@ -47,6 +57,7 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { } 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 +82,109 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { }) } + // 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 = InPlace::pointer(&mut place); + // let value = EXPR; + // unsafe { + // std::ptr::write(raw_place, value); + // InPlace::finalize(place) + // } + + 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 value_ident = token::gensym_ident("value"); + 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 value = fld.cx.expr_ident(span, value_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 ptr_write = ["ptr", "write"]; + let inplace_finalize = ["ops", "InPlace", "finalize"]; + + let make_call = |fld: &mut MacroExpander, p, args| { + let path = mk_core_path(fld, placer_span, p); + let path = fld.cx.expr_path(path); + fld.cx.expr_call(span, 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 = ; + 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) + }; + + // let value = ; + let s4 = fld.cx.stmt_let(value_span, false, value_ident, value_expr); + + // unsafe { ptr::write(p_ptr, value); InPlace::finalize(place) } + let expr = { + let call_ptr_write = StmtSemi(make_call( + fld, &ptr_write, vec![p_ptr, value]), ast::DUMMY_NODE_ID); + let call_ptr_write = codemap::respan(value_span, call_ptr_write); + + let call = make_call(fld, &inplace_finalize, vec![agent]); + Some(fld.cx.expr_block(P(ast::Block { + stmts: vec![P(call_ptr_write)], + expr: Some(call), + id: ast::DUMMY_NODE_ID, + rules: ast::UnsafeBlock(ast::CompilerGenerated), + span: span, + }))) + }; + + let block = fld.cx.block_all(span, vec![s1, s2, s3, s4], expr); + fld.cx.expr_block(block) + } + + // 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); From d79bbbc4ef20a11680a004b600a90281e4c5e04a Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 5 Jun 2015 14:21:32 +0200 Subject: [PATCH 03/27] Revise placement-in expansion to use `push/pop_unsafe` and `move_val_init`. --- src/libsyntax/ext/expand.rs | 61 ++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 3a72a50b5caec..208446ed0464b 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -56,7 +56,7 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { }); } - e.and_then(|ast::Expr {id, node, span}| match node { + 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. @@ -88,12 +88,11 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { // // let p = PLACE; // let mut place = Placer::make_place(p); - // let raw_place = InPlace::pointer(&mut place); - // let value = EXPR; - // unsafe { - // std::ptr::write(raw_place, value); + // let raw_place = Place::pointer(&mut place); + // push_unsafe!({ + // std::intrinsics::move_val_init(raw_place, pop_unsafe!( EXPR )); // InPlace::finalize(place) - // } + // }) let value_span = value_expr.span; let placer_span = placer.span; @@ -103,17 +102,15 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { let placer_ident = token::gensym_ident("placer"); let agent_ident = token::gensym_ident("place"); - let value_ident = token::gensym_ident("value"); 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 value = fld.cx.expr_ident(span, value_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 ptr_write = ["ptr", "write"]; + let move_val_init = ["intrinsics", "move_val_init"]; let inplace_finalize = ["ops", "InPlace", "finalize"]; let make_call = |fld: &mut MacroExpander, p, args| { @@ -145,26 +142,23 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { stmt_let(fld, p_ptr_ident, call) }; - // let value = ; - let s4 = fld.cx.stmt_let(value_span, false, value_ident, value_expr); + // pop_unsafe!(EXPR)); + let pop_unsafe_expr = pop_unsafe_expr(fld.cx, value_expr, value_span); - // unsafe { ptr::write(p_ptr, value); InPlace::finalize(place) } + // push_unsafe!({ + // ptr::write(p_ptr, pop_unsafe!()); + // InPlace::finalize(place) + // }) let expr = { - let call_ptr_write = StmtSemi(make_call( - fld, &ptr_write, vec![p_ptr, value]), ast::DUMMY_NODE_ID); - let call_ptr_write = codemap::respan(value_span, call_ptr_write); + 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(fld.cx.expr_block(P(ast::Block { - stmts: vec![P(call_ptr_write)], - expr: Some(call), - id: ast::DUMMY_NODE_ID, - rules: ast::UnsafeBlock(ast::CompilerGenerated), - span: span, - }))) + 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, s4], expr); + let block = fld.cx.block_all(span, vec![s1, s2, s3], expr); fld.cx.expr_block(block) } @@ -474,7 +468,26 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { span: span }, fld)) } - }) + }); + + fn push_unsafe_expr(cx: &mut ExtCtxt, stmts: Vec>, + expr: P, span: Span) + -> P { + 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, span: Span) + -> P { + 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 From b325e4f28e0735bb951bdf9f1db1206bd3ee715b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 12 Feb 2015 11:30:16 +0100 Subject: [PATCH 04/27] Add feature-gates for desugaring-based `box` and placement-`in`. update test/compile-fail/feature-gate-box-expr.rs to reflect new feature gates. Part of what lands with Issue 22181. --- src/liballoc/lib.rs | 2 + src/libstd/lib.rs | 1 + src/libsyntax/ext/expand.rs | 7 +++ src/libsyntax/feature_gate.rs | 57 ++++++++++++++++++- .../compile-fail/feature-gate-box-expr.rs | 5 +- 5 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index ead0b4259a987..d72b52615ebb1 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -82,8 +82,10 @@ #![feature(no_std)] #![feature(nonzero)] #![feature(optin_builtin_traits)] +#![feature(placement_in_syntax)] #![feature(raw)] #![feature(staged_api)] +#![feature(placement_in_syntax)] #![feature(unboxed_closures)] #![feature(unique)] #![feature(unsafe_no_drop_flag, filling_drop)] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 82bc1314ad547..784f5eecf0be9 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -234,6 +234,7 @@ #![feature(no_std)] #![feature(oom)] #![feature(optin_builtin_traits)] +#![feature(placement_in_syntax)] #![feature(rand)] #![feature(raw)] #![feature(reflect_marker)] diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 208446ed0464b..93e744287bae5 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -56,6 +56,7 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { }); } + 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 @@ -94,6 +95,12 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { // 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); + let value_span = value_expr.span; let placer_span = placer.span; diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 69d120cff601c..8c6855036f6e9 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -80,6 +80,7 @@ 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), @@ -326,6 +327,8 @@ 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, @@ -350,6 +353,8 @@ 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, @@ -361,6 +366,29 @@ 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; @@ -376,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); @@ -498,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> { @@ -764,7 +817,7 @@ fn check_crate_inner(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"); @@ -797,6 +850,8 @@ fn check_crate_inner(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"), diff --git a/src/test/compile-fail/feature-gate-box-expr.rs b/src/test/compile-fail/feature-gate-box-expr.rs index 8f8b035f4a96b..f5c9a63b79bd6 100644 --- a/src/test/compile-fail/feature-gate-box-expr.rs +++ b/src/test/compile-fail/feature-gate-box-expr.rs @@ -17,6 +17,9 @@ fn main() { let x = box () 'c'; //~ ERROR box expression syntax is experimental println!("x: {}", x); - let x = box (HEAP) 'c'; //~ ERROR box expression syntax is experimental + 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); } From 75da81e78231a6fb5d90b3918bebe867249eb14c Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 5 Jun 2015 14:17:49 +0200 Subject: [PATCH 05/27] Change signature for `move_val_init` intrinsic to take `*mut T` for `dest`. rebase update to typeck/check/mod.rs --- src/libcore/intrinsics.rs | 8 ++++++++ src/librustc_typeck/check/mod.rs | 4 +--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index ef022179772c4..a1d7867431be0 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -184,6 +184,14 @@ extern "rust-intrinsic" { /// elements. pub fn size_of() -> 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(dst: *mut T, src: T); + + // SNAP ba0e1cd + #[cfg(stage0)] /// Moves a value to an uninitialized memory location. /// /// Drop glue is not run on the destination. diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d6afd72d3b4db..76ba89caaa076 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4894,9 +4894,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()) From cb5f0b4971481fd8985c78c7a24e422f45ea6560 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 5 Jun 2015 14:20:35 +0200 Subject: [PATCH 06/27] Avoid feature-warnings on stage0. --- src/libstd/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 784f5eecf0be9..907eb3ed401ff 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -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)] From 07afe91fda3df8e3b7bbb14006a64d136627f33c Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 5 Jun 2015 18:51:23 +0200 Subject: [PATCH 07/27] Allow unstable code to be injected by placement-`in` expansion. (Over time the stability checking has gotten more finicky; in particular one must attach the (whole) span of the original `in PLACE BLOCK` expression to the injected references to unstable paths, as noted in the comments.) call `push_compiler_expansion` during the placement-`in` expansion. --- src/libsyntax/ext/expand.rs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 93e744287bae5..faa1e5b2f515f 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -50,12 +50,23 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { 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, }, }); } + // 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 { @@ -101,6 +112,8 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { &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; @@ -121,9 +134,14 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { let inplace_finalize = ["ops", "InPlace", "finalize"]; let make_call = |fld: &mut MacroExpander, p, args| { - let path = mk_core_path(fld, placer_span, p); + // 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); - fld.cx.expr_call(span, path, args) + 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| { @@ -166,7 +184,9 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { }; let block = fld.cx.block_all(span, vec![s1, s2, s3], expr); - fld.cx.expr_block(block) + let result = fld.cx.expr_block(block); + fld.cx.bt_pop(); + result } // Issue #22181: From 9ca1c618794b3da0603ffedff488cd42a32c4eb6 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 5 Jun 2015 18:53:17 +0200 Subject: [PATCH 08/27] Instrumentation in effort to understand treatment of `allow_internal_unstable`. It is all `debug!` instrumentation so it should not impose a cost on non-debug builds. --- src/librustc/middle/stability.rs | 14 ++++++++++++-- src/libsyntax/codemap.rs | 4 ++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 30553d62719bf..6d3bc7fb68c14 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -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); } diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index e6bc3218897d9..17e6b2c2e12df 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -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); From bb952358aa674b815f6180520eb5ddf2289e6401 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 21 Jul 2015 14:31:07 +0200 Subject: [PATCH 09/27] Factor feature gate tests for box syntax into two separate files. The two tests are separate since the current implementation performs the feature gate checks at distinct phases in the compilation, with an `abort_if_errors` calls separating them. --- .../compile-fail/feature-gate-box-expr.rs | 16 +++++------ .../feature-gate-placement-expr.rs | 27 +++++++++++++++++++ 2 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 src/test/compile-fail/feature-gate-placement-expr.rs diff --git a/src/test/compile-fail/feature-gate-box-expr.rs b/src/test/compile-fail/feature-gate-box-expr.rs index f5c9a63b79bd6..9640b2b475b6a 100644 --- a/src/test/compile-fail/feature-gate-box-expr.rs +++ b/src/test/compile-fail/feature-gate-box-expr.rs @@ -8,18 +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 placement-in expression syntax is experimental - println!("x: {}", x); - - let x = in HEAP { 'c' }; //~ ERROR placement-in expression syntax is experimental - println!("x: {}", x); } diff --git a/src/test/compile-fail/feature-gate-placement-expr.rs b/src/test/compile-fail/feature-gate-placement-expr.rs new file mode 100644 index 0000000000000..ac367340a61fe --- /dev/null +++ b/src/test/compile-fail/feature-gate-placement-expr.rs @@ -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 or the MIT license +// , 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); +} From 4a5fb4bd2a4c9d063bfe4868c90ff005ff3d3a62 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 21 Jul 2015 14:31:30 +0200 Subject: [PATCH 10/27] Add necessary feature gate opt-in for the pushpop_unsafe test. --- src/test/compile-fail/pushpop-unsafe-rejects.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/compile-fail/pushpop-unsafe-rejects.rs b/src/test/compile-fail/pushpop-unsafe-rejects.rs index b009a670da1e1..ad6fd5094db4a 100644 --- a/src/test/compile-fail/pushpop-unsafe-rejects.rs +++ b/src/test/compile-fail/pushpop-unsafe-rejects.rs @@ -13,6 +13,8 @@ // 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; } From a9d79978b60a2433620dce2624202f2e120efaf1 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 21 Jul 2015 14:31:51 +0200 Subject: [PATCH 11/27] Update the `intrinsic-move-val.rs` test to reflect its newer signature. --- src/test/run-pass/intrinsic-move-val.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/run-pass/intrinsic-move-val.rs b/src/test/run-pass/intrinsic-move-val.rs index 2e75f2dccd1ba..84af4fd0a08ea 100644 --- a/src/test/run-pass/intrinsic-move-val.rs +++ b/src/test/run-pass/intrinsic-move-val.rs @@ -20,7 +20,7 @@ use std::mem::{self, transmute}; mod rusti { extern "rust-intrinsic" { pub fn init() -> T; - pub fn move_val_init(dst: &mut T, src: T); + pub fn move_val_init(dst: *mut T, src: T); } } From 80465334aa2d371b368b5d734cdcc1061d895ffb Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 21 Jul 2015 14:32:11 +0200 Subject: [PATCH 12/27] Update `issue-14084.rs` test to reflect changes to error output. --- src/test/compile-fail/issue-14084.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/compile-fail/issue-14084.rs b/src/test/compile-fail/issue-14084.rs index 003c6644f7f02..8e566d7aafa68 100644 --- a/src/test/compile-fail/issue-14084.rs +++ b/src/test/compile-fail/issue-14084.rs @@ -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 } From e0b44797ce644210af0fd1596d1aa2e5338c976b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 21 Jul 2015 14:32:33 +0200 Subject: [PATCH 13/27] Add new feature gate opt-in necessary for `new-box-syntax.rs`. --- src/test/run-pass/new-box-syntax.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/run-pass/new-box-syntax.rs b/src/test/run-pass/new-box-syntax.rs index b5a54a90ae758..d2ebd6863fa6d 100644 --- a/src/test/run-pass/new-box-syntax.rs +++ b/src/test/run-pass/new-box-syntax.rs @@ -15,6 +15,7 @@ #![allow(warnings)] #![feature(box_syntax, box_heap)] +#![feature(placement_in_syntax)] // Tests that the new `box` syntax works with unique pointers. From 9f6f35bef4986e02f811a1fb7c3da212241784a8 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 21 Jul 2015 18:31:37 +0200 Subject: [PATCH 14/27] Added support for parsing `in PLACE { BLOCK_CONTENT }`. --- src/libsyntax/parse/parser.rs | 41 ++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index db1b2489f1d9e..6c10d6efa5045 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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 ba0e1cd + // 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 () ` to \ + // `in { }`)"); + + // 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,9 @@ impl<'a> Parser<'a> { self.span_err(span, &format!("expected expression, found `{}`", this_token_to_string)); - let box_span = mk_sp(lo, box_hi); - self.span_suggestion(box_span, - "try using `box()` instead:", - "box()".to_string()); + let box_span = mk_sp(lo, keyword_hi); + let new_expr = format!("box () {}", pprust::expr_to_string(&place)); + self.span_suggestion(box_span, "try using `box ()` instead:", new_expr); self.abort_if_errors(); } let subexpression = try!(self.parse_prefix_expr()); @@ -2650,6 +2674,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. From 9d6cc05a062c9cf131939ab7c9de5acbd6afd8cc Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 22 Jul 2015 15:09:51 +0200 Subject: [PATCH 15/27] uncomment feature-gate testing for `in PLACE BLOCK` now that its in the parser. --- src/test/compile-fail/feature-gate-placement-expr.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/compile-fail/feature-gate-placement-expr.rs b/src/test/compile-fail/feature-gate-placement-expr.rs index ac367340a61fe..64a1d49f9600c 100644 --- a/src/test/compile-fail/feature-gate-placement-expr.rs +++ b/src/test/compile-fail/feature-gate-placement-expr.rs @@ -22,6 +22,6 @@ fn main() { 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); + let x = in HEAP { 'c' }; //~ ERROR placement-in expression syntax is experimental + println!("x: {}", x); } From a81f88d4d642d0cae324b301df3fe089f0d090b4 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 22 Jul 2015 15:30:05 +0200 Subject: [PATCH 16/27] Add actual use of the `struct Structure` lying dormant in `new-box-syntax.rs`. The original test program exercised the (garbage-collected heap) allocation form `box (GC) ...`, feeding an instance of `Structure` in as the expression. This obviously went away when `GC` went away, but there's no reason for us not to include an appropriate unit test here for the same form, just for heap-allocated instances of `Structure`. --- src/test/run-pass/new-box-syntax.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/run-pass/new-box-syntax.rs b/src/test/run-pass/new-box-syntax.rs index d2ebd6863fa6d..503699950012c 100644 --- a/src/test/run-pass/new-box-syntax.rs +++ b/src/test/run-pass/new-box-syntax.rs @@ -31,4 +31,9 @@ pub fn main() { let y: Box = box 2; let b: Box = box()(1 + 2); let c = box()(3 + 4); + + let s: Box = box Structure { + x: 3, + y: 4, + }; } From 7a700341e7bd83cd0a36f79e95cfa29463eb77a6 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 22 Jul 2015 15:32:17 +0200 Subject: [PATCH 17/27] refine set of allowed warnings in `new-box-syntax.rs` test. --- src/test/run-pass/new-box-syntax.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/run-pass/new-box-syntax.rs b/src/test/run-pass/new-box-syntax.rs index 503699950012c..8cf3bcbb6781a 100644 --- a/src/test/run-pass/new-box-syntax.rs +++ b/src/test/run-pass/new-box-syntax.rs @@ -13,7 +13,7 @@ /* 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)] From 505cd8a3cc9ddc89ff766a07cc90797421296d34 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 22 Jul 2015 15:33:09 +0200 Subject: [PATCH 18/27] Add test of placement-in syntax, analogous to `new-box-syntax.rs` --- src/test/run-pass/placement-in-syntax.rs | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/test/run-pass/placement-in-syntax.rs diff --git a/src/test/run-pass/placement-in-syntax.rs b/src/test/run-pass/placement-in-syntax.rs new file mode 100644 index 0000000000000..7bda9ae252439 --- /dev/null +++ b/src/test/run-pass/placement-in-syntax.rs @@ -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 or the MIT license +// , 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 = in HEAP { 2 }; + let b: Box = in HEAP { 1 + 2 }; + let c = in HEAP { 3 + 4 }; + + let s: Box = in HEAP { + Structure { + x: 3, + y: 4, + } + }; +} From abad7d6bba9d3fa9d5f1126c8ef6f1b413db1152 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 22 Jul 2015 23:23:36 +0200 Subject: [PATCH 19/27] placate `make tidy`. --- src/libcore/intrinsics.rs | 2 +- src/libsyntax/parse/parser.rs | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index a1d7867431be0..1d466895f2cfb 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -190,7 +190,7 @@ extern "rust-intrinsic" { /// Drop glue is not run on the destination. pub fn move_val_init(dst: *mut T, src: T); - // SNAP ba0e1cd + // SNAP d4432b3 #[cfg(stage0)] /// Moves a value to an uninitialized memory location. /// diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 6c10d6efa5045..c229ac31fc029 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2638,7 +2638,7 @@ impl<'a> Parser<'a> { // ... but for now: check for a place: `box(PLACE) EXPR`. if try!(self.eat(&token::OpenDelim(token::Paren)) ){ - // SNAP ba0e1cd + // SNAP d4432b3 // Enable this warning after snapshot ... // // let box_span = mk_sp(lo, self.last_span.hi); @@ -2659,9 +2659,15 @@ impl<'a> Parser<'a> { self.span_err(span, &format!("expected expression, found `{}`", this_token_to_string)); + + // Spanning just keyword avoids constructing + // printout of arg expression (which starts + // with parenthesis, as established above). + let box_span = mk_sp(lo, keyword_hi); - let new_expr = format!("box () {}", pprust::expr_to_string(&place)); - self.span_suggestion(box_span, "try using `box ()` instead:", new_expr); + self.span_suggestion(box_span, + "try using `box ()` instead:", + format!("box ()")); self.abort_if_errors(); } let subexpression = try!(self.parse_prefix_expr()); From 1905a498751af69ffe78de8eae0c4e83d562d175 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 22 Jul 2015 23:25:52 +0200 Subject: [PATCH 20/27] address review feedback: remove dupe feature opt-in. --- src/liballoc/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index d72b52615ebb1..480c1a502c6fb 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -85,7 +85,6 @@ #![feature(placement_in_syntax)] #![feature(raw)] #![feature(staged_api)] -#![feature(placement_in_syntax)] #![feature(unboxed_closures)] #![feature(unique)] #![feature(unsafe_no_drop_flag, filling_drop)] From 73df224f0559e31e6d4623978cf25ada9ba1e2d8 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 23 Jul 2015 16:01:46 +0200 Subject: [PATCH 21/27] Review feedback: add unstable marker to Placer API and put in bound that now works. --- src/liballoc/lib.rs | 3 +++ src/libcore/ops.rs | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 480c1a502c6fb..f66495c4057c4 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -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)] @@ -83,6 +85,7 @@ #![feature(nonzero)] #![feature(optin_builtin_traits)] #![feature(placement_in_syntax)] +#![feature(placement_new_protocol)] #![feature(raw)] #![feature(staged_api)] #![feature(unboxed_closures)] diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 18e4e282f2223..2ea42011a5cf2 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -1285,6 +1285,7 @@ impl, U: ?Sized> CoerceUnsized<*const U> for *const T {} /// 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 { /// Returns the address where the input value will be written. /// Note that the data at this address is generally uninitialized, @@ -1315,6 +1316,7 @@ pub trait Place { /// 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 { /// `Place` is the intermedate agent guarding the /// uninitialized state for `Data`. @@ -1325,6 +1327,7 @@ pub trait Placer { } /// Specialization of `Place` trait supporting `in (PLACE) EXPR`. +#[unstable(feature = "placement_new_protocol")] pub trait InPlace: Place { /// `Owner` is the type of the end value of `in (PLACE) EXPR` /// @@ -1361,11 +1364,12 @@ pub trait InPlace: Place { /// `` in turn dictates determines which /// implementation of `BoxPlace` to use, namely: /// `<::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; /* should be bounded by BoxPlace */ + type Place: BoxPlace; /// Converts filled place into final owning value, shifting /// deallocation/cleanup responsibilities (if any remain), over to @@ -1374,6 +1378,7 @@ pub trait Boxed { } /// Specialization of `Place` trait supporting `box EXPR`. +#[unstable(feature = "placement_new_protocol")] pub trait BoxPlace : Place { /// Creates a globally fresh place. fn make_place() -> Self; From 225298c3771f43582df603a137554d997a60592e Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 23 Jul 2015 15:59:58 +0200 Subject: [PATCH 22/27] fix doc-tests by opting into `placement_in_syntax` feature where necessary. --- src/liballoc/boxed.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 4b571a43627f4..acf2209423323 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -75,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() { From 5682e2a1aac9566862fd255ce631c59946917d3f Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 23 Jul 2015 16:00:28 +0200 Subject: [PATCH 23/27] fix pretty printing tests by opting into the features that the expanded code needs. --- src/test/run-pass/new-box-syntax.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/run-pass/new-box-syntax.rs b/src/test/run-pass/new-box-syntax.rs index 8cf3bcbb6781a..da57e8682ca60 100644 --- a/src/test/run-pass/new-box-syntax.rs +++ b/src/test/run-pass/new-box-syntax.rs @@ -17,6 +17,10 @@ #![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. use std::boxed::{Box, HEAP}; From 565df576e8a450ed073e4f02f7d796f05f6ff502 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 23 Jul 2015 16:00:58 +0200 Subject: [PATCH 24/27] Update suggestion from parenthesized-box-expr-message to reflect new output spacing. --- src/test/parse-fail/parenthesized-box-expr-message.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/parse-fail/parenthesized-box-expr-message.rs b/src/test/parse-fail/parenthesized-box-expr-message.rs index 09d32a71dea48..4c32d2f041e3c 100644 --- a/src/test/parse-fail/parenthesized-box-expr-message.rs +++ b/src/test/parse-fail/parenthesized-box-expr-message.rs @@ -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 `;` } From 44bb0dd4a3bdd84b81dcf537d161a9dd9e9068ea Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 23 Jul 2015 16:20:59 +0200 Subject: [PATCH 25/27] review feedback: Use checked-arith instead of saturated-arith for `push_unsafe!` and `pop_unsafe!`. --- src/librustc/middle/effect.rs | 4 ++-- src/librustc_typeck/check/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs index a2e42245bbef8..3fe1e2f5e8369 100644 --- a/src/librustc/middle/effect.rs +++ b/src/librustc/middle/effect.rs @@ -124,11 +124,11 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> { } ast::PushUnsafeBlock(..) => { self.unsafe_context.push_unsafe_count = - self.unsafe_context.push_unsafe_count.saturating_add(1); + self.unsafe_context.push_unsafe_count.checked_add(1).unwrap(); } ast::PopUnsafeBlock(..) => { self.unsafe_context.push_unsafe_count = - self.unsafe_context.push_unsafe_count.saturating_sub(1); + self.unsafe_context.push_unsafe_count.checked_sub(1).unwrap(); } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 76ba89caaa076..a047092d147eb 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -251,9 +251,9 @@ impl UnsafetyState { unsafety => { let (unsafety, def, count) = match blk.rules { ast::PushUnsafeBlock(..) => - (unsafety, blk.id, self.unsafe_push_count.saturating_add(1)), + (unsafety, blk.id, self.unsafe_push_count.checked_add(1).unwrap()), ast::PopUnsafeBlock(..) => - (unsafety, blk.id, self.unsafe_push_count.saturating_sub(1)), + (unsafety, blk.id, self.unsafe_push_count.checked_sub(1).unwrap()), ast::UnsafeBlock(..) => (ast::Unsafety::Unsafe, blk.id, self.unsafe_push_count), ast::DefaultBlock => From 2d68d09b4679018d0ba3faf41d239251991bf17b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 23 Jul 2015 16:22:05 +0200 Subject: [PATCH 26/27] review feedback: common-subexpression-elim across functions in pushpop_safe impl. --- src/libsyntax/ext/pushpop_safe.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libsyntax/ext/pushpop_safe.rs b/src/libsyntax/ext/pushpop_safe.rs index fee445cd31af3..a67d550d3cd21 100644 --- a/src/libsyntax/ext/pushpop_safe.rs +++ b/src/libsyntax/ext/pushpop_safe.rs @@ -48,24 +48,24 @@ enum PushPop { Push, Pop } pub fn expand_push_unsafe<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> Box { - feature_gate::check_for_pushpop_syntax( - cx.ecfg.features, &cx.parse_sess.span_diagnostic, sp); expand_pushpop_unsafe(cx, sp, tts, PushPop::Push) } pub fn expand_pop_unsafe<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> Box { - feature_gate::check_for_pushpop_syntax( - cx.ecfg.features, &cx.parse_sess.span_diagnostic, sp); 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 { + 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, _ => { From d066a7b5069ff857a5bffe7cb5168fe63158144f Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 23 Jul 2015 23:39:02 +0200 Subject: [PATCH 27/27] update compile-fail/pushpop-unsafe-rejects.rs to reflect switch from saturated to checked arith. --- .../compile-fail/pushpop-unsafe-rejects.rs | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/test/compile-fail/pushpop-unsafe-rejects.rs b/src/test/compile-fail/pushpop-unsafe-rejects.rs index ad6fd5094db4a..72c065ae71417 100644 --- a/src/test/compile-fail/pushpop-unsafe-rejects.rs +++ b/src/test/compile-fail/pushpop-unsafe-rejects.rs @@ -45,29 +45,30 @@ fn main() { }); - // Note: For implementation simplicity I have chosen to just have - // the stack do "saturated pop", but perhaps we would prefer to - // have cases like these two here be errors: - - pop_unsafe!{ g() }; - - push_unsafe!({ - pop_unsafe!(pop_unsafe!{ g() }) - }); - - - // Okay, back to examples that do error, even in the presence of - // "saturated pop" - - push_unsafe!({ - g(); - pop_unsafe!(pop_unsafe!({ - f() //~ ERROR: call to unsafe function - })) - }); - - pop_unsafe!({ - 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 + // }) }