From 1829fa5199bae5a192c771807c532badce14be37 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 5 Jun 2015 08:31:27 +0200 Subject: 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/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 +- 7 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 src/libsyntax/ext/pushpop_safe.rs (limited to 'src/libsyntax') diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index a0059d33bed..fba9db401db 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 499562edc0c..409ae86db35 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 53befc092da..b540e0adeea 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 00000000000..fee445cd31a --- /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 ab8cf9ae6b6..69d120cff60 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 d93af5da13c..5424c0b214a 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 6693eed6ace..448857389da 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))); -- cgit 1.4.1-3-g733a5 From 866250c6d4ca63fb56b7d58e55f8949337663998 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 27 Jan 2015 01:22:12 +0100 Subject: 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(-) (limited to 'src/libsyntax') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index d9653cecc73..4b571a43627 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 c2a9b8c8308..18e4e282f22 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 54fc2daff2b..f27a96545dd 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 b540e0adeea..3a72a50b5ca 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); -- cgit 1.4.1-3-g733a5 From d79bbbc4ef20a11680a004b600a90281e4c5e04a Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 5 Jun 2015 14:21:32 +0200 Subject: 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(-) (limited to 'src/libsyntax') diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 3a72a50b5ca..208446ed046 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 -- cgit 1.4.1-3-g733a5 From b325e4f28e0735bb951bdf9f1db1206bd3ee715b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 12 Feb 2015 11:30:16 +0100 Subject: 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 +++++++++++++++++++++++++- src/test/compile-fail/feature-gate-box-expr.rs | 5 ++- 5 files changed, 70 insertions(+), 2 deletions(-) (limited to 'src/libsyntax') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index ead0b4259a9..d72b52615eb 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 82bc1314ad5..784f5eecf0b 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 208446ed046..93e744287ba 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 69d120cff60..8c6855036f6 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 8f8b035f4a9..f5c9a63b79b 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); } -- cgit 1.4.1-3-g733a5 From 07afe91fda3df8e3b7bbb14006a64d136627f33c Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 5 Jun 2015 18:51:23 +0200 Subject: 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(-) (limited to 'src/libsyntax') diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 93e744287ba..faa1e5b2f51 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: -- cgit 1.4.1-3-g733a5 From 9ca1c618794b3da0603ffedff488cd42a32c4eb6 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 5 Jun 2015 18:53:17 +0200 Subject: 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(-) (limited to 'src/libsyntax') diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 30553d62719..6d3bc7fb68c 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 e6bc3218897..17e6b2c2e12 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); -- cgit 1.4.1-3-g733a5 From 9f6f35bef4986e02f811a1fb7c3da212241784a8 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 21 Jul 2015 18:31:37 +0200 Subject: Added support for parsing `in PLACE { BLOCK_CONTENT }`. --- src/libsyntax/parse/parser.rs | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) (limited to 'src/libsyntax') diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index db1b2489f1d..6c10d6efa50 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. -- cgit 1.4.1-3-g733a5 From abad7d6bba9d3fa9d5f1126c8ef6f1b413db1152 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 22 Jul 2015 23:23:36 +0200 Subject: placate `make tidy`. --- src/libcore/intrinsics.rs | 2 +- src/libsyntax/parse/parser.rs | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'src/libsyntax') diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index a1d7867431b..1d466895f2c 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 6c10d6efa50..c229ac31fc0 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()); -- cgit 1.4.1-3-g733a5 From 2d68d09b4679018d0ba3faf41d239251991bf17b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 23 Jul 2015 16:22:05 +0200 Subject: 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(-) (limited to 'src/libsyntax') diff --git a/src/libsyntax/ext/pushpop_safe.rs b/src/libsyntax/ext/pushpop_safe.rs index fee445cd31a..a67d550d3cd 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, _ => { -- cgit 1.4.1-3-g733a5