From cbc31df4fc084b47a5c6456df2efb6e28b82a7da Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 13 May 2014 14:58:29 -0700 Subject: [PATCH 01/33] std: Move the owned module from core to std The compiler was updated to recognize that implementations for ty_uniq(..) are allowed if the Box lang item is located in the current crate. This enforces the idea that libcore cannot allocated, and moves all related trait implementations from libcore to libstd. This is a breaking change in that the AnyOwnExt trait has moved from the any module to the owned module. Any previous users of std::any::AnyOwnExt should now use std::owned::AnyOwnExt instead. This was done because the trait is intended for Box traits and only Box traits. [breaking-change] --- src/libcore/any.rs | 34 +----- src/libcore/clone.rs | 30 +++--- src/libcore/cmp.rs | 24 ----- src/libcore/default.rs | 6 -- src/libcore/iter.rs | 2 +- src/libcore/lib.rs | 5 +- src/libcore/mem.rs | 2 +- src/libcore/owned.rs | 85 +++++++++++++-- src/librustc/middle/typeck/coherence.rs | 17 ++- src/libstd/lib.rs | 8 +- src/libstd/owned.rs | 101 ++++++++++++++++++ src/libstd/rt/task.rs | 3 +- src/libstd/task.rs | 5 +- .../run-pass/unit-like-struct-drop-run.rs | 2 +- 14 files changed, 219 insertions(+), 105 deletions(-) create mode 100644 src/libstd/owned.rs diff --git a/src/libcore/any.rs b/src/libcore/any.rs index b0227fd60bfeb..70cd46dcfa2b4 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs @@ -22,11 +22,8 @@ use mem::{transmute, transmute_copy}; use option::{Option, Some, None}; -use owned::Box; use raw::TraitObject; -use result::{Result, Ok, Err}; use intrinsics::TypeId; -use intrinsics; /// A type with no inhabitants pub enum Void { } @@ -117,38 +114,11 @@ impl<'a> AnyMutRefExt<'a> for &'a mut Any { } } -/// Extension methods for an owning `Any` trait object -pub trait AnyOwnExt { - /// Returns the boxed value if it is of type `T`, or - /// `Err(Self)` if it isn't. - fn move(self) -> Result, Self>; -} - -impl AnyOwnExt for Box { - #[inline] - fn move(self) -> Result, Box> { - if self.is::() { - unsafe { - // Get the raw representation of the trait object - let to: TraitObject = transmute_copy(&self); - - // Prevent destructor on self being run - intrinsics::forget(self); - - // Extract the data pointer - Ok(transmute(to.data)) - } - } else { - Err(self) - } - } -} - #[cfg(test)] mod tests { use prelude::*; use super::*; - use owned::Box; + use realstd::owned::{Box, AnyOwnExt}; use realstd::str::StrAllocating; #[deriving(Eq, Show)] @@ -253,6 +223,8 @@ mod tests { #[test] fn any_move() { + use realstd::any::Any; + use realstd::result::{Ok, Err}; let a = box 8u as Box; let b = box Test as Box; diff --git a/src/libcore/clone.rs b/src/libcore/clone.rs index 06cbaf1981280..cd66beabc12de 100644 --- a/src/libcore/clone.rs +++ b/src/libcore/clone.rs @@ -21,8 +21,6 @@ the `clone` method. */ -use owned::Box; - /// A common trait for cloning an object. pub trait Clone { /// Returns a copy of the value. The contents of owned pointers @@ -41,18 +39,6 @@ pub trait Clone { } } -impl Clone for Box { - /// Return a copy of the owned box. - #[inline] - fn clone(&self) -> Box { box {(**self).clone()} } - - /// Perform copy-assignment from `source` by reusing the existing allocation. - #[inline] - fn clone_from(&mut self, source: &Box) { - (**self).clone_from(&(**source)); - } -} - impl Clone for @T { /// Return a shallow copy of the managed box. #[inline] @@ -129,12 +115,22 @@ extern_fn_clone!(A, B, C, D, E, F, G, H) #[cfg(test)] mod test { use prelude::*; - use owned::Box; + use realstd::owned::Box; + + fn realclone(t: &T) -> T { + use realstd::clone::Clone; + t.clone() + } + + fn realclone_from(t1: &mut T, t2: &T) { + use realstd::clone::Clone; + t1.clone_from(t2) + } #[test] fn test_owned_clone() { let a = box 5i; - let b: Box = a.clone(); + let b: Box = realclone(&a); assert_eq!(a, b); } @@ -157,7 +153,7 @@ mod test { fn test_clone_from() { let a = box 5; let mut b = box 10; - b.clone_from(&a); + realclone_from(&mut b, &a); assert_eq!(*b, 5); } diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index af611cd94e506..f2b1c1cd4cde6 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -193,7 +193,6 @@ pub fn max(v1: T, v2: T) -> T { #[cfg(not(test))] mod impls { use cmp::{Ord, TotalOrd, Eq, TotalEq, Ordering}; - use owned::Box; // & pointers impl<'a, T: Eq> Eq for &'a T { @@ -240,29 +239,6 @@ mod impls { fn cmp(&self, other: &@T) -> Ordering { (**self).cmp(*other) } } impl TotalEq for @T {} - - // box pointers - impl Eq for Box { - #[inline] - fn eq(&self, other: &Box) -> bool { *(*self) == *(*other) } - #[inline] - fn ne(&self, other: &Box) -> bool { *(*self) != *(*other) } - } - impl Ord for Box { - #[inline] - fn lt(&self, other: &Box) -> bool { *(*self) < *(*other) } - #[inline] - fn le(&self, other: &Box) -> bool { *(*self) <= *(*other) } - #[inline] - fn ge(&self, other: &Box) -> bool { *(*self) >= *(*other) } - #[inline] - fn gt(&self, other: &Box) -> bool { *(*self) > *(*other) } - } - impl TotalOrd for Box { - #[inline] - fn cmp(&self, other: &Box) -> Ordering { (**self).cmp(*other) } - } - impl TotalEq for Box {} } #[cfg(test)] diff --git a/src/libcore/default.rs b/src/libcore/default.rs index 9cf3a76364887..af65fcc5a779d 100644 --- a/src/libcore/default.rs +++ b/src/libcore/default.rs @@ -10,8 +10,6 @@ //! The `Default` trait for types which may have meaningful default values -use owned::Box; - /// A trait that types which have a useful default value should implement. pub trait Default { /// Return the "default value" for a type. @@ -21,7 +19,3 @@ pub trait Default { impl Default for @T { fn default() -> @T { @Default::default() } } - -impl Default for Box { - fn default() -> Box { box Default::default() } -} diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 10ae30cf39d0c..f6a77d6decae0 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -2334,7 +2334,7 @@ mod tests { use realstd::num; use cmp; - use owned::Box; + use realstd::owned::Box; use uint; #[test] diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 06bd46fe9ad97..97b086a093d2a 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -30,7 +30,6 @@ #[cfg(test)] pub use cmp = realcore::cmp; #[cfg(test)] pub use kinds = realcore::kinds; #[cfg(test)] pub use ops = realcore::ops; -#[cfg(test)] pub use owned = realcore::owned; #[cfg(test)] pub use ty = realcore::ty; #[cfg(not(test))] @@ -73,7 +72,6 @@ pub mod ptr; #[cfg(not(test))] pub mod ops; #[cfg(not(test))] pub mod ty; #[cfg(not(test))] pub mod cmp; -#[cfg(not(test))] pub mod owned; pub mod clone; pub mod default; pub mod container; @@ -95,6 +93,9 @@ pub mod slice; pub mod str; pub mod tuple; +#[cfg(stage0, not(test))] +pub mod owned; + mod failure; // FIXME: this module should not exist. Once owned allocations are no longer a diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index feb66f569645b..c5c6d75177728 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -380,7 +380,7 @@ mod tests { use mem::*; use option::{Some,None}; use realstd::str::StrAllocating; - use owned::Box; + use realstd::owned::Box; use raw; #[test] diff --git a/src/libcore/owned.rs b/src/libcore/owned.rs index d5cdd9c39b67c..3af12c5154c29 100644 --- a/src/libcore/owned.rs +++ b/src/libcore/owned.rs @@ -10,10 +10,14 @@ //! Operations on unique pointer types -// FIXME: this module should not exist in libcore. It must currently because the -// Box implementation is quite ad-hoc in the compiler. Once there is -// proper support in the compiler this type will be able to be defined in -// its own module. +use any::{Any, AnyRefExt}; +use clone::Clone; +use cmp::{Eq, Ord, TotalEq, TotalOrd, Ordering}; +use default::Default; +use intrinsics; +use mem; +use raw::TraitObject; +use result::{Ok, Err, Result}; /// A value that represents the global exchange heap. This is the default /// place that the `box` keyword allocates into when no place is supplied. @@ -23,16 +27,75 @@ /// let foo = box(HEAP) Bar::new(...); /// let foo = box Bar::new(...); #[lang="exchange_heap"] -#[cfg(not(test))] -pub static HEAP: () = (); - -#[cfg(test)] pub static HEAP: () = (); /// A type that represents a uniquely-owned value. #[lang="owned_box"] -#[cfg(not(test))] pub struct Box(*T); -#[cfg(test)] -pub struct Box(*T); +impl Default for Box { + fn default() -> Box { box Default::default() } +} + +impl Clone for Box { + /// Return a copy of the owned box. + #[inline] + fn clone(&self) -> Box { box {(**self).clone()} } + + /// Perform copy-assignment from `source` by reusing the existing allocation. + #[inline] + fn clone_from(&mut self, source: &Box) { + (**self).clone_from(&(**source)); + } +} + +// box pointers +impl Eq for Box { + #[inline] + fn eq(&self, other: &Box) -> bool { *(*self) == *(*other) } + #[inline] + fn ne(&self, other: &Box) -> bool { *(*self) != *(*other) } +} +impl Ord for Box { + #[inline] + fn lt(&self, other: &Box) -> bool { *(*self) < *(*other) } + #[inline] + fn le(&self, other: &Box) -> bool { *(*self) <= *(*other) } + #[inline] + fn ge(&self, other: &Box) -> bool { *(*self) >= *(*other) } + #[inline] + fn gt(&self, other: &Box) -> bool { *(*self) > *(*other) } +} +impl TotalOrd for Box { + #[inline] + fn cmp(&self, other: &Box) -> Ordering { (**self).cmp(*other) } +} +impl TotalEq for Box {} + +/// Extension methods for an owning `Any` trait object +pub trait AnyOwnExt { + /// Returns the boxed value if it is of type `T`, or + /// `Err(Self)` if it isn't. + fn move(self) -> Result, Self>; +} + +impl AnyOwnExt for Box { + #[inline] + fn move(self) -> Result, Box> { + if self.is::() { + unsafe { + // Get the raw representation of the trait object + let to: TraitObject = + *mem::transmute::<&Box, &TraitObject>(&self); + + // Prevent destructor on self being run + intrinsics::forget(self); + + // Extract the data pointer + Ok(mem::transmute(to.data)) + } + } else { + Err(self) + } + } +} diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs index f93eb4550a94a..f853b0dbad40f 100644 --- a/src/librustc/middle/typeck/coherence.rs +++ b/src/librustc/middle/typeck/coherence.rs @@ -90,7 +90,7 @@ fn get_base_type(inference_context: &InferCtxt, } } -fn type_is_defined_in_local_crate(original_type: t) -> bool { +fn type_is_defined_in_local_crate(tcx: &ty::ctxt, original_type: t) -> bool { /*! * * For coherence, when we have `impl Trait for Type`, we need to @@ -109,6 +109,14 @@ fn type_is_defined_in_local_crate(original_type: t) -> bool { found_nominal = true; } } + ty_uniq(..) => { + match tcx.lang_items.owned_box() { + Some(did) if did.krate == ast::LOCAL_CRATE => { + found_nominal = true; + } + _ => {} + } + } _ => { } } @@ -194,11 +202,10 @@ impl<'a> visit::Visitor<()> for PrivilegedScopeVisitor<'a> { } } ItemImpl(_, Some(ref trait_ref), _, _) => { + let tcx = self.cc.crate_context.tcx; // `for_ty` is `Type` in `impl Trait for Type` - let for_ty = - ty::node_id_to_type(self.cc.crate_context.tcx, - item.id); - if !type_is_defined_in_local_crate(for_ty) { + let for_ty = ty::node_id_to_type(tcx, item.id); + if !type_is_defined_in_local_crate(tcx, for_ty) { // This implementation is not in scope of its base // type. This still might be OK if the trait is // defined in the same crate. diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 34ed7933c397f..a37f9a516fdb9 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -133,14 +133,16 @@ extern crate core; #[cfg(test)] pub use ops = realstd::ops; #[cfg(test)] pub use cmp = realstd::cmp; #[cfg(test)] pub use ty = realstd::ty; -#[cfg(test)] pub use owned = realstd::owned; +#[cfg(not(stage0), test)] pub use owned = realstd::owned; #[cfg(not(test))] pub use cmp = core::cmp; #[cfg(not(test))] pub use kinds = core::kinds; #[cfg(not(test))] pub use ops = core::ops; -#[cfg(not(test))] pub use owned = core::owned; #[cfg(not(test))] pub use ty = core::ty; +#[cfg(stage0, test)] pub use owned = realstd::owned; +#[cfg(stage0, not(test))] pub use owned = core::owned; + pub use core::any; pub use core::bool; pub use core::cell; @@ -207,6 +209,8 @@ pub mod ascii; pub mod rc; pub mod gc; +#[cfg(not(stage0), not(test))] +pub mod owned; /* Common traits */ diff --git a/src/libstd/owned.rs b/src/libstd/owned.rs new file mode 100644 index 0000000000000..3af12c5154c29 --- /dev/null +++ b/src/libstd/owned.rs @@ -0,0 +1,101 @@ +// 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. + +//! Operations on unique pointer types + +use any::{Any, AnyRefExt}; +use clone::Clone; +use cmp::{Eq, Ord, TotalEq, TotalOrd, Ordering}; +use default::Default; +use intrinsics; +use mem; +use raw::TraitObject; +use result::{Ok, Err, Result}; + +/// A value that represents the global exchange heap. This is the default +/// place that the `box` keyword allocates into when no place is supplied. +/// +/// The following two examples are equivalent: +/// +/// let foo = box(HEAP) Bar::new(...); +/// let foo = box Bar::new(...); +#[lang="exchange_heap"] +pub static HEAP: () = (); + +/// A type that represents a uniquely-owned value. +#[lang="owned_box"] +pub struct Box(*T); + +impl Default for Box { + fn default() -> Box { box Default::default() } +} + +impl Clone for Box { + /// Return a copy of the owned box. + #[inline] + fn clone(&self) -> Box { box {(**self).clone()} } + + /// Perform copy-assignment from `source` by reusing the existing allocation. + #[inline] + fn clone_from(&mut self, source: &Box) { + (**self).clone_from(&(**source)); + } +} + +// box pointers +impl Eq for Box { + #[inline] + fn eq(&self, other: &Box) -> bool { *(*self) == *(*other) } + #[inline] + fn ne(&self, other: &Box) -> bool { *(*self) != *(*other) } +} +impl Ord for Box { + #[inline] + fn lt(&self, other: &Box) -> bool { *(*self) < *(*other) } + #[inline] + fn le(&self, other: &Box) -> bool { *(*self) <= *(*other) } + #[inline] + fn ge(&self, other: &Box) -> bool { *(*self) >= *(*other) } + #[inline] + fn gt(&self, other: &Box) -> bool { *(*self) > *(*other) } +} +impl TotalOrd for Box { + #[inline] + fn cmp(&self, other: &Box) -> Ordering { (**self).cmp(*other) } +} +impl TotalEq for Box {} + +/// Extension methods for an owning `Any` trait object +pub trait AnyOwnExt { + /// Returns the boxed value if it is of type `T`, or + /// `Err(Self)` if it isn't. + fn move(self) -> Result, Self>; +} + +impl AnyOwnExt for Box { + #[inline] + fn move(self) -> Result, Box> { + if self.is::() { + unsafe { + // Get the raw representation of the trait object + let to: TraitObject = + *mem::transmute::<&Box, &TraitObject>(&self); + + // Prevent destructor on self being run + intrinsics::forget(self); + + // Extract the data pointer + Ok(mem::transmute(to.data)) + } + } else { + Err(self) + } + } +} diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index cd0445056b290..31a2014530607 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -13,7 +13,6 @@ //! local storage, and logging. Even a 'freestanding' Rust would likely want //! to implement this. -use any::AnyOwnExt; use cleanup; use clone::Clone; use comm::Sender; @@ -24,7 +23,7 @@ use local_data; use mem; use ops::Drop; use option::{Option, Some, None}; -use owned::Box; +use owned::{AnyOwnExt, Box}; use prelude::drop; use result::{Result, Ok, Err}; use rt::Runtime; diff --git a/src/libstd/task.rs b/src/libstd/task.rs index 2f7b31ae31d98..7fb61c29112de 100644 --- a/src/libstd/task.rs +++ b/src/libstd/task.rs @@ -47,10 +47,11 @@ use rt::local::Local; use rt::task::Task; use str::{Str, SendStr, IntoMaybeOwned}; -#[cfg(test)] use any::{AnyOwnExt, AnyRefExt}; +#[cfg(test)] use any::AnyRefExt; +#[cfg(test)] use owned::AnyOwnExt; +#[cfg(test)] use realstd::result::ResultUnwrap; #[cfg(test)] use result; #[cfg(test)] use str::StrAllocating; -#[cfg(test)] use realstd::result::ResultUnwrap; /// Indicates the manner in which a task exited. /// diff --git a/src/test/run-pass/unit-like-struct-drop-run.rs b/src/test/run-pass/unit-like-struct-drop-run.rs index 29dbe35752aa1..ae4623c6e66eb 100644 --- a/src/test/run-pass/unit-like-struct-drop-run.rs +++ b/src/test/run-pass/unit-like-struct-drop-run.rs @@ -10,7 +10,7 @@ // Make sure the destructor is run for unit-like structs. -use std::any::AnyOwnExt; +use std::owned::AnyOwnExt; use std::task; struct Foo; From 24ece07cecf3df59ce7b9ed3b317e3ffd718f756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Sun, 4 May 2014 10:39:11 +0200 Subject: [PATCH 02/33] Allow blocks in const expressions Only blocks with tail expressions that are const expressions and items are allowed. --- src/librustc/middle/check_const.rs | 27 ++++++++ src/librustc/middle/const_eval.rs | 14 +++- src/librustc/middle/trans/base.rs | 3 + src/librustc/middle/trans/consts.rs | 6 ++ src/librustc/middle/typeck/check/mod.rs | 6 ++ src/test/auxiliary/cci_const_block.rs | 16 +++++ .../const-block-non-item-statement.rs | 28 ++++++++ .../run-pass/const-block-cross-crate-fn.rs | 17 +++++ .../const-block-item-macro-codegen.rs | 49 +++++++++++++ src/test/run-pass/const-block-item.rs | 56 +++++++++++++++ src/test/run-pass/const-block.rs | 69 +++++++++++++++++++ 11 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 src/test/auxiliary/cci_const_block.rs create mode 100644 src/test/compile-fail/const-block-non-item-statement.rs create mode 100644 src/test/run-pass/const-block-cross-crate-fn.rs create mode 100644 src/test/run-pass/const-block-item-macro-codegen.rs create mode 100644 src/test/run-pass/const-block-item.rs create mode 100644 src/test/run-pass/const-block.rs diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index f5f551e36f7ba..7da56655378ed 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -148,6 +148,33 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &Expr, is_const: bool) { } } } + ExprBlock(ref block) => { + // Check all statements in the block + for stmt in block.stmts.iter() { + let block_span_err = |span| + v.tcx.sess.span_err(span, + "blocks in constants are limited to \ + items and tail expressions"); + match stmt.node { + StmtDecl(ref span, _) => { + match span.node { + DeclLocal(_) => block_span_err(span.span), + + // Item statements are allowed + DeclItem(_) => {} + } + } + StmtExpr(ref expr, _) => block_span_err(expr.span), + StmtSemi(ref semi, _) => block_span_err(semi.span), + StmtMac(..) => v.tcx.sess.span_bug(e.span, + "unexpanded statement macro in const?!") + } + } + match block.expr { + Some(ref expr) => check_expr(v, &**expr, true), + None => {} + } + } ExprVstore(_, ExprVstoreMutSlice) | ExprVstore(_, ExprVstoreSlice) | ExprVec(_) | diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 34f36363b92bf..aa0b573eba8c8 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -47,7 +47,6 @@ use std::rc::Rc; // fixed-size vectors and strings: [] and ""/_ // vector and string slices: &[] and &"" // tuples: (,) -// records: {...} // enums: foo(...) // floating point literals and operators // & and * pointers @@ -241,6 +240,13 @@ impl<'a> ConstEvalVisitor<'a> { ast::ExprRepeat(..) => general_const, + ast::ExprBlock(ref block) => { + match block.expr { + Some(ref e) => self.classify(&**e), + None => integral_const + } + } + _ => non_const }; self.ccache.insert(did, cn); @@ -479,6 +485,12 @@ pub fn eval_const_expr_partial(tcx: &T, e: &Expr) // If we have a vstore, just keep going; it has to be a string ExprVstore(e, _) => eval_const_expr_partial(tcx, e), ExprParen(e) => eval_const_expr_partial(tcx, e), + ExprBlock(ref block) => { + match block.expr { + Some(ref expr) => eval_const_expr_partial(tcx, &**expr), + None => Ok(const_int(0i64)) + } + } _ => Err("unsupported constant expr".to_strbuf()) } } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 10f35255abb86..3cfabf7f96b7f 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1611,6 +1611,9 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) { } } ast::ItemStatic(_, m, expr) => { + // Recurse on the expression to catch items in blocks + let mut v = TransItemVisitor{ ccx: ccx }; + v.visit_expr(expr, ()); consts::trans_const(ccx, m, item.id); // Do static_assert checking. It can't really be done much earlier // because we need to get the value of the bool out of LLVM diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 548746362cf23..b5ab0a391f3b1 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -672,6 +672,12 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, } } ast::ExprParen(e) => { const_expr(cx, e, is_local) } + ast::ExprBlock(ref block) => { + match block.expr { + Some(ref expr) => const_expr(cx, &**expr, is_local), + None => (C_nil(cx), true) + } + } _ => cx.sess().span_bug(e.span, "bad constant expression type in consts::const_expr") }; diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 6be96a408b86f..68f4fd95626dc 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -3554,6 +3554,12 @@ pub fn check_const_with_ty(fcx: &FnCtxt, _: Span, e: &ast::Expr, declty: ty::t) { + // Gather locals in statics (because of block expressions). + // This is technically uneccessary because locals in static items are forbidden, + // but prevents type checking from blowing up before const checking can properly + // emit a error. + GatherLocalsVisitor { fcx: fcx }.visit_expr(e, ()); + check_expr(fcx, e); let cty = fcx.expr_ty(e); demand::suptype(fcx, e.span, declty, cty); diff --git a/src/test/auxiliary/cci_const_block.rs b/src/test/auxiliary/cci_const_block.rs new file mode 100644 index 0000000000000..a3bcbd201e199 --- /dev/null +++ b/src/test/auxiliary/cci_const_block.rs @@ -0,0 +1,16 @@ +// Copyright 2014 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. + +pub static BLOCK_FN_DEF: fn(uint) -> uint = { + fn foo(a: uint) -> uint { + a + 10 + } + foo +}; diff --git a/src/test/compile-fail/const-block-non-item-statement.rs b/src/test/compile-fail/const-block-non-item-statement.rs new file mode 100644 index 0000000000000..ace917c704acb --- /dev/null +++ b/src/test/compile-fail/const-block-non-item-statement.rs @@ -0,0 +1,28 @@ +// Copyright 2014 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. + +#![feature(macro_rules)] + +static A: uint = { 1; 2 }; +//~^ ERROR: blocks in constants are limited to items and tail expressions + +static B: uint = { { } 2 }; +//~^ ERROR: blocks in constants are limited to items and tail expressions + +macro_rules! foo { + () => (()) //~ ERROR: blocks in constants are limited to items and tail expressions +} +static C: uint = { foo!() 2 }; + +static D: uint = { let x = 4; 2 }; +//~^ ERROR: blocks in constants are limited to items and tail expressions + +pub fn main() { +} diff --git a/src/test/run-pass/const-block-cross-crate-fn.rs b/src/test/run-pass/const-block-cross-crate-fn.rs new file mode 100644 index 0000000000000..16360ff08d059 --- /dev/null +++ b/src/test/run-pass/const-block-cross-crate-fn.rs @@ -0,0 +1,17 @@ +// Copyright 2014 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. + +// aux-build:cci_const_block.rs + +extern crate cci_const_block; + +pub fn main() { + assert_eq!(cci_const_block::BLOCK_FN_DEF(390), 400); +} diff --git a/src/test/run-pass/const-block-item-macro-codegen.rs b/src/test/run-pass/const-block-item-macro-codegen.rs new file mode 100644 index 0000000000000..09f26b15734ff --- /dev/null +++ b/src/test/run-pass/const-block-item-macro-codegen.rs @@ -0,0 +1,49 @@ +// Copyright 2014 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. + +// General test that function items in static blocks +// can be generated with a macro. + +#![feature(macro_rules)] + +struct MyType { + desc: &'static str, + data: uint, + code: fn(uint, uint) -> uint +} + +impl MyType { + fn eval(&self, a: uint) -> uint { + (self.code)(self.data, a) + } +} + +macro_rules! codegen { + ($e:expr, $v:expr) => { + { + fn generated(a: uint, b: uint) -> uint { + a - ($e * b) + } + MyType { + desc: "test", + data: $v, + code: generated + } + } + } +} + +static GENERATED_CODE_1: MyType = codegen!(2, 100); +static GENERATED_CODE_2: MyType = codegen!(5, 1000); + +pub fn main() { + assert_eq!(GENERATED_CODE_1.eval(10), 80); + assert_eq!(GENERATED_CODE_2.eval(100), 500); +} diff --git a/src/test/run-pass/const-block-item.rs b/src/test/run-pass/const-block-item.rs new file mode 100644 index 0000000000000..3365f09cd80ab --- /dev/null +++ b/src/test/run-pass/const-block-item.rs @@ -0,0 +1,56 @@ +// Copyright 2014 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. + +#![feature(macro_rules)] + +mod foo { + pub trait Value { + fn value(&self) -> uint; + } +} + +static BLOCK_USE: uint = { + use foo::Value; + 100 +}; + +static BLOCK_PUB_USE: uint = { + pub use foo::Value; + 200 +}; + +static BLOCK_STRUCT_DEF: uint = { + struct Foo { + a: uint + } + Foo{ a: 300 }.a +}; + +static BLOCK_FN_DEF: fn(uint) -> uint = { + fn foo(a: uint) -> uint { + a + 10 + } + foo +}; + +static BLOCK_MACRO_RULES: uint = { + macro_rules! baz { + () => (412) + } + baz!() +}; + +pub fn main() { + assert_eq!(BLOCK_USE, 100); + assert_eq!(BLOCK_PUB_USE, 200); + assert_eq!(BLOCK_STRUCT_DEF, 300); + assert_eq!(BLOCK_FN_DEF(390), 400); + assert_eq!(BLOCK_MACRO_RULES, 412); +} diff --git a/src/test/run-pass/const-block.rs b/src/test/run-pass/const-block.rs new file mode 100644 index 0000000000000..feac6e68e48ad --- /dev/null +++ b/src/test/run-pass/const-block.rs @@ -0,0 +1,69 @@ +// Copyright 2014 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)] +#![allow(unused_unsafe)] + +struct Foo { + a: uint, + b: *() +} + +fn foo(a: T) -> T { + a +} + +static BLOCK_INTEGRAL: uint = { 1 }; +static BLOCK_EXPLICIT_UNIT: () = { () }; +static BLOCK_IMPLICIT_UNIT: () = { }; +static BLOCK_FLOAT: f64 = { 1.0 }; +static BLOCK_ENUM: Option = { Some(100) }; +static BLOCK_STRUCT: Foo = { Foo { a: 12, b: 0 as *() } }; +static BLOCK_UNSAFE: uint = unsafe { 1000 }; + +// FIXME: #13970 +// static BLOCK_FN_INFERRED: fn(uint) -> uint = { foo }; + +// FIXME: #13971 +// static BLOCK_FN: fn(uint) -> uint = { foo:: }; + +// FIXME: #13972 +// static BLOCK_ENUM_CONSTRUCTOR: fn(uint) -> Option = { Some }; + +// FIXME: #13973 +// static BLOCK_UNSAFE_SAFE_PTR: &'static int = unsafe { &*(0xdeadbeef as *int) }; +// static BLOCK_UNSAFE_SAFE_PTR_2: &'static int = unsafe { +// static X: *int = 0xdeadbeef as *int; +// &*X +// }; + +pub fn main() { + assert_eq!(BLOCK_INTEGRAL, 1); + assert_eq!(BLOCK_EXPLICIT_UNIT, ()); + assert_eq!(BLOCK_IMPLICIT_UNIT, ()); + assert_eq!(BLOCK_FLOAT, 1.0_f64); + assert_eq!(BLOCK_STRUCT.a, 12); + assert_eq!(BLOCK_STRUCT.b, 0 as *()); + assert_eq!(BLOCK_ENUM, Some(100)); + assert_eq!(BLOCK_UNSAFE, 1000); + + // FIXME: #13970 + // assert_eq!(BLOCK_FN_INFERRED(300), 300); + + // FIXME: #13971 + // assert_eq!(BLOCK_FN(300), 300); + + // FIXME: #13972 + // assert_eq!(BLOCK_ENUM_CONSTRUCTOR(200), Some(200)); + + // FIXME: #13973 + // assert_eq!(BLOCK_UNSAFE_SAFE_PTR as *int as uint, 0xdeadbeef_u); + // assert_eq!(BLOCK_UNSAFE_SAFE_PTR_2 as *int as uint, 0xdeadbeef_u); +} From de2c48c30a24eb1de07355f14d2353385f9db2b2 Mon Sep 17 00:00:00 2001 From: OGINO Masanori Date: Tue, 13 May 2014 23:12:55 +0900 Subject: [PATCH 03/33] Add tests for from_bits. Signed-off-by: OGINO Masanori --- src/libstd/bitflags.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libstd/bitflags.rs b/src/libstd/bitflags.rs index 5737bc772df02..f834a158588cd 100644 --- a/src/libstd/bitflags.rs +++ b/src/libstd/bitflags.rs @@ -207,6 +207,13 @@ mod tests { assert_eq!(FlagABC.bits(), 0x00000111); } + #[test] + fn test_from_bits() { + assert!(unsafe { Flags::from_bits(0x00000000) } == Flags::empty()); + assert!(unsafe { Flags::from_bits(0x00000001) } == FlagA); + assert!(unsafe { Flags::from_bits(0x00000111) } == FlagABC); + } + #[test] fn test_is_empty(){ assert!(Flags::empty().is_empty()); From a390b5dd0320957b00177c8ae837c51ceb737fef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20Ochagav=C3=ADa?= Date: Tue, 13 May 2014 15:43:10 +0200 Subject: [PATCH 04/33] Replaced ~T by Box in manual --- src/doc/rust.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/doc/rust.md b/src/doc/rust.md index 870b9dcb70bb3..f242a89784ce9 100644 --- a/src/doc/rust.md +++ b/src/doc/rust.md @@ -2648,9 +2648,9 @@ before the expression they apply to. : Logical negation. On the boolean type, this flips between `true` and `false`. On integer types, this inverts the individual bits in the two's complement representation of the value. -* `~` +* `box` : [Boxing](#pointer-types) operators. Allocate a box to hold the value they are applied to, - and store the value in it. `~` creates an owned box. + and store the value in it. `box` creates an owned box. * `&` : Borrow operator. Returns a reference, pointing to its operand. The operand of a borrow is statically proven to outlive the resulting pointer. @@ -3156,7 +3156,7 @@ fn main() { ~~~~ Patterns can also dereference pointers by using the `&`, -`~` or `@` symbols, as appropriate. For example, these two matches +`box` or `@` symbols, as appropriate. For example, these two matches on `x: &int` are equivalent: ~~~~ @@ -3438,11 +3438,11 @@ All pointers in Rust are explicit first-class values. They can be copied, stored into data structures, and returned from functions. There are four varieties of pointer in Rust: -* Owning pointers (`~`) +* Owning pointers (`Box`) : These point to owned heap allocations (or "boxes") in the shared, inter-task heap. Each owned box has a single owning pointer; pointer and pointee retain a 1:1 relationship at all times. - Owning pointers are written `~content`, - for example `~int` means an owning pointer to an owned box containing an integer. + Owning pointers are written `Box`, + for example `Box` means an owning pointer to an owned box containing an integer. Copying an owned box is a "deep" operation: it involves allocating a new owned box and copying the contents of the old box into the new box. Releasing an owning pointer immediately releases its corresponding owned box. @@ -3562,8 +3562,8 @@ Whereas most calls to trait methods are "early bound" (statically resolved) to s a call to a method on an object type is only resolved to a vtable entry at compile time. The actual implementation for each vtable entry can vary on an object-by-object basis. -Given a pointer-typed expression `E` of type `&T` or `~T`, where `T` implements trait `R`, -casting `E` to the corresponding pointer type `&R` or `~R` results in a value of the _object type_ `R`. +Given a pointer-typed expression `E` of type `&T` or `Box`, where `T` implements trait `R`, +casting `E` to the corresponding pointer type `&R` or `Box` results in a value of the _object type_ `R`. This result is represented as a pair of pointers: the vtable pointer for the `T` implementation of `R`, and the pointer value of `E`. @@ -3761,7 +3761,7 @@ Local variables are immutable unless declared otherwise like: `let mut x = ...`. Function parameters are immutable unless declared with `mut`. The `mut` keyword applies only to the following parameter (so `|mut x, y|` -and `fn f(mut x: ~int, y: ~int)` declare one mutable variable `x` and +and `fn f(mut x: Box, y: Box)` declare one mutable variable `x` and one immutable variable `y`). Methods that take either `self` or `~self` can optionally place them in a From 655487b59666afe5a5c9e0a305c27be342b8fa46 Mon Sep 17 00:00:00 2001 From: Piotr Jawniak Date: Tue, 13 May 2014 11:00:11 +0200 Subject: [PATCH 05/33] Implements Default trait for BigInt and BigUint --- src/libnum/bigint.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libnum/bigint.rs b/src/libnum/bigint.rs index ef6f1aafe8864..ac8da664de751 100644 --- a/src/libnum/bigint.rs +++ b/src/libnum/bigint.rs @@ -19,6 +19,7 @@ A `BigInt` is a combination of `BigUint` and `Sign`. use Integer; use std::cmp; +use std::default::Default; use std::fmt; use std::from_str::FromStr; use std::num::CheckedDiv; @@ -112,6 +113,11 @@ impl TotalOrd for BigUint { } } +impl Default for BigUint { + #[inline] + fn default() -> BigUint { BigUint::new(Vec::new()) } +} + impl fmt::Show for BigUint { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f.buf, "{}", self.to_str_radix(10)) @@ -830,6 +836,11 @@ impl TotalOrd for BigInt { } } +impl Default for BigInt { + #[inline] + fn default() -> BigInt { BigInt::new(Zero, Vec::new()) } +} + impl fmt::Show for BigInt { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f.buf, "{}", self.to_str_radix(10)) From 5bf268d0b028d8e8abe62166c533f8515955bc6b Mon Sep 17 00:00:00 2001 From: Edward Wang Date: Tue, 13 May 2014 13:20:52 +0800 Subject: [PATCH 06/33] Fix #8391 Closes #8391 --- src/librustc/middle/check_match.rs | 10 +++++++++- src/test/run-pass/issue-8391.rs | 5 +++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index ecadc6138715f..c08078ff86b5d 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -239,7 +239,15 @@ fn is_useful(cx: &MatchCheckCtxt, m: &matrix, v: &[@Pat]) -> useful { return not_useful } let real_pat = match m.iter().find(|r| r.get(0).id != 0) { - Some(r) => *r.get(0), None => v[0] + Some(r) => { + match r.get(0).node { + // An arm of the form `ref x @ sub_pat` has type + // `sub_pat`, not `&sub_pat` as `x` itself does. + PatIdent(BindByRef(_), _, Some(sub)) => sub, + _ => *r.get(0) + } + } + None => v[0] }; let left_ty = if real_pat.id == 0 { ty::mk_nil() } else { ty::node_id_to_type(cx.tcx, real_pat.id) }; diff --git a/src/test/run-pass/issue-8391.rs b/src/test/run-pass/issue-8391.rs index fdd27f8542fc7..86c9b8c696469 100644 --- a/src/test/run-pass/issue-8391.rs +++ b/src/test/run-pass/issue-8391.rs @@ -9,8 +9,9 @@ // except according to those terms. fn main() { - let _x = match Some(1) { - _y @ Some(_) => 1, + let x = match Some(1) { + ref _y @ Some(_) => 1, None => 2, }; + assert_eq!(x, 1); } From 21867fa1279c38189faccfb430c8bd6bffe0ef9e Mon Sep 17 00:00:00 2001 From: Edward Wang Date: Tue, 13 May 2014 14:30:21 +0800 Subject: [PATCH 07/33] check_match: get rid of superfluous clones --- src/librustc/middle/check_match.rs | 55 ++++++++++++------------------ 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index c08078ff86b5d..b48fddac1a451 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -266,7 +266,7 @@ fn is_useful(cx: &MatchCheckCtxt, m: &matrix, v: &[@Pat]) -> useful { val(const_bool(false)), 0u, left_ty) } - ref u => (*u).clone(), + u => u, } } ty::ty_enum(eid, _) => { @@ -274,7 +274,7 @@ fn is_useful(cx: &MatchCheckCtxt, m: &matrix, v: &[@Pat]) -> useful { match is_useful_specialized(cx, m, v, variant(va.id), va.args.len(), left_ty) { not_useful => (), - ref u => return (*u).clone(), + u => return u, } } not_useful @@ -296,7 +296,7 @@ fn is_useful(cx: &MatchCheckCtxt, m: &matrix, v: &[@Pat]) -> useful { for n in iter::range(0u, max_len + 1) { match is_useful_specialized(cx, m, v, vec(n), n, left_ty) { not_useful => (), - ref u => return (*u).clone(), + u => return u, } } not_useful @@ -312,21 +312,21 @@ fn is_useful(cx: &MatchCheckCtxt, m: &matrix, v: &[@Pat]) -> useful { } } } - Some(ref ctor) => { + Some(ctor) => { match is_useful(cx, &m.iter().filter_map(|r| { default(cx, r.as_slice()) }).collect::(), v.tail()) { - useful_ => useful(left_ty, (*ctor).clone()), - ref u => (*u).clone(), + useful_ => useful(left_ty, ctor), + u => u, } } } } - Some(ref v0_ctor) => { - let arity = ctor_arity(cx, v0_ctor, left_ty); - is_useful_specialized(cx, m, v, (*v0_ctor).clone(), arity, left_ty) + Some(v0_ctor) => { + let arity = ctor_arity(cx, &v0_ctor, left_ty); + is_useful_specialized(cx, m, v, v0_ctor, arity, left_ty) } } } @@ -345,7 +345,7 @@ fn is_useful_specialized(cx: &MatchCheckCtxt, cx, &ms, specialize(cx, v, &ctor, arity, lty).unwrap().as_slice()); match could_be_useful { useful_ => useful(lty, ctor), - ref u => (*u).clone(), + u => u, } } @@ -416,9 +416,9 @@ fn missing_ctor(cx: &MatchCheckCtxt, let mut found = Vec::new(); for r in m.iter() { let r = pat_ctor_id(cx, *r.get(0)); - for id in r.iter() { - if !found.contains(id) { - found.push((*id).clone()); + for id in r.move_iter() { + if !found.contains(&id) { + found.push(id); } } } @@ -820,30 +820,17 @@ fn specialize(cx: &MatchCheckCtxt, let num_elements = before.len() + after.len(); if num_elements < arity && slice.is_some() { let mut result = Vec::new(); - for pat in before.iter() { - result.push((*pat).clone()); - } - for _ in iter::range(0, arity - num_elements) { - result.push(wild()) - } - for pat in after.iter() { - result.push((*pat).clone()); - } - for pat in r.tail().iter() { - result.push((*pat).clone()); - } + let wilds = Vec::from_elem(arity - num_elements, wild()); + result.push_all_move(before); + result.push_all_move(wilds); + result.push_all_move(after); + result.push_all(r.tail()); Some(result) } else if num_elements == arity { let mut result = Vec::new(); - for pat in before.iter() { - result.push((*pat).clone()); - } - for pat in after.iter() { - result.push((*pat).clone()); - } - for pat in r.tail().iter() { - result.push((*pat).clone()); - } + result.push_all_move(before); + result.push_all_move(after); + result.push_all(r.tail()); Some(result) } else { None From 74ad0236747469f9646916d1916dee2598076161 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Mon, 12 May 2014 19:56:41 +0200 Subject: [PATCH 08/33] std, core: Generate unicode.rs using unicode.py --- src/etc/unicode.py | 131 ++++++++++++++++++++++++----------------- src/libcore/unicode.rs | 11 ++-- src/libstd/unicode.rs | 4 +- 3 files changed, 85 insertions(+), 61 deletions(-) diff --git a/src/etc/unicode.py b/src/etc/unicode.py index d5c74e367340e..e98c65ca50eee 100755 --- a/src/etc/unicode.py +++ b/src/etc/unicode.py @@ -169,7 +169,7 @@ def emit_bsearch_range_table(f): else if hi < c { Less } else { Greater } }) != None -}\n\n +}\n """); def emit_property_module(f, mod, tbl): @@ -193,11 +193,11 @@ def emit_property_module(f, mod, tbl): f.write(" pub fn %s(c: char) -> bool {\n" % cat) f.write(" super::bsearch_range_table(c, %s_table)\n" % cat) f.write(" }\n\n") - f.write("}\n") + f.write("}\n\n") def emit_conversions_module(f, lowerupper, upperlower): - f.write("pub mod conversions {\n") + f.write("pub mod conversions {") f.write(""" use cmp::{Equal, Less, Greater}; use slice::ImmutableVector; @@ -225,13 +225,14 @@ def emit_conversions_module(f, lowerupper, upperlower): else { Greater } }) } + """); emit_caseconversion_table(f, "LuLl", upperlower) emit_caseconversion_table(f, "LlLu", lowerupper) f.write("}\n") def emit_caseconversion_table(f, name, table): - f.write(" static %s_table : &'static [(char, char)] = &[\n" % name) + f.write(" static %s_table : &'static [(char, char)] = &[\n" % name) sorted_table = sorted(table.iteritems(), key=operator.itemgetter(0)) ix = 0 for key, value in sorted_table: @@ -255,7 +256,7 @@ def format_table_content(f, content, indent): line = " "*indent + chunk f.write(line) -def emit_decomp_module(f, canon, compat, combine): +def emit_core_decomp_module(f, canon, compat): canon_keys = canon.keys() canon_keys.sort() @@ -279,23 +280,6 @@ def emit_decomp_module(f, canon, compat, combine): } None => None } - }\n -""") - - f.write(""" - fn bsearch_range_value_table(c: char, r: &'static [(char, char, u8)]) -> u8 { - use cmp::{Equal, Less, Greater}; - match r.bsearch(|&(lo, hi, _)| { - if lo <= c && c <= hi { Equal } - else if hi < c { Less } - else { Greater } - }) { - Some(idx) => { - let (_, _, result) = r[idx]; - result - } - None => 0 - } }\n\n """) @@ -337,21 +321,10 @@ def emit_decomp_module(f, canon, compat, combine): format_table_content(f, data, 8) f.write("\n ];\n\n") - f.write(" static combining_class_table : &'static [(char, char, u8)] = &[\n") - ix = 0 - for pair in combine: - f.write(ch_prefix(ix)) - f.write("(%s, %s, %s)" % (escape_char(pair[0]), escape_char(pair[1]), pair[2])) - ix += 1 - f.write("\n ];\n") - f.write(" pub fn canonical(c: char, i: |char|) " + "{ d(c, i, false); }\n\n") f.write(" pub fn compatibility(c: char, i: |char|) " +"{ d(c, i, true); }\n\n") - f.write(" pub fn canonical_combining_class(c: char) -> u8 {\n" - + " bsearch_range_value_table(c, combining_class_table)\n" - + " }\n\n") f.write(" fn d(c: char, i: |char|, k: bool) {\n") f.write(" use iter::Iterator;\n"); @@ -389,17 +362,43 @@ def emit_decomp_module(f, canon, compat, combine): f.write(" }\n") f.write("}\n\n") -r = "unicode.rs" -for i in [r]: - if os.path.exists(i): - os.remove(i); -rf = open(r, "w") +def emit_std_decomp_module(f, combine): + f.write("pub mod decompose {\n"); + f.write(" use option::{Some, None};\n"); + f.write(" use slice::ImmutableVector;\n"); -(canon_decomp, compat_decomp, gencats, - combines, lowerupper, upperlower) = load_unicode_data("UnicodeData.txt") + f.write(""" + fn bsearch_range_value_table(c: char, r: &'static [(char, char, u8)]) -> u8 { + use cmp::{Equal, Less, Greater}; + match r.bsearch(|&(lo, hi, _)| { + if lo <= c && c <= hi { Equal } + else if hi < c { Less } + else { Greater } + }) { + Some(idx) => { + let (_, _, result) = r[idx]; + result + } + None => 0 + } + }\n\n +""") + + f.write(" static combining_class_table : &'static [(char, char, u8)] = &[\n") + ix = 0 + for pair in combine: + f.write(ch_prefix(ix)) + f.write("(%s, %s, %s)" % (escape_char(pair[0]), escape_char(pair[1]), pair[2])) + ix += 1 + f.write("\n ];\n\n") -# Preamble -rf.write('''// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT + f.write(" pub fn canonical_combining_class(c: char) -> u8 {\n" + + " bsearch_range_value_table(c, combining_class_table)\n" + + " }\n") + f.write("}\n") + + +preamble = '''// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -409,23 +408,45 @@ def emit_decomp_module(f, canon, compat, combine): // option. This file may not be copied, modified, or distributed // except according to those terms. -// The following code was generated by "src/etc/unicode.py" +// NOTE: The following code was generated by "src/etc/unicode.py", do not edit directly + +#![allow(missing_doc, non_uppercase_statics)] + +''' + +(canon_decomp, compat_decomp, gencats, + combines, lowerupper, upperlower) = load_unicode_data("UnicodeData.txt") + +def gen_core_unicode(): + r = "core_unicode.rs" + if os.path.exists(r): + os.remove(r); + with open(r, "w") as rf: + # Preamble + rf.write(preamble) -#![allow(missing_doc)] -#![allow(non_uppercase_statics)] + emit_bsearch_range_table(rf); + emit_property_module(rf, "general_category", gencats) -''') + emit_core_decomp_module(rf, canon_decomp, compat_decomp) -emit_bsearch_range_table(rf); -emit_property_module(rf, "general_category", gencats) + derived = load_properties("DerivedCoreProperties.txt", + ["XID_Start", "XID_Continue", "Alphabetic", "Lowercase", "Uppercase"]) -emit_decomp_module(rf, canon_decomp, compat_decomp, combines) + emit_property_module(rf, "derived_property", derived) -derived = load_properties("DerivedCoreProperties.txt", - ["XID_Start", "XID_Continue", "Alphabetic", "Lowercase", "Uppercase"]) + props = load_properties("PropList.txt", ["White_Space"]) + emit_property_module(rf, "property", props) + emit_conversions_module(rf, lowerupper, upperlower) -emit_property_module(rf, "derived_property", derived) +def gen_std_unicode(): + r = "std_unicode.rs" + if os.path.exists(r): + os.remove(r); + with open(r, "w") as rf: + # Preamble + rf.write(preamble) + emit_std_decomp_module(rf, combines) -props = load_properties("PropList.txt", ["White_Space"]) -emit_property_module(rf, "property", props) -emit_conversions_module(rf, lowerupper, upperlower) +gen_core_unicode() +gen_std_unicode() diff --git a/src/libcore/unicode.rs b/src/libcore/unicode.rs index db016ad880741..b3298bde05547 100644 --- a/src/libcore/unicode.rs +++ b/src/libcore/unicode.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,10 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// The following code was generated by "src/etc/unicode.py" +// NOTE: The following code was generated by "src/etc/unicode.py", do not edit directly #![allow(missing_doc, non_uppercase_statics)] + fn bsearch_range_table(c: char, r: &'static [(char,char)]) -> bool { use cmp::{Equal, Less, Greater}; use slice::ImmutableVector; @@ -102,6 +103,7 @@ pub mod general_category { } } + pub mod decompose { use option::Option; use option::{Some, None}; @@ -123,7 +125,6 @@ pub mod decompose { } - // Canonical decompositions static canonical_table : &'static [(char, &'static [char])] = &[ ('\xc0', &['\x41', '\u0300']), ('\xc1', &['\x41', '\u0301']), ('\xc2', &['\x41', '\u0302']), @@ -3968,6 +3969,7 @@ pub mod derived_property { pub fn XID_Start(c: char) -> bool { super::bsearch_range_table(c, XID_Start_table) } + } pub mod property { @@ -3983,6 +3985,7 @@ pub mod property { pub fn White_Space(c: char) -> bool { super::bsearch_range_table(c, White_Space_table) } + } pub mod conversions { @@ -4501,7 +4504,7 @@ pub mod conversions { ('\U00010426', '\U0001044e'), ('\U00010427', '\U0001044f') ]; - static LlLu_table : &'static [(char, char)] = &[ + static LlLu_table : &'static [(char, char)] = &[ ('\x61', '\x41'), ('\x62', '\x42'), ('\x63', '\x43'), ('\x64', '\x44'), ('\x65', '\x45'), ('\x66', '\x46'), diff --git a/src/libstd/unicode.rs b/src/libstd/unicode.rs index be6e5d040a7c9..d534b30221b4a 100644 --- a/src/libstd/unicode.rs +++ b/src/libstd/unicode.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// The following code was generated by "src/etc/unicode.py" +// NOTE: The following code was generated by "src/etc/unicode.py", do not edit directly #![allow(missing_doc, non_uppercase_statics)] From 2f71b72a12f05d3efc6b507b12102ce52c6415dd Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Mon, 12 May 2014 21:53:53 +0200 Subject: [PATCH 09/33] core: Use appropriately sized integers for codepoints and bytes --- src/libcore/char.rs | 95 +++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 50 deletions(-) diff --git a/src/libcore/char.rs b/src/libcore/char.rs index 7f2deb81f8c90..ca5e56f0649cc 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -33,13 +33,14 @@ use unicode::{derived_property, property, general_category, decompose, conversio #[cfg(not(test))] use default::Default; // UTF-8 ranges and tags for encoding characters -static TAG_CONT: uint = 128u; -static MAX_ONE_B: uint = 128u; -static TAG_TWO_B: uint = 192u; -static MAX_TWO_B: uint = 2048u; -static TAG_THREE_B: uint = 224u; -static MAX_THREE_B: uint = 65536u; -static TAG_FOUR_B: uint = 240u; +static TAG_CONT: u8 = 0b1000_0000u8; +static TAG_TWO_B: u8 = 0b1100_0000u8; +static TAG_THREE_B: u8 = 0b1110_0000u8; +static TAG_FOUR_B: u8 = 0b1111_0000u8; +static MAX_ONE_B: u32 = 0x80u32; +static MAX_TWO_B: u32 = 0x800u32; +static MAX_THREE_B: u32 = 0x10000u32; +static MAX_FOUR_B: u32 = 0x200000u32; /* Lu Uppercase_Letter an uppercase letter @@ -285,37 +286,37 @@ pub fn from_digit(num: uint, radix: uint) -> Option { } // Constants from Unicode 6.2.0 Section 3.12 Conjoining Jamo Behavior -static S_BASE: uint = 0xAC00; -static L_BASE: uint = 0x1100; -static V_BASE: uint = 0x1161; -static T_BASE: uint = 0x11A7; -static L_COUNT: uint = 19; -static V_COUNT: uint = 21; -static T_COUNT: uint = 28; -static N_COUNT: uint = (V_COUNT * T_COUNT); -static S_COUNT: uint = (L_COUNT * N_COUNT); +static S_BASE: u32 = 0xAC00; +static L_BASE: u32 = 0x1100; +static V_BASE: u32 = 0x1161; +static T_BASE: u32 = 0x11A7; +static L_COUNT: u32 = 19; +static V_COUNT: u32 = 21; +static T_COUNT: u32 = 28; +static N_COUNT: u32 = (V_COUNT * T_COUNT); +static S_COUNT: u32 = (L_COUNT * N_COUNT); // Decompose a precomposed Hangul syllable fn decompose_hangul(s: char, f: |char|) { - let si = s as uint - S_BASE; + let si = s as u32 - S_BASE; let li = si / N_COUNT; unsafe { - f(transmute((L_BASE + li) as u32)); + f(transmute(L_BASE + li)); let vi = (si % N_COUNT) / T_COUNT; - f(transmute((V_BASE + vi) as u32)); + f(transmute(V_BASE + vi)); let ti = si % T_COUNT; if ti > 0 { - f(transmute((T_BASE + ti) as u32)); + f(transmute(T_BASE + ti)); } } } /// Returns the canonical decomposition of a character pub fn decompose_canonical(c: char, f: |char|) { - if (c as uint) < S_BASE || (c as uint) >= (S_BASE + S_COUNT) { + if (c as u32) < S_BASE || (c as u32) >= (S_BASE + S_COUNT) { decompose::canonical(c, f); } else { decompose_hangul(c, f); @@ -324,7 +325,7 @@ pub fn decompose_canonical(c: char, f: |char|) { /// Returns the compatibility decomposition of a character pub fn decompose_compatible(c: char, f: |char|) { - if (c as uint) < S_BASE || (c as uint) >= (S_BASE + S_COUNT) { + if (c as u32) < S_BASE || (c as u32) >= (S_BASE + S_COUNT) { decompose::compatibility(c, f); } else { decompose_hangul(c, f); @@ -386,12 +387,7 @@ pub fn escape_default(c: char, f: |char|) { /// Returns the amount of bytes this `char` would need if encoded in UTF-8 pub fn len_utf8_bytes(c: char) -> uint { - static MAX_ONE_B: uint = 128u; - static MAX_TWO_B: uint = 2048u; - static MAX_THREE_B: uint = 65536u; - static MAX_FOUR_B: uint = 2097152u; - - let code = c as uint; + let code = c as u32; match () { _ if code < MAX_ONE_B => 1u, _ if code < MAX_TWO_B => 2u, @@ -606,41 +602,40 @@ impl Char for char { fn len_utf8_bytes(&self) -> uint { len_utf8_bytes(*self) } - fn encode_utf8(&self, dst: &mut [u8]) -> uint { - let code = *self as uint; + fn encode_utf8<'a>(&self, dst: &'a mut [u8]) -> uint { + let code = *self as u32; if code < MAX_ONE_B { dst[0] = code as u8; - return 1; + 1 } else if code < MAX_TWO_B { - dst[0] = (code >> 6u & 31u | TAG_TWO_B) as u8; - dst[1] = (code & 63u | TAG_CONT) as u8; - return 2; + dst[0] = (code >> 6u & 0x1F_u32) as u8 | TAG_TWO_B; + dst[1] = (code & 0x3F_u32) as u8 | TAG_CONT; + 2 } else if code < MAX_THREE_B { - dst[0] = (code >> 12u & 15u | TAG_THREE_B) as u8; - dst[1] = (code >> 6u & 63u | TAG_CONT) as u8; - dst[2] = (code & 63u | TAG_CONT) as u8; - return 3; + dst[0] = (code >> 12u & 0x0F_u32) as u8 | TAG_THREE_B; + dst[1] = (code >> 6u & 0x3F_u32) as u8 | TAG_CONT; + dst[2] = (code & 0x3F_u32) as u8 | TAG_CONT; + 3 } else { - dst[0] = (code >> 18u & 7u | TAG_FOUR_B) as u8; - dst[1] = (code >> 12u & 63u | TAG_CONT) as u8; - dst[2] = (code >> 6u & 63u | TAG_CONT) as u8; - dst[3] = (code & 63u | TAG_CONT) as u8; - return 4; + dst[0] = (code >> 18u & 0x07_u32) as u8 | TAG_FOUR_B; + dst[1] = (code >> 12u & 0x3F_u32) as u8 | TAG_CONT; + dst[2] = (code >> 6u & 0x3F_u32) as u8 | TAG_CONT; + dst[3] = (code & 0x3F_u32) as u8 | TAG_CONT; + 4 } } fn encode_utf16(&self, dst: &mut [u16]) -> uint { - let mut ch = *self as uint; - if (ch & 0xFFFF_u) == ch { - // The BMP falls through (assuming non-surrogate, as it - // should) - assert!(ch <= 0xD7FF_u || ch >= 0xE000_u); + let mut ch = *self as u32; + if (ch & 0xFFFF_u32) == ch { + // The BMP falls through (assuming non-surrogate, as it should) + assert!(ch <= 0xD7FF_u32 || ch >= 0xE000_u32); dst[0] = ch as u16; 1 } else { // Supplementary planes break into surrogates. - assert!(ch >= 0x1_0000_u && ch <= 0x10_FFFF_u); - ch -= 0x1_0000_u; + assert!(ch >= 0x1_0000_u32 && ch <= 0x10_FFFF_u32); + ch -= 0x1_0000_u32; dst[0] = 0xD800_u16 | ((ch >> 10) as u16); dst[1] = 0xDC00_u16 | ((ch as u16) & 0x3FF_u16); 2 From 8c54d5bf406fbfdbebd1a4553f430fca02b2c117 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Mon, 12 May 2014 22:25:38 +0200 Subject: [PATCH 10/33] core: Move Hangul decomposition into unicode.rs --- src/etc/unicode.py | 77 +++++++++++++++++++++++++++++++----------- src/libcore/char.rs | 54 ++++------------------------- src/libcore/unicode.rs | 48 ++++++++++++++++++++++++-- 3 files changed, 110 insertions(+), 69 deletions(-) diff --git a/src/etc/unicode.py b/src/etc/unicode.py index e98c65ca50eee..f079ef73cd8e2 100755 --- a/src/etc/unicode.py +++ b/src/etc/unicode.py @@ -321,17 +321,24 @@ def emit_core_decomp_module(f, canon, compat): format_table_content(f, data, 8) f.write("\n ];\n\n") - f.write(" pub fn canonical(c: char, i: |char|) " - + "{ d(c, i, false); }\n\n") - f.write(" pub fn compatibility(c: char, i: |char|) " - +"{ d(c, i, true); }\n\n") - f.write(" fn d(c: char, i: |char|, k: bool) {\n") - f.write(" use iter::Iterator;\n"); + f.write(""" + pub fn decompose_canonical(c: char, i: |char|) { d(c, i, false); } - f.write(" if c <= '\\x7f' { i(c); return; }\n") + pub fn decompose_compatible(c: char, i: |char|) { d(c, i, true); } - # First check the canonical decompositions - f.write(""" + fn d(c: char, i: |char|, k: bool) { + use iter::Iterator; + + // 7-bit ASCII never decomposes + if c <= '\\x7f' { i(c); return; } + + // Perform decomposition for Hangul + if (c as u32) >= S_BASE && (c as u32) < (S_BASE + S_COUNT) { + decompose_hangul(c, i); + return; + } + + // First check the canonical decompositions match bsearch_table(c, canonical_table) { Some(canon) => { for x in canon.iter() { @@ -340,13 +347,12 @@ def emit_core_decomp_module(f, canon, compat): return; } None => () - }\n\n""") + } - # Bottom out if we're not doing compat. - f.write(" if !k { i(c); return; }\n") + // Bottom out if we're not doing compat. + if !k { i(c); return; } - # Then check the compatibility decompositions - f.write(""" + // Then check the compatibility decompositions match bsearch_table(c, compatibility_table) { Some(compat) => { for x in compat.iter() { @@ -355,12 +361,45 @@ def emit_core_decomp_module(f, canon, compat): return; } None => () - }\n\n""") + } - # Finally bottom out. - f.write(" i(c);\n") - f.write(" }\n") - f.write("}\n\n") + // Finally bottom out. + i(c); + } + + // Constants from Unicode 6.2.0 Section 3.12 Conjoining Jamo Behavior + static S_BASE: u32 = 0xAC00; + static L_BASE: u32 = 0x1100; + static V_BASE: u32 = 0x1161; + static T_BASE: u32 = 0x11A7; + static L_COUNT: u32 = 19; + static V_COUNT: u32 = 21; + static T_COUNT: u32 = 28; + static N_COUNT: u32 = (V_COUNT * T_COUNT); + static S_COUNT: u32 = (L_COUNT * N_COUNT); + + // Decompose a precomposed Hangul syllable + fn decompose_hangul(s: char, f: |char|) { + use cast::transmute; + + let si = s as u32 - S_BASE; + + let li = si / N_COUNT; + unsafe { + f(transmute(L_BASE + li)); + + let vi = (si % N_COUNT) / T_COUNT; + f(transmute(V_BASE + vi)); + + let ti = si % T_COUNT; + if ti > 0 { + f(transmute(T_BASE + ti)); + } + } + } +} + +""") def emit_std_decomp_module(f, combine): f.write("pub mod decompose {\n"); diff --git a/src/libcore/char.rs b/src/libcore/char.rs index ca5e56f0649cc..71a2d75715b5a 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -27,7 +27,12 @@ use mem::transmute; use option::{None, Option, Some}; use iter::{Iterator, range_step}; -use unicode::{derived_property, property, general_category, decompose, conversions}; +use unicode::{derived_property, property, general_category, conversions}; + +/// Returns the canonical decomposition of a character. +pub use unicode::decompose::decompose_canonical; +/// Returns the compatibility decomposition of a character. +pub use unicode::decompose::decompose_compatible; #[cfg(not(test))] use cmp::{Eq, Ord, TotalEq, TotalOrd, Ordering}; #[cfg(not(test))] use default::Default; @@ -285,53 +290,6 @@ pub fn from_digit(num: uint, radix: uint) -> Option { } } -// Constants from Unicode 6.2.0 Section 3.12 Conjoining Jamo Behavior -static S_BASE: u32 = 0xAC00; -static L_BASE: u32 = 0x1100; -static V_BASE: u32 = 0x1161; -static T_BASE: u32 = 0x11A7; -static L_COUNT: u32 = 19; -static V_COUNT: u32 = 21; -static T_COUNT: u32 = 28; -static N_COUNT: u32 = (V_COUNT * T_COUNT); -static S_COUNT: u32 = (L_COUNT * N_COUNT); - -// Decompose a precomposed Hangul syllable -fn decompose_hangul(s: char, f: |char|) { - let si = s as u32 - S_BASE; - - let li = si / N_COUNT; - unsafe { - f(transmute(L_BASE + li)); - - let vi = (si % N_COUNT) / T_COUNT; - f(transmute(V_BASE + vi)); - - let ti = si % T_COUNT; - if ti > 0 { - f(transmute(T_BASE + ti)); - } - } -} - -/// Returns the canonical decomposition of a character -pub fn decompose_canonical(c: char, f: |char|) { - if (c as u32) < S_BASE || (c as u32) >= (S_BASE + S_COUNT) { - decompose::canonical(c, f); - } else { - decompose_hangul(c, f); - } -} - -/// Returns the compatibility decomposition of a character -pub fn decompose_compatible(c: char, f: |char|) { - if (c as u32) < S_BASE || (c as u32) >= (S_BASE + S_COUNT) { - decompose::compatibility(c, f); - } else { - decompose_hangul(c, f); - } -} - /// /// Returns the hexadecimal Unicode escape of a `char` /// diff --git a/src/libcore/unicode.rs b/src/libcore/unicode.rs index b3298bde05547..bffde2323bf94 100644 --- a/src/libcore/unicode.rs +++ b/src/libcore/unicode.rs @@ -2121,14 +2121,24 @@ pub mod decompose { &['\u53ef']) ]; - pub fn canonical(c: char, i: |char|) { d(c, i, false); } - pub fn compatibility(c: char, i: |char|) { d(c, i, true); } + pub fn decompose_canonical(c: char, i: |char|) { d(c, i, false); } + + pub fn decompose_compatible(c: char, i: |char|) { d(c, i, true); } fn d(c: char, i: |char|, k: bool) { use iter::Iterator; + + // 7-bit ASCII never decomposes if c <= '\x7f' { i(c); return; } + // Perform decomposition for Hangul + if (c as u32) >= S_BASE && (c as u32) < (S_BASE + S_COUNT) { + decompose_hangul(c, i); + return; + } + + // First check the canonical decompositions match bsearch_table(c, canonical_table) { Some(canon) => { for x in canon.iter() { @@ -2139,8 +2149,10 @@ pub mod decompose { None => () } + // Bottom out if we're not doing compat. if !k { i(c); return; } + // Then check the compatibility decompositions match bsearch_table(c, compatibility_table) { Some(compat) => { for x in compat.iter() { @@ -2151,8 +2163,40 @@ pub mod decompose { None => () } + // Finally bottom out. i(c); } + + // Constants from Unicode 6.2.0 Section 3.12 Conjoining Jamo Behavior + static S_BASE: u32 = 0xAC00; + static L_BASE: u32 = 0x1100; + static V_BASE: u32 = 0x1161; + static T_BASE: u32 = 0x11A7; + static L_COUNT: u32 = 19; + static V_COUNT: u32 = 21; + static T_COUNT: u32 = 28; + static N_COUNT: u32 = (V_COUNT * T_COUNT); + static S_COUNT: u32 = (L_COUNT * N_COUNT); + + // Decompose a precomposed Hangul syllable + fn decompose_hangul(s: char, f: |char|) { + use mem::transmute; + + let si = s as u32 - S_BASE; + + let li = si / N_COUNT; + unsafe { + f(transmute(L_BASE + li)); + + let vi = (si % N_COUNT) / T_COUNT; + f(transmute(V_BASE + vi)); + + let ti = si % T_COUNT; + if ti > 0 { + f(transmute(T_BASE + ti)); + } + } + } } pub mod derived_property { From df802a2754a5d536bbec264136a08d166d3bb81a Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Mon, 12 May 2014 22:44:21 +0200 Subject: [PATCH 11/33] std: Rename str::Normalizations to str::Decompositions The Normalizations iterator has been renamed to Decompositions. It does not currently include all forms of Unicode normalization, but only encompasses decompositions. If implemented recomposition would likely be a separate iterator which works on the result of this one. [breaking-change] --- src/etc/unicode.py | 12 ++++++------ src/libcore/char.rs | 4 ++-- src/libcore/unicode.rs | 2 +- src/libstd/str.rs | 32 ++++++++++++++++---------------- src/libstd/unicode.rs | 2 +- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/etc/unicode.py b/src/etc/unicode.py index f079ef73cd8e2..586890ebe4c9a 100755 --- a/src/etc/unicode.py +++ b/src/etc/unicode.py @@ -256,13 +256,13 @@ def format_table_content(f, content, indent): line = " "*indent + chunk f.write(line) -def emit_core_decomp_module(f, canon, compat): +def emit_core_norm_module(f, canon, compat): canon_keys = canon.keys() canon_keys.sort() compat_keys = compat.keys() compat_keys.sort() - f.write("pub mod decompose {\n"); + f.write("pub mod normalization {\n"); f.write(" use option::Option;\n"); f.write(" use option::{Some, None};\n"); f.write(" use slice::ImmutableVector;\n"); @@ -401,8 +401,8 @@ def emit_core_decomp_module(f, canon, compat): """) -def emit_std_decomp_module(f, combine): - f.write("pub mod decompose {\n"); +def emit_std_norm_module(f, combine): + f.write("pub mod normalization {\n"); f.write(" use option::{Some, None};\n"); f.write(" use slice::ImmutableVector;\n"); @@ -467,7 +467,7 @@ def gen_core_unicode(): emit_bsearch_range_table(rf); emit_property_module(rf, "general_category", gencats) - emit_core_decomp_module(rf, canon_decomp, compat_decomp) + emit_core_norm_module(rf, canon_decomp, compat_decomp) derived = load_properties("DerivedCoreProperties.txt", ["XID_Start", "XID_Continue", "Alphabetic", "Lowercase", "Uppercase"]) @@ -485,7 +485,7 @@ def gen_std_unicode(): with open(r, "w") as rf: # Preamble rf.write(preamble) - emit_std_decomp_module(rf, combines) + emit_std_norm_module(rf, combines) gen_core_unicode() gen_std_unicode() diff --git a/src/libcore/char.rs b/src/libcore/char.rs index 71a2d75715b5a..934483dbed423 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -30,9 +30,9 @@ use iter::{Iterator, range_step}; use unicode::{derived_property, property, general_category, conversions}; /// Returns the canonical decomposition of a character. -pub use unicode::decompose::decompose_canonical; +pub use unicode::normalization::decompose_canonical; /// Returns the compatibility decomposition of a character. -pub use unicode::decompose::decompose_compatible; +pub use unicode::normalization::decompose_compatible; #[cfg(not(test))] use cmp::{Eq, Ord, TotalEq, TotalOrd, Ordering}; #[cfg(not(test))] use default::Default; diff --git a/src/libcore/unicode.rs b/src/libcore/unicode.rs index bffde2323bf94..242672de2967a 100644 --- a/src/libcore/unicode.rs +++ b/src/libcore/unicode.rs @@ -104,7 +104,7 @@ pub mod general_category { } -pub mod decompose { +pub mod normalization { use option::Option; use option::{Some, None}; use slice::ImmutableVector; diff --git a/src/libstd/str.rs b/src/libstd/str.rs index 24cf9681ca84d..fa4cf8e4427d0 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -228,25 +228,25 @@ fn canonical_sort(comb: &mut [(char, u8)]) { } #[deriving(Clone)] -enum NormalizationForm { - NFD, - NFKD +enum DecompositionType { + Canonical, + Compatible } -/// External iterator for a string's normalization's characters. +/// External iterator for a string's decomposition's characters. /// Use with the `std::iter` module. #[deriving(Clone)] -pub struct Normalizations<'a> { - kind: NormalizationForm, +pub struct Decompositions<'a> { + kind: DecompositionType, iter: Chars<'a>, buffer: Vec<(char, u8)>, sorted: bool } -impl<'a> Iterator for Normalizations<'a> { +impl<'a> Iterator for Decompositions<'a> { #[inline] fn next(&mut self) -> Option { - use unicode::decompose::canonical_combining_class; + use unicode::normalization::canonical_combining_class; match self.buffer.as_slice().head() { Some(&(c, 0)) => { @@ -262,8 +262,8 @@ impl<'a> Iterator for Normalizations<'a> { } let decomposer = match self.kind { - NFD => char::decompose_canonical, - NFKD => char::decompose_compatible + Canonical => char::decompose_canonical, + Compatible => char::decompose_compatible }; if !self.sorted { @@ -887,24 +887,24 @@ pub trait StrAllocating: Str { /// An Iterator over the string in Unicode Normalization Form D /// (canonical decomposition). #[inline] - fn nfd_chars<'a>(&'a self) -> Normalizations<'a> { - Normalizations { + fn nfd_chars<'a>(&'a self) -> Decompositions<'a> { + Decompositions { iter: self.as_slice().chars(), buffer: Vec::new(), sorted: false, - kind: NFD + kind: Canonical } } /// An Iterator over the string in Unicode Normalization Form KD /// (compatibility decomposition). #[inline] - fn nfkd_chars<'a>(&'a self) -> Normalizations<'a> { - Normalizations { + fn nfkd_chars<'a>(&'a self) -> Decompositions<'a> { + Decompositions { iter: self.as_slice().chars(), buffer: Vec::new(), sorted: false, - kind: NFKD + kind: Compatible } } } diff --git a/src/libstd/unicode.rs b/src/libstd/unicode.rs index d534b30221b4a..03c960e96ffe1 100644 --- a/src/libstd/unicode.rs +++ b/src/libstd/unicode.rs @@ -12,7 +12,7 @@ #![allow(missing_doc, non_uppercase_statics)] -pub mod decompose { +pub mod normalization { use option::{Some, None}; use slice::ImmutableVector; From ef23fa17c3030a1a5ecbe1509b36d706fadc3248 Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Sun, 11 May 2014 23:10:40 -0700 Subject: [PATCH 12/33] docs: Add a not found page --- mk/docs.mk | 2 +- src/doc/not_found.md | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/doc/not_found.md diff --git a/mk/docs.mk b/mk/docs.mk index 9c79e1e864253..40c2440c0c07a 100644 --- a/mk/docs.mk +++ b/mk/docs.mk @@ -30,7 +30,7 @@ DOCS := index intro tutorial guide-ffi guide-macros guide-lifetimes \ guide-tasks guide-container guide-pointers guide-testing \ guide-runtime complement-bugreport complement-cheatsheet \ complement-lang-faq complement-project-faq rust rustdoc \ - guide-unsafe + guide-unsafe not_found PDF_DOCS := tutorial rust diff --git a/src/doc/not_found.md b/src/doc/not_found.md new file mode 100644 index 0000000000000..a19418ff66793 --- /dev/null +++ b/src/doc/not_found.md @@ -0,0 +1,20 @@ +% Not Found + + + + +Looks like you've taken a wrong turn. + +Some things that might be helpful to you though: + +## Reference +* [The Rust official site](http://rust-lang.org) +* [The Rust reference manual](http://static.rust-lang.org/doc/master/rust.html) (* [PDF](http://static.rust-lang.org/doc/master/rust.pdf)) + +## Docs +* [The standard library (stable)](http://doc.rust-lang.org/doc/0.10/std/index.html) +* [The standard library (master)](http://doc.rust-lang.org/doc/master/std/index.html) From 0004953c3a29a884d1e772e8874096f21e3b6513 Mon Sep 17 00:00:00 2001 From: Zooko Wilcox-O'Hearn Date: Mon, 12 May 2014 03:57:04 +0000 Subject: [PATCH 13/33] add a line to the example to clarify semantics This is to clarify that match construct doesn't define a new variable, since I observed a person reading the Rust tutorial who seemed to incorrectly think that it did. Fixes https://github.com/mozilla/rust/issues/13571 . --- src/doc/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md index 454d5cf9ddfa7..7dd31f9cc77c6 100644 --- a/src/doc/tutorial.md +++ b/src/doc/tutorial.md @@ -455,7 +455,7 @@ against each pattern in order until one matches. The matching pattern executes its corresponding arm. ~~~~ -# let my_number = 1; +let my_number = 1; match my_number { 0 => println!("zero"), 1 | 2 => println!("one or two"), From 6d535b5b8d71e1cdbdbe54958bcfc4dd956121aa Mon Sep 17 00:00:00 2001 From: klutzy Date: Wed, 16 Apr 2014 14:32:35 +0900 Subject: [PATCH 14/33] rustc: Make std_inject valid for pretty-printer Inject `extern crate {std, native}` before `use` statements. Add `#![feature(glob)]` since `use std::prelude::*` is used. (Unfortunately `rustc --pretty expanded` does not converge, since `extern crate` and `use std::prelude::*` is injected at every iteration.) --- src/librustc/front/std_inject.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/librustc/front/std_inject.rs b/src/librustc/front/std_inject.rs index 66bf6a77f8cfc..f6e6875f0e71f 100644 --- a/src/librustc/front/std_inject.rs +++ b/src/librustc/front/std_inject.rs @@ -100,6 +100,7 @@ impl<'a> fold::Folder for StandardLibraryInjector<'a> { }); } + // `extern crate` must be precede `use` items vis.push_all_move(krate.module.view_items.clone()); let new_module = ast::Mod { view_items: vis, @@ -130,8 +131,20 @@ impl<'a> fold::Folder for PreludeInjector<'a> { if !no_prelude(krate.attrs.as_slice()) { // only add `use std::prelude::*;` if there wasn't a // `#![no_implicit_prelude]` at the crate level. + + let mut attrs = krate.attrs.clone(); + + // fold_mod() will insert glob path. + let globs_attr = attr::mk_attr(attr::mk_list_item( + InternedString::new("feature"), + vec!( + attr::mk_word_item(InternedString::new("globs")), + ))); + attrs.push(globs_attr); + ast::Crate { module: self.fold_mod(&krate.module), + attrs: attrs, ..krate } } else { @@ -175,11 +188,20 @@ impl<'a> fold::Folder for PreludeInjector<'a> { span: DUMMY_SP, }; - let vis = (vec!(vi2)).append(module.view_items.as_slice()); + let (crates, uses) = module.view_items.partitioned(|x| { + match x.node { + ast::ViewItemExternCrate(..) => true, + _ => false, + } + }); + + // add vi2 after any `extern crate` but before any `use` + let mut view_items = crates; + view_items.push(vi2); + view_items.push_all_move(uses); - // FIXME #2543: Bad copy. let new_module = ast::Mod { - view_items: vis, + view_items: view_items, ..(*module).clone() }; fold::noop_fold_mod(&new_module, self) From 7f203b69559a669b623240e43080801a475f8b79 Mon Sep 17 00:00:00 2001 From: klutzy Date: Thu, 17 Apr 2014 05:33:58 +0900 Subject: [PATCH 15/33] pprust: Add parentheses to some Expr Some `Expr` needs parentheses when printed. For example, without parentheses, `ExprUnary(UnNeg, ExprBinary(BiAdd, ..))` becomes `-lhs + rhs` which is wrong. Those cases don't appear in ordinary code (since parentheses are explicitly added) but they can appear in manually crafted ast by extensions. --- src/libsyntax/print/pprust.rs | 48 ++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 71c2f6337e0e6..c8894c1f816b4 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -247,6 +247,15 @@ pub fn visibility_qualified(vis: ast::Visibility, s: &str) -> StrBuf { } } +fn needs_parentheses(expr: &ast::Expr) -> bool { + match expr.node { + ast::ExprAssign(..) | ast::ExprBinary(..) | + ast::ExprFnBlock(..) | ast::ExprProc(..) | + ast::ExprAssignOp(..) | ast::ExprCast(..) => true, + _ => false, + } +} + impl<'a> State<'a> { pub fn ibox(&mut self, u: uint) -> IoResult<()> { self.boxes.push(pp::Inconsistent); @@ -1136,6 +1145,18 @@ impl<'a> State<'a> { self.pclose() } + pub fn print_expr_maybe_paren(&mut self, expr: &ast::Expr) -> IoResult<()> { + let needs_par = needs_parentheses(expr); + if needs_par { + try!(self.popen()); + } + try!(self.print_expr(expr)); + if needs_par { + try!(self.pclose()); + } + Ok(()) + } + pub fn print_expr(&mut self, expr: &ast::Expr) -> IoResult<()> { try!(self.maybe_print_comment(expr.span.lo)); try!(self.ibox(indent_unit)); @@ -1209,7 +1230,7 @@ impl<'a> State<'a> { try!(self.pclose()); } ast::ExprCall(func, ref args) => { - try!(self.print_expr(func)); + try!(self.print_expr_maybe_paren(func)); try!(self.print_call_post(args.as_slice())); } ast::ExprMethodCall(ident, ref tys, ref args) => { @@ -1233,17 +1254,38 @@ impl<'a> State<'a> { } ast::ExprUnary(op, expr) => { try!(word(&mut self.s, ast_util::unop_to_str(op))); - try!(self.print_expr(expr)); + try!(self.print_expr_maybe_paren(expr)); } ast::ExprAddrOf(m, expr) => { try!(word(&mut self.s, "&")); + + // `ExprAddrOf(ExprLit("str"))` should be `&&"str"` instead of `&"str"` + // since `&"str"` is `ExprVstore(ExprLit("str"))` which has same meaning to + // `"str"`. + // In many cases adding parentheses (`&("str")`) would help, but it become invalid + // if expr is in `PatLit()`. + let needs_extra_amp = match expr.node { + ast::ExprLit(lit) => { + match lit.node { + ast::LitStr(..) => true, + _ => false, + } + } + ast::ExprVec(..) => true, + _ => false, + }; + if needs_extra_amp { + try!(word(&mut self.s, "&")); + } + try!(self.print_mutability(m)); // Avoid `& &e` => `&&e`. match (m, &expr.node) { (ast::MutImmutable, &ast::ExprAddrOf(..)) => try!(space(&mut self.s)), _ => { } } - try!(self.print_expr(expr)); + + try!(self.print_expr_maybe_paren(expr)); } ast::ExprLit(lit) => try!(self.print_literal(lit)), ast::ExprCast(expr, ty) => { From cc31bb0a9eebc99e51d5332039c6054e63baf774 Mon Sep 17 00:00:00 2001 From: klutzy Date: Thu, 17 Apr 2014 17:35:40 +0900 Subject: [PATCH 16/33] pprust: Fix asm output --- src/libsyntax/print/pprust.rs | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index c8894c1f816b4..7e94fa4758820 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1516,22 +1516,27 @@ impl<'a> State<'a> { try!(self.popen()); try!(self.print_string(a.asm.get(), a.asm_str_style)); try!(self.word_space(":")); - for &(ref co, o) in a.outputs.iter() { - try!(self.print_string(co.get(), ast::CookedStr)); - try!(self.popen()); - try!(self.print_expr(o)); - try!(self.pclose()); - try!(self.word_space(",")); - } + + try!(self.commasep(Inconsistent, a.outputs.as_slice(), |s, &(ref co, o)| { + try!(s.print_string(co.get(), ast::CookedStr)); + try!(s.popen()); + try!(s.print_expr(o)); + try!(s.pclose()); + Ok(()) + })); + try!(space(&mut self.s)); try!(self.word_space(":")); - for &(ref co, o) in a.inputs.iter() { - try!(self.print_string(co.get(), ast::CookedStr)); - try!(self.popen()); - try!(self.print_expr(o)); - try!(self.pclose()); - try!(self.word_space(",")); - } + + try!(self.commasep(Inconsistent, a.inputs.as_slice(), |s, &(ref co, o)| { + try!(s.print_string(co.get(), ast::CookedStr)); + try!(s.popen()); + try!(s.print_expr(o)); + try!(s.pclose()); + Ok(()) + })); + try!(space(&mut self.s)); try!(self.word_space(":")); + try!(self.print_string(a.clobbers.get(), ast::CookedStr)); try!(self.pclose()); } From 4675a87c8bf4dacdd112c712503f362b7ded1857 Mon Sep 17 00:00:00 2001 From: klutzy Date: Fri, 18 Apr 2014 15:44:25 +0900 Subject: [PATCH 17/33] pprust: Print `&&e` instead of `& &e` --- src/libsyntax/print/pprust.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 7e94fa4758820..78b9930f8a1dd 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1279,11 +1279,6 @@ impl<'a> State<'a> { } try!(self.print_mutability(m)); - // Avoid `& &e` => `&&e`. - match (m, &expr.node) { - (ast::MutImmutable, &ast::ExprAddrOf(..)) => try!(space(&mut self.s)), - _ => { } - } try!(self.print_expr_maybe_paren(expr)); } From 90d976e8fce44d24acefad884d86be08a4a02212 Mon Sep 17 00:00:00 2001 From: klutzy Date: Fri, 18 Apr 2014 15:48:47 +0900 Subject: [PATCH 18/33] pprust: Remove unnecessary && of `print_tt` --- src/libsyntax/print/pprust.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 78b9930f8a1dd..a039959557267 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -723,7 +723,7 @@ impl<'a> State<'a> { try!(self.print_ident(item.ident)); try!(self.cbox(indent_unit)); try!(self.popen()); - try!(self.print_tts(&(tts.as_slice()))); + try!(self.print_tts(tts.as_slice())); try!(self.pclose()); try!(self.end()); } @@ -839,7 +839,7 @@ impl<'a> State<'a> { /// expression arguments as expressions). It can be done! I think. pub fn print_tt(&mut self, tt: &ast::TokenTree) -> IoResult<()> { match *tt { - ast::TTDelim(ref tts) => self.print_tts(&(tts.as_slice())), + ast::TTDelim(ref tts) => self.print_tts(tts.as_slice()), ast::TTTok(_, ref tk) => { word(&mut self.s, parse::token::to_str(tk).as_slice()) } @@ -865,7 +865,7 @@ impl<'a> State<'a> { } } - pub fn print_tts(&mut self, tts: & &[ast::TokenTree]) -> IoResult<()> { + pub fn print_tts(&mut self, tts: &[ast::TokenTree]) -> IoResult<()> { try!(self.ibox(0)); for (i, tt) in tts.iter().enumerate() { if i != 0 { @@ -1122,7 +1122,7 @@ impl<'a> State<'a> { try!(self.print_path(pth, false)); try!(word(&mut self.s, "!")); try!(self.popen()); - try!(self.print_tts(&tts.as_slice())); + try!(self.print_tts(tts.as_slice())); self.pclose() } } From bdd360bb13bb4afad15a9ec7ea1594c22a8d359f Mon Sep 17 00:00:00 2001 From: klutzy Date: Thu, 17 Apr 2014 17:35:36 +0900 Subject: [PATCH 19/33] libsyntax: Workaround pprust `for` issue --- src/libsyntax/ext/expand.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 84525718bd95b..ccc9d73d5447f 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -156,7 +156,7 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr { // } // } - let local_ident = token::gensym_ident("i"); + let local_ident = token::gensym_ident("__i"); // FIXME #13573 let next_ident = fld.cx.ident_of("next"); let none_ident = fld.cx.ident_of("None"); From 0350d8e6d048d7af37ec00622fc40b504981f100 Mon Sep 17 00:00:00 2001 From: klutzy Date: Thu, 17 Apr 2014 18:20:37 +0900 Subject: [PATCH 20/33] test: Add missing `#![feature(managed_boxes)]` The tests use managed boxes, but are not perfectly feature-gated because they use `@` inside macros. (It causes issue after `--pretty expanded`.) --- src/test/run-pass/assert-eq-macro-success.rs | 2 ++ src/test/run-pass/binops.rs | 2 ++ src/test/run-pass/box-compare.rs | 2 +- src/test/run-pass/ifmt.rs | 2 +- src/test/run-pass/nullable-pointer-iotareduction.rs | 2 +- src/test/run-pass/nullable-pointer-size.rs | 2 +- 6 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/test/run-pass/assert-eq-macro-success.rs b/src/test/run-pass/assert-eq-macro-success.rs index 4aea57871b94d..c32d513f07455 100644 --- a/src/test/run-pass/assert-eq-macro-success.rs +++ b/src/test/run-pass/assert-eq-macro-success.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(managed_boxes)] + #[deriving(Eq, Show)] struct Point { x : int } diff --git a/src/test/run-pass/binops.rs b/src/test/run-pass/binops.rs index 331e947586ab8..69705996fadda 100644 --- a/src/test/run-pass/binops.rs +++ b/src/test/run-pass/binops.rs @@ -10,6 +10,8 @@ // Binop corner cases +#![feature(managed_boxes)] + fn test_nil() { assert_eq!((), ()); assert!((!(() != ()))); diff --git a/src/test/run-pass/box-compare.rs b/src/test/run-pass/box-compare.rs index 2e90fba122e09..10a3db5808e2d 100644 --- a/src/test/run-pass/box-compare.rs +++ b/src/test/run-pass/box-compare.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - +#![feature(managed_boxes)] pub fn main() { assert!((@1 < @3)); diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs index 1ac3602293807..12fb8dcccc8dc 100644 --- a/src/test/run-pass/ifmt.rs +++ b/src/test/run-pass/ifmt.rs @@ -9,7 +9,7 @@ // except according to those terms. -#![feature(macro_rules)] +#![feature(macro_rules, managed_boxes)] #![deny(warnings)] #![allow(unused_must_use)] #![allow(deprecated_owned_vector)] diff --git a/src/test/run-pass/nullable-pointer-iotareduction.rs b/src/test/run-pass/nullable-pointer-iotareduction.rs index fb61bea83da8d..728c0154a90e9 100644 --- a/src/test/run-pass/nullable-pointer-iotareduction.rs +++ b/src/test/run-pass/nullable-pointer-iotareduction.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(macro_rules)] +#![feature(macro_rules, managed_boxes)] use std::{option, mem}; diff --git a/src/test/run-pass/nullable-pointer-size.rs b/src/test/run-pass/nullable-pointer-size.rs index 00edcd6a0924b..0ca8efb3fdc91 100644 --- a/src/test/run-pass/nullable-pointer-size.rs +++ b/src/test/run-pass/nullable-pointer-size.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(macro_rules)] +#![feature(macro_rules, managed_boxes)] use std::mem; From 96eeda9708605a60635229d45587f79f9a334ded Mon Sep 17 00:00:00 2001 From: klutzy Date: Wed, 16 Apr 2014 13:56:39 +0900 Subject: [PATCH 21/33] compiletest: Modernize typenames --- src/compiletest/common.rs | 53 +++++++++++++++---- src/compiletest/compiletest.rs | 71 ++++++++------------------ src/compiletest/header.rs | 10 ++-- src/compiletest/runtest.rs | 93 +++++++++++++++++----------------- src/compiletest/util.rs | 4 +- 5 files changed, 119 insertions(+), 112 deletions(-) diff --git a/src/compiletest/common.rs b/src/compiletest/common.rs index ef8b48a053564..ca59f344e288f 100644 --- a/src/compiletest/common.rs +++ b/src/compiletest/common.rs @@ -8,19 +8,52 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::from_str::FromStr; +use std::fmt; + #[deriving(Clone, Eq)] -pub enum mode { - mode_compile_fail, - mode_run_fail, - mode_run_pass, - mode_pretty, - mode_debug_info_gdb, - mode_debug_info_lldb, - mode_codegen +pub enum Mode { + CompileFail, + RunFail, + RunPass, + Pretty, + DebugInfoGdb, + DebugInfoLldb, + Codegen +} + +impl FromStr for Mode { + fn from_str(s: &str) -> Option { + match s { + "compile-fail" => Some(CompileFail), + "run-fail" => Some(RunFail), + "run-pass" => Some(RunPass), + "pretty" => Some(Pretty), + "debuginfo-lldb" => Some(DebugInfoLldb), + "debuginfo-gdb" => Some(DebugInfoGdb), + "codegen" => Some(Codegen), + _ => None, + } + } +} + +impl fmt::Show for Mode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let msg = match *self { + CompileFail => "compile-fail", + RunFail => "run-fail", + RunPass => "run-pass", + Pretty => "pretty", + DebugInfoGdb => "debuginfo-gdb", + DebugInfoLldb => "debuginfo-lldb", + Codegen => "codegen", + }; + write!(f.buf, "{}", msg) + } } #[deriving(Clone)] -pub struct config { +pub struct Config { // The library paths required for running the compiler pub compile_lib_path: ~str, @@ -49,7 +82,7 @@ pub struct config { pub stage_id: ~str, // The test mode, compile-fail, run-fail, run-pass - pub mode: mode, + pub mode: Mode, // Run ignored tests pub run_ignored: bool, diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index f484ea5a8f1fd..d23182b522598 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -14,7 +14,6 @@ // we use our own (green) start below; do not link in libnative; issue #13247. #![no_start] -#![allow(non_camel_case_types)] #![deny(warnings)] extern crate test; @@ -27,9 +26,10 @@ extern crate rustuv; use std::os; use std::io; use std::io::fs; +use std::from_str::FromStr; use getopts::{optopt, optflag, reqopt}; -use common::{config, mode_run_pass, mode_run_fail, mode_compile_fail, mode_pretty, - mode_debug_info_gdb, mode_debug_info_lldb, mode_codegen, mode}; +use common::Config; +use common::{Pretty, DebugInfo, Codegen}; use util::logv; pub mod procsrv; @@ -51,7 +51,7 @@ pub fn main() { run_tests(&config); } -pub fn parse_config(args: Vec<~str> ) -> config { +pub fn parse_config(args: Vec<~str> ) -> Config { let groups : Vec = vec!(reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"), @@ -112,7 +112,7 @@ pub fn parse_config(args: Vec<~str> ) -> config { Path::new(m.opt_str(nm).unwrap()) } - config { + Config { compile_lib_path: matches.opt_str("compile-lib-path").unwrap(), run_lib_path: matches.opt_str("run-lib-path").unwrap(), rustc_path: opt_path(matches, "rustc-path"), @@ -122,7 +122,7 @@ pub fn parse_config(args: Vec<~str> ) -> config { build_base: opt_path(matches, "build-base"), aux_base: opt_path(matches, "aux-base"), stage_id: matches.opt_str("stage-id").unwrap(), - mode: str_mode(matches.opt_str("mode").unwrap()), + mode: FromStr::from_str(matches.opt_str("mode").unwrap()).expect("invalid mode"), run_ignored: matches.opt_present("ignored"), filter: if !matches.free.is_empty() { @@ -155,7 +155,7 @@ pub fn parse_config(args: Vec<~str> ) -> config { } } -pub fn log_config(config: &config) { +pub fn log_config(config: &Config) { let c = config; logv(c, format!("configuration:")); logv(c, format!("compile_lib_path: {}", config.compile_lib_path)); @@ -164,7 +164,7 @@ pub fn log_config(config: &config) { logv(c, format!("src_base: {}", config.src_base.display())); logv(c, format!("build_base: {}", config.build_base.display())); logv(c, format!("stage_id: {}", config.stage_id)); - logv(c, format!("mode: {}", mode_str(config.mode))); + logv(c, format!("mode: {}", config.mode)); logv(c, format!("run_ignored: {}", config.run_ignored)); logv(c, format!("filter: {}", opt_str(&config.filter))); logv(c, format!("runtool: {}", opt_str(&config.runtool))); @@ -198,35 +198,10 @@ pub fn opt_str2(maybestr: Option<~str>) -> ~str { match maybestr { None => "(none)".to_owned(), Some(s) => { s } } } -pub fn str_mode(s: ~str) -> mode { - match s.as_slice() { - "compile-fail" => mode_compile_fail, - "run-fail" => mode_run_fail, - "run-pass" => mode_run_pass, - "pretty" => mode_pretty, - "debuginfo-gdb" => mode_debug_info_gdb, - "debuginfo-lldb" => mode_debug_info_lldb, - "codegen" => mode_codegen, - s => fail!("invalid mode: " + s) - } -} - -pub fn mode_str(mode: mode) -> ~str { - match mode { - mode_compile_fail => "compile-fail".to_owned(), - mode_run_fail => "run-fail".to_owned(), - mode_run_pass => "run-pass".to_owned(), - mode_pretty => "pretty".to_owned(), - mode_debug_info_gdb => "debuginfo-gdb".to_owned(), - mode_debug_info_lldb => "debuginfo-lldb".to_owned(), - mode_codegen => "codegen".to_owned(), - } -} - -pub fn run_tests(config: &config) { - if config.target == "arm-linux-androideabi".to_owned() { - match config.mode{ - mode_debug_info_gdb => { +pub fn run_tests(config: &Config) { + if config.target == ~"arm-linux-androideabi" { + match config.mode { + DebugInfoGdb => { println!("arm-linux-androideabi debug-info \ test uses tcp 5039 port. please reserve it"); } @@ -255,7 +230,7 @@ pub fn run_tests(config: &config) { } } -pub fn test_opts(config: &config) -> test::TestOpts { +pub fn test_opts(config: &Config) -> test::TestOpts { test::TestOpts { filter: config.filter.clone(), run_ignored: config.run_ignored, @@ -270,7 +245,7 @@ pub fn test_opts(config: &config) -> test::TestOpts { } } -pub fn make_tests(config: &config) -> Vec { +pub fn make_tests(config: &Config) -> Vec { debug!("making tests from {}", config.src_base.display()); let mut tests = Vec::new(); @@ -281,7 +256,7 @@ pub fn make_tests(config: &config) -> Vec { if is_test(config, &file) { let t = make_test(config, &file, || { match config.mode { - mode_codegen => make_metrics_test_closure(config, &file), + Codegen => make_metrics_test_closure(config, &file), _ => make_test_closure(config, &file) } }); @@ -291,11 +266,11 @@ pub fn make_tests(config: &config) -> Vec { tests } -pub fn is_test(config: &config, testfile: &Path) -> bool { +pub fn is_test(config: &Config, testfile: &Path) -> bool { // Pretty-printer does not work with .rc files yet let valid_extensions = match config.mode { - mode_pretty => vec!(".rs".to_owned()), + Pretty => vec!(".rs".to_owned()), _ => vec!(".rc".to_owned(), ".rs".to_owned()) }; let invalid_prefixes = vec!(".".to_owned(), "#".to_owned(), "~".to_owned()); @@ -314,7 +289,7 @@ pub fn is_test(config: &config, testfile: &Path) -> bool { return valid; } -pub fn make_test(config: &config, testfile: &Path, f: || -> test::TestFn) +pub fn make_test(config: &Config, testfile: &Path, f: || -> test::TestFn) -> test::TestDescAndFn { test::TestDescAndFn { desc: test::TestDesc { @@ -326,7 +301,7 @@ pub fn make_test(config: &config, testfile: &Path, f: || -> test::TestFn) } } -pub fn make_test_name(config: &config, testfile: &Path) -> test::TestName { +pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName { // Try to elide redundant long paths fn shorten(path: &Path) -> ~str { @@ -336,19 +311,17 @@ pub fn make_test_name(config: &config, testfile: &Path) -> test::TestName { format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or("")) } - test::DynTestName(format!("[{}] {}", - mode_str(config.mode), - shorten(testfile))) + test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile))) } -pub fn make_test_closure(config: &config, testfile: &Path) -> test::TestFn { +pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn { let config = (*config).clone(); // FIXME (#9639): This needs to handle non-utf8 paths let testfile = testfile.as_str().unwrap().to_owned(); test::DynTestFn(proc() { runtest::run(config, testfile) }) } -pub fn make_metrics_test_closure(config: &config, testfile: &Path) -> test::TestFn { +pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::TestFn { let config = (*config).clone(); // FIXME (#9639): This needs to handle non-utf8 paths let testfile = testfile.as_str().unwrap().to_owned(); diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index 3d954a33a0029..e9c41a137d901 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use common::config; +use common::Config; use common; use util; @@ -110,11 +110,11 @@ pub fn load_props(testfile: &Path) -> TestProps { } } -pub fn is_test_ignored(config: &config, testfile: &Path) -> bool { - fn ignore_target(config: &config) -> ~str { +pub fn is_test_ignored(config: &Config, testfile: &Path) -> bool { + fn ignore_target(config: &Config) -> ~str { "ignore-".to_owned() + util::get_os(config.target) } - fn ignore_stage(config: &config) -> ~str { + fn ignore_stage(config: &Config) -> ~str { "ignore-".to_owned() + config.stage_id.split('-').next().unwrap() } @@ -122,7 +122,7 @@ pub fn is_test_ignored(config: &config, testfile: &Path) -> bool { if parse_name_directive(ln, "ignore-test") { false } else if parse_name_directive(ln, ignore_target(config)) { false } else if parse_name_directive(ln, ignore_stage(config)) { false } - else if config.mode == common::mode_pretty && + else if config.mode == common::Pretty && parse_name_directive(ln, "ignore-pretty") { false } else if config.target != config.host && parse_name_directive(ln, "ignore-cross-compile") { false } diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index e47e7dc33d8e4..66bebaff01481 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use common::{config, mode_compile_fail, mode_pretty, mode_run_fail, mode_run_pass}; +use common::Config; +use common::{CompileFail, Pretty, RunFail, RunPass}; use errors; use header::TestProps; use header; @@ -30,7 +31,7 @@ use std::strbuf::StrBuf; use std::task; use test::MetricMap; -pub fn run(config: config, testfile: ~str) { +pub fn run(config: Config, testfile: ~str) { match config.target.as_slice() { @@ -47,7 +48,7 @@ pub fn run(config: config, testfile: ~str) { run_metrics(config, testfile, &mut _mm); } -pub fn run_metrics(config: config, testfile: ~str, mm: &mut MetricMap) { +pub fn run_metrics(config: Config, testfile: ~str, mm: &mut MetricMap) { if config.verbose { // We're going to be dumping a lot of info. Start on a new line. print!("\n\n"); @@ -57,17 +58,17 @@ pub fn run_metrics(config: config, testfile: ~str, mm: &mut MetricMap) { let props = header::load_props(&testfile); debug!("loaded props"); match config.mode { - mode_compile_fail => run_cfail_test(&config, &props, &testfile), - mode_run_fail => run_rfail_test(&config, &props, &testfile), - mode_run_pass => run_rpass_test(&config, &props, &testfile), - mode_pretty => run_pretty_test(&config, &props, &testfile), - mode_debug_info_gdb => run_debuginfo_gdb_test(&config, &props, &testfile), - mode_debug_info_lldb => run_debuginfo_lldb_test(&config, &props, &testfile), - mode_codegen => run_codegen_test(&config, &props, &testfile, mm) + CompileFail => run_cfail_test(&config, &props, &testfile), + RunFail => run_rfail_test(&config, &props, &testfile), + RunPass => run_rpass_test(&config, &props, &testfile), + Pretty => run_pretty_test(&config, &props, &testfile), + DebugInfoGdb => run_debuginfo_gdb_test(&config, &props, &testfile), + DebugInfoLldb => run_debuginfo_lldb_test(&config, &props, &testfile), + Codegen => run_codegen_test(&config, &props, &testfile, mm) } } -fn run_cfail_test(config: &config, props: &TestProps, testfile: &Path) { +fn run_cfail_test(config: &Config, props: &TestProps, testfile: &Path) { let proc_res = compile_test(config, props, testfile); if proc_res.status.success() { @@ -88,7 +89,7 @@ fn run_cfail_test(config: &config, props: &TestProps, testfile: &Path) { check_no_compiler_crash(&proc_res); } -fn run_rfail_test(config: &config, props: &TestProps, testfile: &Path) { +fn run_rfail_test(config: &Config, props: &TestProps, testfile: &Path) { let proc_res = if !config.jit { let proc_res = compile_test(config, props, testfile); @@ -121,7 +122,7 @@ fn check_correct_failure_status(proc_res: &ProcRes) { } } -fn run_rpass_test(config: &config, props: &TestProps, testfile: &Path) { +fn run_rpass_test(config: &Config, props: &TestProps, testfile: &Path) { if !config.jit { let mut proc_res = compile_test(config, props, testfile); @@ -141,7 +142,7 @@ fn run_rpass_test(config: &config, props: &TestProps, testfile: &Path) { } } -fn run_pretty_test(config: &config, props: &TestProps, testfile: &Path) { +fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) { if props.pp_exact.is_some() { logv(config, "testing for exact pretty-printing".to_owned()); } else { logv(config, "testing for converging pretty-printing".to_owned()); } @@ -198,12 +199,12 @@ fn run_pretty_test(config: &config, props: &TestProps, testfile: &Path) { return; - fn print_source(config: &config, testfile: &Path, src: ~str) -> ProcRes { + fn print_source(config: &Config, testfile: &Path, src: ~str) -> ProcRes { compose_and_run(config, testfile, make_pp_args(config, testfile), Vec::new(), config.compile_lib_path, Some(src)) } - fn make_pp_args(config: &config, _testfile: &Path) -> ProcArgs { + fn make_pp_args(config: &Config, _testfile: &Path) -> ProcArgs { let args = vec!("-".to_owned(), "--pretty".to_owned(), "normal".to_owned(), "--target=".to_owned() + config.target); // FIXME (#9639): This needs to handle non-utf8 paths @@ -228,13 +229,13 @@ actual:\n\ } } - fn typecheck_source(config: &config, props: &TestProps, + fn typecheck_source(config: &Config, props: &TestProps, testfile: &Path, src: ~str) -> ProcRes { let args = make_typecheck_args(config, props, testfile); compose_and_run_compiler(config, props, testfile, args, Some(src)) } - fn make_typecheck_args(config: &config, props: &TestProps, testfile: &Path) -> ProcArgs { + fn make_typecheck_args(config: &Config, props: &TestProps, testfile: &Path) -> ProcArgs { let aux_dir = aux_output_dir_name(config, testfile); let target = if props.force_host { config.host.as_slice() @@ -255,8 +256,8 @@ actual:\n\ } } -fn run_debuginfo_gdb_test(config: &config, props: &TestProps, testfile: &Path) { - let mut config = config { +fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) { + let mut config = Config { target_rustcflags: cleanup_debug_info_options(&config.target_rustcflags), host_rustcflags: cleanup_debug_info_options(&config.host_rustcflags), .. config.clone() @@ -834,16 +835,16 @@ struct ProcArgs {prog: ~str, args: Vec<~str> } struct ProcRes {status: ProcessExit, stdout: ~str, stderr: ~str, cmdline: ~str} -fn compile_test(config: &config, props: &TestProps, +fn compile_test(config: &Config, props: &TestProps, testfile: &Path) -> ProcRes { compile_test_(config, props, testfile, []) } -fn jit_test(config: &config, props: &TestProps, testfile: &Path) -> ProcRes { +fn jit_test(config: &Config, props: &TestProps, testfile: &Path) -> ProcRes { compile_test_(config, props, testfile, ["--jit".to_owned()]) } -fn compile_test_(config: &config, props: &TestProps, +fn compile_test_(config: &Config, props: &TestProps, testfile: &Path, extra_args: &[~str]) -> ProcRes { let aux_dir = aux_output_dir_name(config, testfile); // FIXME (#9639): This needs to handle non-utf8 paths @@ -855,7 +856,7 @@ fn compile_test_(config: &config, props: &TestProps, compose_and_run_compiler(config, props, testfile, args, None) } -fn exec_compiled_test(config: &config, props: &TestProps, +fn exec_compiled_test(config: &Config, props: &TestProps, testfile: &Path) -> ProcRes { let env = props.exec_env.clone(); @@ -876,7 +877,7 @@ fn exec_compiled_test(config: &config, props: &TestProps, } fn compose_and_run_compiler( - config: &config, + config: &Config, props: &TestProps, testfile: &Path, args: ProcArgs, @@ -934,7 +935,7 @@ fn ensure_dir(path: &Path) { fs::mkdir(path, io::UserRWX).unwrap(); } -fn compose_and_run(config: &config, testfile: &Path, +fn compose_and_run(config: &Config, testfile: &Path, ProcArgs{ args, prog }: ProcArgs, procenv: Vec<(~str, ~str)> , lib_path: &str, @@ -948,10 +949,10 @@ enum TargetLocation { ThisDirectory(Path), } -fn make_compile_args(config: &config, +fn make_compile_args(config: &Config, props: &TestProps, extras: Vec<~str> , - xform: |&config, &Path| -> TargetLocation, + xform: |&Config, &Path| -> TargetLocation, testfile: &Path) -> ProcArgs { let xform_file = xform(config, testfile); @@ -983,14 +984,14 @@ fn make_compile_args(config: &config, return ProcArgs {prog: config.rustc_path.as_str().unwrap().to_owned(), args: args}; } -fn make_lib_name(config: &config, auxfile: &Path, testfile: &Path) -> Path { +fn make_lib_name(config: &Config, auxfile: &Path, testfile: &Path) -> Path { // what we return here is not particularly important, as it // happens; rustc ignores everything except for the directory. let auxname = output_testname(auxfile); aux_output_dir_name(config, testfile).join(&auxname) } -fn make_exe_name(config: &config, testfile: &Path) -> Path { +fn make_exe_name(config: &Config, testfile: &Path) -> Path { let mut f = output_base_name(config, testfile); if !os::consts::EXE_SUFFIX.is_empty() { match f.filename().map(|s| Vec::from_slice(s).append(os::consts::EXE_SUFFIX.as_bytes())) { @@ -1001,7 +1002,7 @@ fn make_exe_name(config: &config, testfile: &Path) -> Path { f } -fn make_run_args(config: &config, props: &TestProps, testfile: &Path) -> +fn make_run_args(config: &Config, props: &TestProps, testfile: &Path) -> ProcArgs { // If we've got another tool to run under (valgrind), // then split apart its command @@ -1029,7 +1030,7 @@ fn split_maybe_args(argstr: &Option<~str>) -> Vec<~str> { } } -fn program_output(config: &config, testfile: &Path, lib_path: &str, prog: ~str, +fn program_output(config: &Config, testfile: &Path, lib_path: &str, prog: ~str, args: Vec<~str> , env: Vec<(~str, ~str)> , input: Option<~str>) -> ProcRes { let cmdline = @@ -1069,23 +1070,23 @@ fn lib_path_cmd_prefix(path: &str) -> ~str { format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path)) } -fn dump_output(config: &config, testfile: &Path, out: &str, err: &str) { +fn dump_output(config: &Config, testfile: &Path, out: &str, err: &str) { dump_output_file(config, testfile, out, "out"); dump_output_file(config, testfile, err, "err"); maybe_dump_to_stdout(config, out, err); } -fn dump_output_file(config: &config, testfile: &Path, +fn dump_output_file(config: &Config, testfile: &Path, out: &str, extension: &str) { let outfile = make_out_name(config, testfile, extension); File::create(&outfile).write(out.as_bytes()).unwrap(); } -fn make_out_name(config: &config, testfile: &Path, extension: &str) -> Path { +fn make_out_name(config: &Config, testfile: &Path, extension: &str) -> Path { output_base_name(config, testfile).with_extension(extension) } -fn aux_output_dir_name(config: &config, testfile: &Path) -> Path { +fn aux_output_dir_name(config: &Config, testfile: &Path) -> Path { let mut f = output_base_name(config, testfile); match f.filename().map(|s| Vec::from_slice(s).append(bytes!(".libaux"))) { Some(v) => f.set_filename(v), @@ -1098,13 +1099,13 @@ fn output_testname(testfile: &Path) -> Path { Path::new(testfile.filestem().unwrap()) } -fn output_base_name(config: &config, testfile: &Path) -> Path { +fn output_base_name(config: &Config, testfile: &Path) -> Path { config.build_base .join(&output_testname(testfile)) .with_extension(config.stage_id.as_slice()) } -fn maybe_dump_to_stdout(config: &config, out: &str, err: &str) { +fn maybe_dump_to_stdout(config: &Config, out: &str, err: &str) { if config.verbose { println!("------{}------------------------------", "stdout"); println!("{}", out); @@ -1137,7 +1138,7 @@ stderr:\n\ fail!(); } -fn _arm_exec_compiled_test(config: &config, props: &TestProps, +fn _arm_exec_compiled_test(config: &Config, props: &TestProps, testfile: &Path, env: Vec<(~str, ~str)> ) -> ProcRes { let args = make_run_args(config, props, testfile); @@ -1237,7 +1238,7 @@ fn _arm_exec_compiled_test(config: &config, props: &TestProps, } } -fn _arm_push_aux_shared_library(config: &config, testfile: &Path) { +fn _arm_push_aux_shared_library(config: &Config, testfile: &Path) { let tdir = aux_output_dir_name(config, testfile); let dirs = fs::readdir(&tdir).unwrap(); @@ -1260,7 +1261,7 @@ fn _arm_push_aux_shared_library(config: &config, testfile: &Path) { // codegen tests (vs. clang) -fn make_o_name(config: &config, testfile: &Path) -> Path { +fn make_o_name(config: &Config, testfile: &Path) -> Path { output_base_name(config, testfile).with_extension("o") } @@ -1273,7 +1274,7 @@ fn append_suffix_to_stem(p: &Path, suffix: &str) -> Path { } } -fn compile_test_and_save_bitcode(config: &config, props: &TestProps, +fn compile_test_and_save_bitcode(config: &Config, props: &TestProps, testfile: &Path) -> ProcRes { let aux_dir = aux_output_dir_name(config, testfile); // FIXME (#9639): This needs to handle non-utf8 paths @@ -1287,7 +1288,7 @@ fn compile_test_and_save_bitcode(config: &config, props: &TestProps, compose_and_run_compiler(config, props, testfile, args, None) } -fn compile_cc_with_clang_and_save_bitcode(config: &config, _props: &TestProps, +fn compile_cc_with_clang_and_save_bitcode(config: &Config, _props: &TestProps, testfile: &Path) -> ProcRes { let bitcodefile = output_base_name(config, testfile).with_extension("bc"); let bitcodefile = append_suffix_to_stem(&bitcodefile, "clang"); @@ -1303,7 +1304,7 @@ fn compile_cc_with_clang_and_save_bitcode(config: &config, _props: &TestProps, compose_and_run(config, testfile, proc_args, Vec::new(), "", None) } -fn extract_function_from_bitcode(config: &config, _props: &TestProps, +fn extract_function_from_bitcode(config: &Config, _props: &TestProps, fname: &str, testfile: &Path, suffix: &str) -> ProcRes { let bitcodefile = output_base_name(config, testfile).with_extension("bc"); @@ -1320,7 +1321,7 @@ fn extract_function_from_bitcode(config: &config, _props: &TestProps, compose_and_run(config, testfile, proc_args, Vec::new(), "", None) } -fn disassemble_extract(config: &config, _props: &TestProps, +fn disassemble_extract(config: &Config, _props: &TestProps, testfile: &Path, suffix: &str) -> ProcRes { let bitcodefile = output_base_name(config, testfile).with_extension("bc"); let bitcodefile = append_suffix_to_stem(&bitcodefile, suffix); @@ -1344,7 +1345,7 @@ fn count_extracted_lines(p: &Path) -> uint { } -fn run_codegen_test(config: &config, props: &TestProps, +fn run_codegen_test(config: &Config, props: &TestProps, testfile: &Path, mm: &mut MetricMap) { if config.llvm_bin_path.is_none() { diff --git a/src/compiletest/util.rs b/src/compiletest/util.rs index e0afd57adf04b..253b7e87d0223 100644 --- a/src/compiletest/util.rs +++ b/src/compiletest/util.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use common::config; +use common::Config; #[cfg(target_os = "win32")] use std::os::getenv; @@ -51,7 +51,7 @@ pub fn lib_path_env_var() -> ~str { "PATH".to_owned() } #[cfg(target_os = "win32")] pub fn path_div() -> ~str { ";".to_owned() } -pub fn logv(config: &config, s: ~str) { +pub fn logv(config: &Config, s: ~str) { debug!("{}", s); if config.verbose { println!("{}", s); } } From ce8c467bd2062edc1410f54c8efafbc5ae245358 Mon Sep 17 00:00:00 2001 From: klutzy Date: Wed, 16 Apr 2014 14:29:02 +0900 Subject: [PATCH 22/33] compiletest: Test `--pretty expanded` After testing `--pretty normal`, it tries to run `--pretty expanded` and typecheck output. Here we don't check convergence since it really diverges: for every iteration, some extra lines (e.g.`extern crate std`) are inserted. Some tests are `ignore-pretty`-ed since they cause various issues with `--pretty expanded`. --- src/compiletest/runtest.rs | 25 +++++++++++++++---- src/test/run-fail/run-unexported-tests.rs | 1 + src/test/run-fail/test-fail.rs | 1 + src/test/run-fail/test-tasks-invalid-value.rs | 1 + .../quote-unused-sp-no-warning.rs | 2 ++ src/test/run-pass/hygienic-labels-in-let.rs | 2 ++ src/test/run-pass/ifmt.rs | 1 + src/test/run-pass/shebang.rs | 2 +- src/test/run-pass/test-ignore-cfg.rs | 1 + src/test/run-pass/test-runner-hides-main.rs | 1 + 10 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 66bebaff01481..bd909c8f0693f 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -157,9 +157,7 @@ fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) { let mut round = 0; while round < rounds { logv(config, format!("pretty-printing round {}", round)); - let proc_res = print_source(config, - testfile, - (*srcs.get(round)).clone()); + let proc_res = print_source(config, props, testfile, (*srcs.get(round)).clone(), "normal"); if !proc_res.status.success() { fatal_ProcRes(format!("pretty-printing failed in round {}", round), @@ -197,10 +195,27 @@ fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) { fatal_ProcRes("pretty-printed source does not typecheck".to_owned(), &proc_res); } + // additionally, run `--pretty expanded` and try to build it. + let proc_res = print_source(config, props, testfile, (*srcs.get(round)).clone(), "expanded"); + if !proc_res.status.success() { + fatal_ProcRes(format!("pretty-printing (expanded) failed"), &proc_res); + } + + let ProcRes{ stdout: expanded_src, .. } = proc_res; + let proc_res = typecheck_source(config, props, testfile, expanded_src); + if !proc_res.status.success() { + fatal_ProcRes(format!("pretty-printed source (expanded) does not typecheck"), &proc_res); + } + return; - fn print_source(config: &Config, testfile: &Path, src: ~str) -> ProcRes { - compose_and_run(config, testfile, make_pp_args(config, testfile), + fn print_source(config: &Config, + props: &TestProps, + testfile: &Path, + src: ~str, + pretty_type: &str) -> ProcRes { + compose_and_run(config, testfile, + make_pp_args(config, props, testfile, pretty_type.to_owned()), Vec::new(), config.compile_lib_path, Some(src)) } diff --git a/src/test/run-fail/run-unexported-tests.rs b/src/test/run-fail/run-unexported-tests.rs index b894127559161..70ef4a0c0c3d3 100644 --- a/src/test/run-fail/run-unexported-tests.rs +++ b/src/test/run-fail/run-unexported-tests.rs @@ -11,6 +11,7 @@ // error-pattern:runned an unexported test // compile-flags:--test // check-stdout +// ignore-pretty: does not work well with `--test` mod m { pub fn exported() { } diff --git a/src/test/run-fail/test-fail.rs b/src/test/run-fail/test-fail.rs index 77d87c22c6f1c..b628f101fd576 100644 --- a/src/test/run-fail/test-fail.rs +++ b/src/test/run-fail/test-fail.rs @@ -11,6 +11,7 @@ // check-stdout // error-pattern:task 'test_foo' failed at // compile-flags: --test +// ignore-pretty: does not work well with `--test` #[test] fn test_foo() { diff --git a/src/test/run-fail/test-tasks-invalid-value.rs b/src/test/run-fail/test-tasks-invalid-value.rs index 74531deb58ef6..8c9cd2d63cb47 100644 --- a/src/test/run-fail/test-tasks-invalid-value.rs +++ b/src/test/run-fail/test-tasks-invalid-value.rs @@ -14,6 +14,7 @@ // error-pattern:should be a positive integer // compile-flags: --test // exec-env:RUST_TEST_TASKS=foo +// ignore-pretty: does not work well with `--test` #[test] fn do_nothing() {} diff --git a/src/test/run-pass-fulldeps/quote-unused-sp-no-warning.rs b/src/test/run-pass-fulldeps/quote-unused-sp-no-warning.rs index 5025cc12b4a64..2d5bbd43e826f 100644 --- a/src/test/run-pass-fulldeps/quote-unused-sp-no-warning.rs +++ b/src/test/run-pass-fulldeps/quote-unused-sp-no-warning.rs @@ -9,6 +9,8 @@ // except according to those terms. // ignore-android +// ignore-pretty: does not work well with `--test` + #![feature(quote)] #![deny(unused_variable)] diff --git a/src/test/run-pass/hygienic-labels-in-let.rs b/src/test/run-pass/hygienic-labels-in-let.rs index 22523f5e6ccb7..ee90cfd3475b1 100644 --- a/src/test/run-pass/hygienic-labels-in-let.rs +++ b/src/test/run-pass/hygienic-labels-in-let.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-pretty: pprust doesn't print hygiene output + #![feature(macro_rules)] macro_rules! loop_x { diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs index 12fb8dcccc8dc..4d445db302b0b 100644 --- a/src/test/run-pass/ifmt.rs +++ b/src/test/run-pass/ifmt.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-pretty: `--pretty expand` creates unnecessary `unsafe` block #![feature(macro_rules, managed_boxes)] #![deny(warnings)] diff --git a/src/test/run-pass/shebang.rs b/src/test/run-pass/shebang.rs index b6451c5777266..2f78513b95cf9 100644 --- a/src/test/run-pass/shebang.rs +++ b/src/test/run-pass/shebang.rs @@ -9,6 +9,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// pp-exact +// ignore-pretty: `expand` addes some preludes before shebang pub fn main() { println!("Hello World"); } diff --git a/src/test/run-pass/test-ignore-cfg.rs b/src/test/run-pass/test-ignore-cfg.rs index e8d36dad20877..309325ab7db4a 100644 --- a/src/test/run-pass/test-ignore-cfg.rs +++ b/src/test/run-pass/test-ignore-cfg.rs @@ -9,6 +9,7 @@ // except according to those terms. // compile-flags: --test --cfg ignorecfg +// ignore-pretty: does not work well with `--test` #[test] #[ignore(cfg(ignorecfg))] diff --git a/src/test/run-pass/test-runner-hides-main.rs b/src/test/run-pass/test-runner-hides-main.rs index 954d88c0bdc5e..05d5506f0a6b0 100644 --- a/src/test/run-pass/test-runner-hides-main.rs +++ b/src/test/run-pass/test-runner-hides-main.rs @@ -10,6 +10,7 @@ // compile-flags:--test // ignore-win32 #10872 +// ignore-pretty: does not work well with `--test` // Building as a test runner means that a synthetic main will be run, // not ours From 12375304524ffe732752f5a29551c2caf0b14b4f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 10 May 2014 17:39:08 -0700 Subject: [PATCH 23/33] Touch up and rebase previous commits * Added `// no-pretty-expanded` to pretty-print a test, but not run it through the `expanded` variant. * Removed #[deriving] and other expanded attributes after they are expanded * Removed hacks around &str and &&str and friends (from both the parser and the pretty printer). * Un-ignored a bunch of tests --- src/compiletest/compiletest.rs | 4 +-- src/compiletest/header.rs | 12 +++++++ src/compiletest/runtest.rs | 24 +++++++++----- src/librustc/middle/lint.rs | 31 ++++++++++++++++--- src/librustdoc/html/highlight.rs | 1 + src/librustdoc/html/markdown.rs | 1 + src/libsyntax/ext/expand.rs | 16 +++++++--- src/libsyntax/parse/parser.rs | 3 -- src/libsyntax/print/pprust.rs | 23 +------------- src/test/bench/core-set.rs | 4 +-- src/test/bench/shootout-k-nucleotide-pipes.rs | 2 +- src/test/bench/shootout-k-nucleotide.rs | 1 - src/test/bench/shootout-mandelbrot.rs | 2 ++ src/test/bench/shootout-reverse-complement.rs | 2 +- src/test/bench/sudoku.rs | 4 +-- .../bench/task-perf-jargon-metal-smoke.rs | 4 +-- .../compile-fail/borrowck-lend-flow-match.rs | 2 -- .../borrowck-pat-reassign-binding.rs | 2 -- .../borrowck-preserve-box-in-field.rs | 2 -- .../borrowck-preserve-box-in-uniq.rs | 2 -- .../compile-fail/borrowck-preserve-box.rs | 2 -- .../borrowck-preserve-expl-deref.rs | 2 -- .../run-pass/anon-extern-mod-cross-crate-2.rs | 1 - src/test/run-pass/big-literals.rs | 2 -- src/test/run-pass/borrowck-pat-enum.rs | 2 -- src/test/run-pass/closure-syntax.rs | 2 -- src/test/run-pass/deriving-global.rs | 12 ------- src/test/run-pass/ifmt.rs | 3 +- src/test/run-pass/invoke-external-foreign.rs | 1 - .../run-pass/numeric-method-autoexport.rs | 5 +-- ...cts-owned-object-borrowed-method-header.rs | 2 -- src/test/run-pass/super-fast-paren-parsing.rs | 2 -- src/test/run-pass/trait-bounds-in-arc.rs | 2 -- 33 files changed, 85 insertions(+), 95 deletions(-) diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index d23182b522598..ee0fe2065303b 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -29,7 +29,7 @@ use std::io::fs; use std::from_str::FromStr; use getopts::{optopt, optflag, reqopt}; use common::Config; -use common::{Pretty, DebugInfo, Codegen}; +use common::{Pretty, DebugInfoGdb, Codegen}; use util::logv; pub mod procsrv; @@ -199,7 +199,7 @@ pub fn opt_str2(maybestr: Option<~str>) -> ~str { } pub fn run_tests(config: &Config) { - if config.target == ~"arm-linux-androideabi" { + if config.target == "arm-linux-androideabi".to_owned() { match config.mode { DebugInfoGdb => { println!("arm-linux-androideabi debug-info \ diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index e9c41a137d901..047be95547746 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -34,6 +34,8 @@ pub struct TestProps { pub check_stdout: bool, // Don't force a --crate-type=dylib flag on the command line pub no_prefer_dynamic: bool, + // Don't run --pretty expanded when running pretty printing tests + pub no_pretty_expanded: bool, } // Load any test directives embedded in the file @@ -48,6 +50,7 @@ pub fn load_props(testfile: &Path) -> TestProps { let mut force_host = false; let mut check_stdout = false; let mut no_prefer_dynamic = false; + let mut no_pretty_expanded = false; iter_header(testfile, |ln| { match parse_error_pattern(ln) { Some(ep) => error_patterns.push(ep), @@ -78,6 +81,10 @@ pub fn load_props(testfile: &Path) -> TestProps { no_prefer_dynamic = parse_no_prefer_dynamic(ln); } + if !no_pretty_expanded { + no_pretty_expanded = parse_no_pretty_expanded(ln); + } + match parse_aux_build(ln) { Some(ab) => { aux_builds.push(ab); } None => {} @@ -107,6 +114,7 @@ pub fn load_props(testfile: &Path) -> TestProps { force_host: force_host, check_stdout: check_stdout, no_prefer_dynamic: no_prefer_dynamic, + no_pretty_expanded: no_pretty_expanded, } } @@ -180,6 +188,10 @@ fn parse_no_prefer_dynamic(line: &str) -> bool { parse_name_directive(line, "no-prefer-dynamic") } +fn parse_no_pretty_expanded(line: &str) -> bool { + parse_name_directive(line, "no-pretty-expanded") +} + fn parse_exec_env(line: &str) -> Option<(~str, ~str)> { parse_name_value_directive(line, "exec-env".to_owned()).map(|nv| { // nv is either FOO or FOO=BAR diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index bd909c8f0693f..d7fa4f209d4da 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -9,7 +9,7 @@ // except according to those terms. use common::Config; -use common::{CompileFail, Pretty, RunFail, RunPass}; +use common::{CompileFail, Pretty, RunFail, RunPass, DebugInfoGdb, DebugInfoLldb}; use errors; use header::TestProps; use header; @@ -64,7 +64,7 @@ pub fn run_metrics(config: Config, testfile: ~str, mm: &mut MetricMap) { Pretty => run_pretty_test(&config, &props, &testfile), DebugInfoGdb => run_debuginfo_gdb_test(&config, &props, &testfile), DebugInfoLldb => run_debuginfo_lldb_test(&config, &props, &testfile), - Codegen => run_codegen_test(&config, &props, &testfile, mm) + Codegen => run_codegen_test(&config, &props, &testfile, mm), } } @@ -194,6 +194,7 @@ fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) { if !proc_res.status.success() { fatal_ProcRes("pretty-printed source does not typecheck".to_owned(), &proc_res); } + if props.no_pretty_expanded { return } // additionally, run `--pretty expanded` and try to build it. let proc_res = print_source(config, props, testfile, (*srcs.get(round)).clone(), "expanded"); @@ -219,10 +220,17 @@ fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) { Vec::new(), config.compile_lib_path, Some(src)) } - fn make_pp_args(config: &Config, _testfile: &Path) -> ProcArgs { - let args = vec!("-".to_owned(), "--pretty".to_owned(), "normal".to_owned(), - "--target=".to_owned() + config.target); + fn make_pp_args(config: &Config, + props: &TestProps, + testfile: &Path, + pretty_type: ~str) -> ProcArgs { + let aux_dir = aux_output_dir_name(config, testfile); // FIXME (#9639): This needs to handle non-utf8 paths + let mut args = vec!("-".to_owned(), "--pretty".to_owned(), pretty_type, + "--target=".to_owned() + config.target, + "-L".to_owned(), aux_dir.as_str().unwrap().to_owned()); + args.push_all_move(split_maybe_args(&config.target_rustcflags)); + args.push_all_move(split_maybe_args(&props.compile_flags)); return ProcArgs {prog: config.rustc_path.as_str().unwrap().to_owned(), args: args}; } @@ -419,14 +427,14 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) { check_debugger_output(&debugger_run_result, check_lines.as_slice()); } -fn run_debuginfo_lldb_test(config: &config, props: &TestProps, testfile: &Path) { +fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path) { use std::io::process::{Process, ProcessConfig, ProcessOutput}; if config.lldb_python_dir.is_none() { fatal("Can't run LLDB test because LLDB's python path is not set.".to_owned()); } - let mut config = config { + let mut config = Config { target_rustcflags: cleanup_debug_info_options(&config.target_rustcflags), host_rustcflags: cleanup_debug_info_options(&config.host_rustcflags), .. config.clone() @@ -481,7 +489,7 @@ fn run_debuginfo_lldb_test(config: &config, props: &TestProps, testfile: &Path) check_debugger_output(&debugger_run_result, check_lines.as_slice()); - fn run_lldb(config: &config, test_executable: &Path, debugger_script: &Path) -> ProcRes { + fn run_lldb(config: &Config, test_executable: &Path, debugger_script: &Path) -> ProcRes { // Prepare the lldb_batchmode which executes the debugger script let lldb_batchmode_script = "./src/etc/lldb_batchmode.py".to_owned(); let test_executable_str = test_executable.as_str().unwrap().to_owned(); diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs index cc0697ce52769..1bf4d0a02faf0 100644 --- a/src/librustc/middle/lint.rs +++ b/src/librustc/middle/lint.rs @@ -46,6 +46,7 @@ use middle::typeck::astconv::{ast_ty_to_ty, AstConv}; use middle::typeck::infer; use middle::typeck; use util::ppaux::{ty_to_str}; +use util::nodemap::NodeSet; use std::cmp; use collections::HashMap; @@ -453,10 +454,13 @@ struct Context<'a> { // When recursing into an attributed node of the ast which modifies lint // levels, this stack keeps track of the previous lint levels of whatever // was modified. - lint_stack: Vec<(Lint, level, LintSource)> , + lint_stack: Vec<(Lint, level, LintSource)>, // id of the last visited negated expression - negated_expr_id: ast::NodeId + negated_expr_id: ast::NodeId, + + // ids of structs/enums which have been checked for raw_pointer_deriving + checked_raw_pointers: NodeSet, } impl<'a> Context<'a> { @@ -1014,10 +1018,26 @@ impl<'a> Visitor<()> for RawPtrDerivingVisitor<'a> { fn visit_block(&mut self, _: &ast::Block, _: ()) {} } -fn check_raw_ptr_deriving(cx: &Context, item: &ast::Item) { - if !attr::contains_name(item.attrs.as_slice(), "deriving") { +fn check_raw_ptr_deriving(cx: &mut Context, item: &ast::Item) { + if !attr::contains_name(item.attrs.as_slice(), "automatically_derived") { return } + let did = match item.node { + ast::ItemImpl(..) => { + match ty::get(ty::node_id_to_type(cx.tcx, item.id)).sty { + ty::ty_enum(did, _) => did, + ty::ty_struct(did, _) => did, + _ => return, + } + } + _ => return, + }; + if !ast_util::is_local(did) { return } + let item = match cx.tcx.map.find(did.node) { + Some(ast_map::NodeItem(item)) => item, + _ => return, + }; + if !cx.checked_raw_pointers.insert(item.id) { return } match item.node { ast::ItemStruct(..) | ast::ItemEnum(..) => { let mut visitor = RawPtrDerivingVisitor { cx: cx }; @@ -1848,7 +1868,8 @@ pub fn check_crate(tcx: &ty::ctxt, cur_struct_def_id: -1, is_doc_hidden: false, lint_stack: Vec::new(), - negated_expr_id: -1 + negated_expr_id: -1, + checked_raw_pointers: NodeSet::new(), }; // Install default lint levels, followed by the command line levels, and diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 590086e9d3ac1..5d4350f8fb5cd 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -26,6 +26,7 @@ use t = syntax::parse::token; /// Highlights some source code, returning the HTML output. pub fn highlight(src: &str, class: Option<&str>) -> StrBuf { + debug!("highlighting: ================\n{}\n==============", src); let sess = parse::new_parse_sess(); let fm = parse::string_to_filemap(&sess, src.to_strbuf(), diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 76f7949bcf9c2..d6831e225bc29 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -149,6 +149,7 @@ pub fn render(w: &mut io::Writer, s: &str, print_toc: bool) -> fmt::Result { let my_opaque: &MyOpaque = &*((*opaque).opaque as *MyOpaque); slice::raw::buf_as_slice((*text).data, (*text).size as uint, |text| { let text = str::from_utf8(text).unwrap(); + debug!("docblock: ==============\n{}\n=======", text); let mut lines = text.lines().filter(|l| { stripped_filtered_line(*l).is_none() }); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index ccc9d73d5447f..1898e8bf000a8 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -262,7 +262,8 @@ pub fn expand_item(it: @ast::Item, fld: &mut MacroExpander) let it = expand_item_modifiers(it, fld); let mut decorator_items = SmallVector::zero(); - for attr in it.attrs.iter().rev() { + let mut new_attrs = Vec::new(); + for attr in it.attrs.iter() { let mname = attr.name(); match fld.extsbox.find(&intern(mname.get())) { @@ -286,7 +287,7 @@ pub fn expand_item(it: @ast::Item, fld: &mut MacroExpander) fld.cx.bt_pop(); } - _ => {} + _ => new_attrs.push((*attr).clone()), } } @@ -294,14 +295,21 @@ pub fn expand_item(it: @ast::Item, fld: &mut MacroExpander) ast::ItemMac(..) => expand_item_mac(it, fld), ast::ItemMod(_) | ast::ItemForeignMod(_) => { fld.cx.mod_push(it.ident); - let macro_escape = contains_macro_escape(it.attrs.as_slice()); + let macro_escape = contains_macro_escape(new_attrs.as_slice()); let result = with_exts_frame!(fld.extsbox, macro_escape, noop_fold_item(it, fld)); fld.cx.mod_pop(); result }, - _ => noop_fold_item(it, fld) + _ => { + let it = @ast::Item { + attrs: new_attrs, + ..(*it).clone() + + }; + noop_fold_item(it, fld) + } }; new_items.push_all(decorator_items); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 2201b08f2ca48..a83bb7d1bf575 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2241,9 +2241,6 @@ impl<'a> Parser<'a> { ExprVec(..) if m == MutImmutable => { ExprVstore(e, ExprVstoreSlice) } - ExprLit(lit) if lit_is_str(lit) && m == MutImmutable => { - ExprVstore(e, ExprVstoreSlice) - } ExprVec(..) if m == MutMutable => { ExprVstore(e, ExprVstoreMutSlice) } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index a039959557267..326f31d11e958 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -168,7 +168,7 @@ pub fn tt_to_str(tt: &ast::TokenTree) -> StrBuf { } pub fn tts_to_str(tts: &[ast::TokenTree]) -> StrBuf { - to_str(|s| s.print_tts(&tts)) + to_str(|s| s.print_tts(tts)) } pub fn stmt_to_str(stmt: &ast::Stmt) -> StrBuf { @@ -1258,28 +1258,7 @@ impl<'a> State<'a> { } ast::ExprAddrOf(m, expr) => { try!(word(&mut self.s, "&")); - - // `ExprAddrOf(ExprLit("str"))` should be `&&"str"` instead of `&"str"` - // since `&"str"` is `ExprVstore(ExprLit("str"))` which has same meaning to - // `"str"`. - // In many cases adding parentheses (`&("str")`) would help, but it become invalid - // if expr is in `PatLit()`. - let needs_extra_amp = match expr.node { - ast::ExprLit(lit) => { - match lit.node { - ast::LitStr(..) => true, - _ => false, - } - } - ast::ExprVec(..) => true, - _ => false, - }; - if needs_extra_amp { - try!(word(&mut self.s, "&")); - } - try!(self.print_mutability(m)); - try!(self.print_expr_maybe_paren(expr)); } ast::ExprLit(lit) => try!(self.print_literal(lit)), diff --git a/src/test/bench/core-set.rs b/src/test/bench/core-set.rs index b1181a3c17c5a..53b371e06cbe0 100644 --- a/src/test/bench/core-set.rs +++ b/src/test/bench/core-set.rs @@ -1,5 +1,3 @@ -// ignore-pretty - // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. @@ -10,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-pretty very bad with line comments + extern crate collections; extern crate rand; extern crate time; diff --git a/src/test/bench/shootout-k-nucleotide-pipes.rs b/src/test/bench/shootout-k-nucleotide-pipes.rs index e2bcc55d13982..04032c4aa3903 100644 --- a/src/test/bench/shootout-k-nucleotide-pipes.rs +++ b/src/test/bench/shootout-k-nucleotide-pipes.rs @@ -9,8 +9,8 @@ // except according to those terms. // ignore-android: FIXME(#10393) +// ignore-pretty very bad with line comments -// ignore-pretty the `let to_child` line gets an extra newline // multi tasking k-nucleotide extern crate collections; diff --git a/src/test/bench/shootout-k-nucleotide.rs b/src/test/bench/shootout-k-nucleotide.rs index dfa287459f33c..1434838e59bce 100644 --- a/src/test/bench/shootout-k-nucleotide.rs +++ b/src/test/bench/shootout-k-nucleotide.rs @@ -9,7 +9,6 @@ // except according to those terms. // ignore-android see #10393 #13206 -// ignore-pretty extern crate sync; diff --git a/src/test/bench/shootout-mandelbrot.rs b/src/test/bench/shootout-mandelbrot.rs index ee715aecec4fc..e17324ee59649 100644 --- a/src/test/bench/shootout-mandelbrot.rs +++ b/src/test/bench/shootout-mandelbrot.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-pretty very bad with line comments + extern crate sync; use std::io; diff --git a/src/test/bench/shootout-reverse-complement.rs b/src/test/bench/shootout-reverse-complement.rs index 4ee4f94d4354b..fdd711d22c760 100644 --- a/src/test/bench/shootout-reverse-complement.rs +++ b/src/test/bench/shootout-reverse-complement.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-pretty very bad with line comments // ignore-android doesn't terminate? -// ignore-pretty use std::iter::range_step; use std::io::{stdin, stdout, File}; diff --git a/src/test/bench/sudoku.rs b/src/test/bench/sudoku.rs index bd47734c3da89..58568282e1584 100644 --- a/src/test/bench/sudoku.rs +++ b/src/test/bench/sudoku.rs @@ -1,5 +1,3 @@ -// ignore-pretty - // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. @@ -10,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-pretty very bad with line comments + #![feature(managed_boxes)] use std::io; diff --git a/src/test/bench/task-perf-jargon-metal-smoke.rs b/src/test/bench/task-perf-jargon-metal-smoke.rs index 35c314dac93cc..442386e30586e 100644 --- a/src/test/bench/task-perf-jargon-metal-smoke.rs +++ b/src/test/bench/task-perf-jargon-metal-smoke.rs @@ -1,5 +1,3 @@ -// ignore-pretty - // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. @@ -17,6 +15,8 @@ // // The filename is a song reference; google it in quotes. +// ignore-pretty very bad with line comments + use std::comm; use std::os; use std::task; diff --git a/src/test/compile-fail/borrowck-lend-flow-match.rs b/src/test/compile-fail/borrowck-lend-flow-match.rs index ea0f5d34b72e5..6b875ff268dd8 100644 --- a/src/test/compile-fail/borrowck-lend-flow-match.rs +++ b/src/test/compile-fail/borrowck-lend-flow-match.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-pretty -- comments are unfaithfully preserved - #![allow(unused_variable)] #![allow(dead_assignment)] diff --git a/src/test/compile-fail/borrowck-pat-reassign-binding.rs b/src/test/compile-fail/borrowck-pat-reassign-binding.rs index 47f92d9f4b1a0..f33e5e9b02d59 100644 --- a/src/test/compile-fail/borrowck-pat-reassign-binding.rs +++ b/src/test/compile-fail/borrowck-pat-reassign-binding.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-pretty -- comments are unfaithfully preserved - fn main() { let mut x: Option = None; match x { diff --git a/src/test/compile-fail/borrowck-preserve-box-in-field.rs b/src/test/compile-fail/borrowck-preserve-box-in-field.rs index 168a331e9fe29..68410ae4fe196 100644 --- a/src/test/compile-fail/borrowck-preserve-box-in-field.rs +++ b/src/test/compile-fail/borrowck-preserve-box-in-field.rs @@ -1,5 +1,3 @@ -// ignore-pretty - // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. diff --git a/src/test/compile-fail/borrowck-preserve-box-in-uniq.rs b/src/test/compile-fail/borrowck-preserve-box-in-uniq.rs index d79b7e040c769..0db097ec003c2 100644 --- a/src/test/compile-fail/borrowck-preserve-box-in-uniq.rs +++ b/src/test/compile-fail/borrowck-preserve-box-in-uniq.rs @@ -1,5 +1,3 @@ -// ignore-pretty - // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. diff --git a/src/test/compile-fail/borrowck-preserve-box.rs b/src/test/compile-fail/borrowck-preserve-box.rs index 1a920c7871e1c..cd36d93060462 100644 --- a/src/test/compile-fail/borrowck-preserve-box.rs +++ b/src/test/compile-fail/borrowck-preserve-box.rs @@ -1,5 +1,3 @@ -// ignore-pretty - // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. diff --git a/src/test/compile-fail/borrowck-preserve-expl-deref.rs b/src/test/compile-fail/borrowck-preserve-expl-deref.rs index 9b7966b0af05b..ca24192e797e2 100644 --- a/src/test/compile-fail/borrowck-preserve-expl-deref.rs +++ b/src/test/compile-fail/borrowck-preserve-expl-deref.rs @@ -1,5 +1,3 @@ -// ignore-pretty - // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. diff --git a/src/test/run-pass/anon-extern-mod-cross-crate-2.rs b/src/test/run-pass/anon-extern-mod-cross-crate-2.rs index fd600907ddb2b..0ef666031114e 100644 --- a/src/test/run-pass/anon-extern-mod-cross-crate-2.rs +++ b/src/test/run-pass/anon-extern-mod-cross-crate-2.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-pretty // aux-build:anon-extern-mod-cross-crate-1.rs extern crate anonexternmod; diff --git a/src/test/run-pass/big-literals.rs b/src/test/run-pass/big-literals.rs index 9da3a7079df62..f8eaa99b5f0bc 100644 --- a/src/test/run-pass/big-literals.rs +++ b/src/test/run-pass/big-literals.rs @@ -1,5 +1,3 @@ -// ignore-pretty - // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. diff --git a/src/test/run-pass/borrowck-pat-enum.rs b/src/test/run-pass/borrowck-pat-enum.rs index ade286663a19c..7b6680294999f 100644 --- a/src/test/run-pass/borrowck-pat-enum.rs +++ b/src/test/run-pass/borrowck-pat-enum.rs @@ -1,5 +1,3 @@ -// ignore-pretty - // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. diff --git a/src/test/run-pass/closure-syntax.rs b/src/test/run-pass/closure-syntax.rs index 983cd00f39cb8..30c01ba9d5151 100644 --- a/src/test/run-pass/closure-syntax.rs +++ b/src/test/run-pass/closure-syntax.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-pretty #13324 - #![allow(dead_code)] fn foo() {} diff --git a/src/test/run-pass/deriving-global.rs b/src/test/run-pass/deriving-global.rs index 55e2615835ab4..3cd50bfff3255 100644 --- a/src/test/run-pass/deriving-global.rs +++ b/src/test/run-pass/deriving-global.rs @@ -8,18 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-pretty - does not converge - -// Copyright 2013-2014 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. - extern crate serialize; // {En,De}codable extern crate rand; // Rand diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs index 4d445db302b0b..b524527561722 100644 --- a/src/test/run-pass/ifmt.rs +++ b/src/test/run-pass/ifmt.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-pretty: `--pretty expand` creates unnecessary `unsafe` block - #![feature(macro_rules, managed_boxes)] #![deny(warnings)] #![allow(unused_must_use)] @@ -77,6 +75,7 @@ pub fn main() { t!(format!("{foo} {1} {bar} {0}", 0, 1, foo=2, bar=3), "2 1 3 0"); t!(format!("{} {0}", "a"), "a a"); t!(format!("{foo_bar}", foo_bar=1), "1"); + t!(format!("{:d}", 5 + 5), "10"); // Methods should probably work t!(format!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 0u), "c0"); diff --git a/src/test/run-pass/invoke-external-foreign.rs b/src/test/run-pass/invoke-external-foreign.rs index 2603e2d1b099c..ef5ef2f215cc2 100644 --- a/src/test/run-pass/invoke-external-foreign.rs +++ b/src/test/run-pass/invoke-external-foreign.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-pretty // aux-build:foreign_lib.rs // The purpose of this test is to check that we can diff --git a/src/test/run-pass/numeric-method-autoexport.rs b/src/test/run-pass/numeric-method-autoexport.rs index fbb404b3809d7..585ade71fc603 100644 --- a/src/test/run-pass/numeric-method-autoexport.rs +++ b/src/test/run-pass/numeric-method-autoexport.rs @@ -1,5 +1,3 @@ -// ignore-pretty - // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. @@ -10,10 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// no-pretty-expanded + // This file is intended to test only that methods are automatically // reachable for each numeric type, for each exported impl, with no imports // necessary. Testing the methods of the impls is done within the source // file for each numeric type. + pub fn main() { // ints // num diff --git a/src/test/run-pass/objects-owned-object-borrowed-method-header.rs b/src/test/run-pass/objects-owned-object-borrowed-method-header.rs index 456a5c5d29753..7752aed7236a6 100644 --- a/src/test/run-pass/objects-owned-object-borrowed-method-header.rs +++ b/src/test/run-pass/objects-owned-object-borrowed-method-header.rs @@ -1,5 +1,3 @@ -// ignore-pretty - // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. diff --git a/src/test/run-pass/super-fast-paren-parsing.rs b/src/test/run-pass/super-fast-paren-parsing.rs index 70c7200bee9ae..759b066c8dbf5 100644 --- a/src/test/run-pass/super-fast-paren-parsing.rs +++ b/src/test/run-pass/super-fast-paren-parsing.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-pretty - static a: int = ((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((( diff --git a/src/test/run-pass/trait-bounds-in-arc.rs b/src/test/run-pass/trait-bounds-in-arc.rs index 0a6e5ce0b6796..98dd3772a4f92 100644 --- a/src/test/run-pass/trait-bounds-in-arc.rs +++ b/src/test/run-pass/trait-bounds-in-arc.rs @@ -1,5 +1,3 @@ -// ignore-pretty - // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. From 042c8ae40e0bb642263d8b891ef7a0d4e81fe819 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 10 May 2014 20:16:51 -0700 Subject: [PATCH 24/33] syntax: Fix printing INT64_MIN Integers are always parsed as a u64 in libsyntax, but they're stored as i64. The parser and pretty printer both printed an i64 instead of u64, sometimes introducing an extra negative sign. --- src/librustc/util/ppaux.rs | 22 +++++++++------------- src/libsyntax/ast.rs | 6 ++++-- src/libsyntax/ast_util.rs | 21 ++++++++++++++++----- src/libsyntax/parse/token.rs | 8 +++++--- src/libsyntax/print/pprust.rs | 6 ++++-- src/test/run-pass/big-literals.rs | 1 + 6 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 5060c5572cdbb..f18f39ac925f5 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -355,18 +355,14 @@ pub fn ty_to_str(cx: &ctxt, typ: t) -> StrBuf { ty_bot => "!".to_strbuf(), ty_bool => "bool".to_strbuf(), ty_char => "char".to_strbuf(), - ty_int(t) => ast_util::int_ty_to_str(t, None), - ty_uint(t) => ast_util::uint_ty_to_str(t, None), - ty_float(t) => ast_util::float_ty_to_str(t), - ty_box(typ) => { - ("@".to_owned() + ty_to_str(cx, typ).as_slice()).to_strbuf() - } - ty_uniq(typ) => { - ("~".to_owned() + ty_to_str(cx, typ).as_slice()).to_strbuf() - } - ty_ptr(ref tm) => { - ("*".to_owned() + mt_to_str(cx, tm).as_slice()).to_strbuf() - } + ty_int(t) => ast_util::int_ty_to_str(t, None, + ast_util::AutoSuffix).to_strbuf(), + ty_uint(t) => ast_util::uint_ty_to_str(t, None, + ast_util::AutoSuffix).to_strbuf(), + ty_float(t) => ast_util::float_ty_to_str(t).to_strbuf(), + ty_box(typ) => "@".to_strbuf() + ty_to_str(cx, typ), + ty_uniq(typ) => "~".to_strbuf() + ty_to_str(cx, typ), + ty_ptr(ref tm) => "*".to_strbuf() + mt_to_str(cx, tm), ty_rptr(r, ref tm) => { let mut buf = region_ptr_to_str(cx, r); buf.push_str(mt_to_str(cx, tm).as_slice()); @@ -374,7 +370,7 @@ pub fn ty_to_str(cx: &ctxt, typ: t) -> StrBuf { } ty_tup(ref elems) => { let strs: Vec = elems.iter().map(|elem| ty_to_str(cx, *elem)).collect(); - ("(".to_owned() + strs.connect(",") + ")").to_strbuf() + ("(".to_strbuf() + strs.connect(",") + ")").to_strbuf() } ty_closure(ref f) => { closure_to_str(cx, *f) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 391116d2dbcd2..e5ef31a95a38b 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -711,7 +711,8 @@ pub enum IntTy { impl fmt::Show for IntTy { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f.buf, "{}", ast_util::int_ty_to_str(*self, None)) + write!(f.buf, "{}", + ast_util::int_ty_to_str(*self, None, ast_util::AutoSuffix)) } } @@ -726,7 +727,8 @@ pub enum UintTy { impl fmt::Show for UintTy { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f.buf, "{}", ast_util::uint_ty_to_str(*self, None)) + write!(f.buf, "{}", + ast_util::uint_ty_to_str(*self, None, ast_util::AutoSuffix)) } } diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 550b6603d5d55..74fc43e521b65 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -132,11 +132,19 @@ pub fn is_path(e: @Expr) -> bool { return match e.node { ExprPath(_) => true, _ => false }; } +pub enum SuffixMode { + ForceSuffix, + AutoSuffix, +} + // Get a string representation of a signed int type, with its value. // We want to avoid "45int" and "-3int" in favor of "45" and "-3" -pub fn int_ty_to_str(t: IntTy, val: Option) -> StrBuf { +pub fn int_ty_to_str(t: IntTy, val: Option, mode: SuffixMode) -> StrBuf { let s = match t { - TyI if val.is_some() => "", + TyI if val.is_some() => match mode { + AutoSuffix => "", + ForceSuffix => "i", + }, TyI => "int", TyI8 => "i8", TyI16 => "i16", @@ -145,7 +153,7 @@ pub fn int_ty_to_str(t: IntTy, val: Option) -> StrBuf { }; match val { - Some(n) => format!("{}{}", n, s).to_strbuf(), + Some(n) => format!("{}{}", n as u64, s).to_strbuf(), None => s.to_strbuf() } } @@ -161,9 +169,12 @@ pub fn int_ty_max(t: IntTy) -> u64 { // Get a string representation of an unsigned int type, with its value. // We want to avoid "42uint" in favor of "42u" -pub fn uint_ty_to_str(t: UintTy, val: Option) -> StrBuf { +pub fn uint_ty_to_str(t: UintTy, val: Option, mode: SuffixMode) -> StrBuf { let s = match t { - TyU if val.is_some() => "u", + TyU if val.is_some() => match mode { + AutoSuffix => "", + ForceSuffix => "u", + }, TyU => "uint", TyU8 => "u8", TyU16 => "u16", diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 3888ed6b8d1d4..68ce8cb2bc123 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -203,9 +203,11 @@ pub fn to_str(t: &Token) -> StrBuf { res.push_char('\''); res } - LIT_INT(i, t) => ast_util::int_ty_to_str(t, Some(i)), - LIT_UINT(u, t) => ast_util::uint_ty_to_str(t, Some(u)), - LIT_INT_UNSUFFIXED(i) => { i.to_str().to_strbuf() } + LIT_INT(i, t) => ast_util::int_ty_to_str(t, Some(i), + ast_util::ForceSuffix), + LIT_UINT(u, t) => ast_util::uint_ty_to_str(t, Some(u), + ast_util::ForceSuffix), + LIT_INT_UNSUFFIXED(i) => { (i as u64).to_str().to_strbuf() } LIT_FLOAT(s, t) => { let mut body = StrBuf::from_str(get_ident(s).get()); if body.as_slice().ends_with(".") { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 326f31d11e958..0b6efcd4f40bb 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2232,11 +2232,13 @@ impl<'a> State<'a> { } ast::LitInt(i, t) => { word(&mut self.s, - ast_util::int_ty_to_str(t, Some(i)).as_slice()) + ast_util::int_ty_to_str(t, Some(i), + ast_util::AutoSuffix).as_slice()) } ast::LitUint(u, t) => { word(&mut self.s, - ast_util::uint_ty_to_str(t, Some(u)).as_slice()) + ast_util::uint_ty_to_str(t, Some(u), + ast_util::AutoSuffix).as_slice()) } ast::LitIntUnsuffixed(i) => { word(&mut self.s, format!("{}", i)) diff --git a/src/test/run-pass/big-literals.rs b/src/test/run-pass/big-literals.rs index f8eaa99b5f0bc..3b2618c060d00 100644 --- a/src/test/run-pass/big-literals.rs +++ b/src/test/run-pass/big-literals.rs @@ -16,4 +16,5 @@ pub fn main() { assert_eq!(-2147483648i32 - 1i32, 2147483647i32); assert_eq!(-9223372036854775808i64 - 1i64, 9223372036854775807i64); + assert_eq!(-9223372036854775808 - 1, 9223372036854775807); } From ac1a27043a7481676502e383716e20c017122bcb Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 10 May 2014 21:27:44 -0700 Subject: [PATCH 25/33] syntax: Fix parsing << with closure types This uses the trick of replacing the << token with a < token to parse closure types correctly. Closes #13324 --- src/libsyntax/parse/parser.rs | 60 +++++++++++++++++++++++--- src/test/run-pass/borrowck-pat-enum.rs | 2 + src/test/run-pass/closure-syntax.rs | 16 ++++++- 3 files changed, 71 insertions(+), 7 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index a83bb7d1bf575..92e5f8da6aa7e 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -587,16 +587,64 @@ impl<'a> Parser<'a> { self.replace_token(token::BINOP(token::OR), lo, self.span.hi) } _ => { - let token_str = self.this_token_to_str(); - let found_token = + let found_token = self.this_token_to_str(); + let token_str = Parser::token_to_str(&token::BINOP(token::OR)); self.fatal(format!("expected `{}`, found `{}`", - found_token, - token_str)) + token_str, found_token)) } } } + // Attempt to consume a `<`. If `<<` is seen, replace it with a single + // `<` and continue. If a `<` is not seen, return false. + // + // This is meant to be used when parsing generics on a path to get the + // starting token. The `force` parameter is used to forcefully break up a + // `<<` token. If `force` is false, then `<<` is only broken when a lifetime + // shows up next. For example, consider the expression: + // + // foo as bar << test + // + // The parser needs to know if `bar <<` is the start of a generic path or if + // it's a left-shift token. If `test` were a lifetime, then it's impossible + // for the token to be a left-shift, but if it's not a lifetime, then it's + // considered a left-shift. + // + // The reason for this is that the only current ambiguity with `<<` is when + // parsing closure types: + // + // foo::<<'a> ||>(); + // impl Foo<<'a> ||>() { ... } + fn eat_lt(&mut self, force: bool) -> bool { + match self.token { + token::LT => { self.bump(); true } + token::BINOP(token::SHL) => { + let next_lifetime = self.look_ahead(1, |t| match *t { + token::LIFETIME(..) => true, + _ => false, + }); + if force || next_lifetime { + let lo = self.span.lo + BytePos(1); + self.replace_token(token::LT, lo, self.span.hi); + true + } else { + false + } + } + _ => false, + } + } + + fn expect_lt(&mut self) { + if !self.eat_lt(true) { + let found_token = self.this_token_to_str(); + let token_str = Parser::token_to_str(&token::LT); + self.fatal(format!("expected `{}`, found `{}`", + token_str, found_token)) + } + } + // Parse a sequence bracketed by `|` and `|`, stopping before the `|`. fn parse_seq_to_before_or( &mut self, @@ -1500,7 +1548,7 @@ impl<'a> Parser<'a> { // Parse the `<` before the lifetime and types, if applicable. let (any_lifetime_or_types, lifetimes, types) = { - if mode != NoTypesAllowed && self.eat(&token::LT) { + if mode != NoTypesAllowed && self.eat_lt(false) { let (lifetimes, types) = self.parse_generic_values_after_lt(); (true, lifetimes, OwnedSlice::from_vec(types)) @@ -1948,7 +1996,7 @@ impl<'a> Parser<'a> { hi = self.span.hi; self.bump(); let (_, tys) = if self.eat(&token::MOD_SEP) { - self.expect(&token::LT); + self.expect_lt(); self.parse_generic_values_after_lt() } else { (Vec::new(), Vec::new()) diff --git a/src/test/run-pass/borrowck-pat-enum.rs b/src/test/run-pass/borrowck-pat-enum.rs index 7b6680294999f..74ce8ef2e4531 100644 --- a/src/test/run-pass/borrowck-pat-enum.rs +++ b/src/test/run-pass/borrowck-pat-enum.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-pretty + fn match_ref(v: Option) -> int { match v { Some(ref i) => { diff --git a/src/test/run-pass/closure-syntax.rs b/src/test/run-pass/closure-syntax.rs index 30c01ba9d5151..2bb0e6fa19c15 100644 --- a/src/test/run-pass/closure-syntax.rs +++ b/src/test/run-pass/closure-syntax.rs @@ -43,6 +43,12 @@ fn g<'a>(a: &'a int, f: proc<'b>(&'b int) -> &'b int) -> &'a int { f(a) } +struct A; + +impl A { + fn foo(&self) {} +} + fn bar<'b>() { foo::<||>(); foo::<|| -> ()>(); @@ -58,17 +64,25 @@ fn bar<'b>() { foo::(); foo::(int, f32, &'a int):'static + Share -> &'a int>(); + foo::<<'a>||>(); + // issue #11209 let _: ||: 'b; // for comparison let _: <'a> ||; let _: Option<||:'b>; - // let _: Option<<'a>||>; + let _: Option<<'a>||>; let _: Option< <'a>||>; // issue #11210 let _: ||: 'static; + + let a = A; + a.foo::<<'a>||>(); } +struct B; +impl<'b> B<<'a>||: 'b> {} + pub fn main() { } From 560def15113d3d54b262d2b9365b6952e5e5ee3a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 10 May 2014 21:46:46 -0700 Subject: [PATCH 26/33] test: Fix a pretty printing test The pretty printer handles inlines comments quite badly --- src/test/run-pass/deriving-global.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/run-pass/deriving-global.rs b/src/test/run-pass/deriving-global.rs index 3cd50bfff3255..cbf6a1c50df1d 100644 --- a/src/test/run-pass/deriving-global.rs +++ b/src/test/run-pass/deriving-global.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate serialize; // {En,De}codable -extern crate rand; // Rand +extern crate serialize; +extern crate rand; mod submod { // if any of these are implemented without global calls for any From 1581fb8d6f17d8e58589ee880d4c946e60c95b20 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 10 May 2014 21:57:11 -0700 Subject: [PATCH 27/33] test: Ignore a pretty expanded failing test When expanding, an extra unsafe block is generated which is currently not handled well. --- src/test/run-pass/ifmt.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs index b524527561722..56d265233baf1 100644 --- a/src/test/run-pass/ifmt.rs +++ b/src/test/run-pass/ifmt.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// no-pretty-expanded unnecessary unsafe block generated + #![feature(macro_rules, managed_boxes)] #![deny(warnings)] #![allow(unused_must_use)] From d92b9ae7164a73e7c9e44a229d0e1eddbe8121cf Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 10 May 2014 21:57:49 -0700 Subject: [PATCH 28/33] syntax: Print suffixed token literals correctly Previously, literals "1i" were printed as "1". This fixes the numeric-method-autoexport test for pretty printing. --- src/libsyntax/ast_util.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 74fc43e521b65..fb69e440b2fb4 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -153,6 +153,9 @@ pub fn int_ty_to_str(t: IntTy, val: Option, mode: SuffixMode) -> StrBuf { }; match val { + // cast to a u64 so we can correctly print INT64_MIN. All integral types + // are parsed as u64, so we wouldn't want to print an extra negative + // sign. Some(n) => format!("{}{}", n as u64, s).to_strbuf(), None => s.to_strbuf() } From f912005ef34c8adfc2b0827f2d1e353e1508d3d0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 10 May 2014 22:02:59 -0700 Subject: [PATCH 29/33] test: Give a test a bigger stack for pretty printing --- src/compiletest/runtest.rs | 2 +- src/test/run-pass/super-fast-paren-parsing.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index d7fa4f209d4da..29f7a771e86fb 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -217,7 +217,7 @@ fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) { pretty_type: &str) -> ProcRes { compose_and_run(config, testfile, make_pp_args(config, props, testfile, pretty_type.to_owned()), - Vec::new(), config.compile_lib_path, Some(src)) + props.exec_env.clone(), config.compile_lib_path, Some(src)) } fn make_pp_args(config: &Config, diff --git a/src/test/run-pass/super-fast-paren-parsing.rs b/src/test/run-pass/super-fast-paren-parsing.rs index 759b066c8dbf5..1204efc29ebd3 100644 --- a/src/test/run-pass/super-fast-paren-parsing.rs +++ b/src/test/run-pass/super-fast-paren-parsing.rs @@ -8,6 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// exec-env:RUST_MIN_STACK=8000000 +// +// Big stack is needed for pretty printing, a little sad... + static a: int = ((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((( From 25ac81eb8998e08add3eba703c701a0b9a65173f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 11 May 2014 01:11:33 -0700 Subject: [PATCH 30/33] syntax: Preserve attributes in #[deriving] Now that the #[deriving] attribute is removed, the raw_pointers_deriving lint was broken. This commit restores the lint by preserving lint attributes across #[deriving] to the implementations and using #[automatically_derived] as the trigger for activating the lint. --- src/libsyntax/ext/deriving/generic.rs | 36 +++++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index 9c967cfb4eeaa..6df4da8940219 100644 --- a/src/libsyntax/ext/deriving/generic.rs +++ b/src/libsyntax/ext/deriving/generic.rs @@ -182,6 +182,7 @@ use std::cell::RefCell; use ast; use ast::{P, EnumDef, Expr, Ident, Generics, StructDef}; use ast_util; +use attr::AttrMetaMethods; use ext::base::ExtCtxt; use ext::build::AstBuilder; use codemap; @@ -330,21 +331,34 @@ impl<'a> TraitDef<'a> { _mitem: @ast::MetaItem, item: @ast::Item, push: |@ast::Item|) { - match item.node { + let newitem = match item.node { ast::ItemStruct(struct_def, ref generics) => { - push(self.expand_struct_def(cx, - struct_def, - item.ident, - generics)); + self.expand_struct_def(cx, + struct_def, + item.ident, + generics) } ast::ItemEnum(ref enum_def, ref generics) => { - push(self.expand_enum_def(cx, - enum_def, - item.ident, - generics)); + self.expand_enum_def(cx, + enum_def, + item.ident, + generics) } - _ => () - } + _ => return + }; + // Keep the lint attributes of the previous item to control how the + // generated implementations are linted + let mut attrs = newitem.attrs.clone(); + attrs.extend(item.attrs.iter().filter(|a| { + match a.name().get() { + "allow" | "warn" | "deny" | "forbid" => true, + _ => false, + } + }).map(|a| a.clone())); + push(@ast::Item { + attrs: attrs, + ..(*newitem).clone() + }) } /** From 6878039c122211f227d6c42b7f08282629ceb6c4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 11 May 2014 01:34:28 -0700 Subject: [PATCH 31/33] syntax: Improve --pretty normal slightly When printing doc comments, always put a newline after them in a macro invocation to ensure that a line-doc-comment doesn't consume remaining tokens on the line. --- src/librustc/util/ppaux.rs | 8 ++++---- src/libsyntax/print/pprust.rs | 10 ++++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index f18f39ac925f5..95ae05985d38e 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -360,9 +360,9 @@ pub fn ty_to_str(cx: &ctxt, typ: t) -> StrBuf { ty_uint(t) => ast_util::uint_ty_to_str(t, None, ast_util::AutoSuffix).to_strbuf(), ty_float(t) => ast_util::float_ty_to_str(t).to_strbuf(), - ty_box(typ) => "@".to_strbuf() + ty_to_str(cx, typ), - ty_uniq(typ) => "~".to_strbuf() + ty_to_str(cx, typ), - ty_ptr(ref tm) => "*".to_strbuf() + mt_to_str(cx, tm), + ty_box(typ) => format_strbuf!("@{}", ty_to_str(cx, typ)), + ty_uniq(typ) => format_strbuf!("~{}", ty_to_str(cx, typ)), + ty_ptr(ref tm) => format_strbuf!("*{}", mt_to_str(cx, tm)), ty_rptr(r, ref tm) => { let mut buf = region_ptr_to_str(cx, r); buf.push_str(mt_to_str(cx, tm).as_slice()); @@ -370,7 +370,7 @@ pub fn ty_to_str(cx: &ctxt, typ: t) -> StrBuf { } ty_tup(ref elems) => { let strs: Vec = elems.iter().map(|elem| ty_to_str(cx, *elem)).collect(); - ("(".to_strbuf() + strs.connect(",") + ")").to_strbuf() + format_strbuf!("({})", strs.connect(",")) } ty_closure(ref f) => { closure_to_str(cx, *f) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 0b6efcd4f40bb..15b931d58545e 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -841,7 +841,13 @@ impl<'a> State<'a> { match *tt { ast::TTDelim(ref tts) => self.print_tts(tts.as_slice()), ast::TTTok(_, ref tk) => { - word(&mut self.s, parse::token::to_str(tk).as_slice()) + try!(word(&mut self.s, parse::token::to_str(tk).as_slice())); + match *tk { + parse::token::DOC_COMMENT(..) => { + hardbreak(&mut self.s) + } + _ => Ok(()) + } } ast::TTSeq(_, ref tts, ref sep, zerok) => { try!(word(&mut self.s, "$(")); @@ -2238,7 +2244,7 @@ impl<'a> State<'a> { ast::LitUint(u, t) => { word(&mut self.s, ast_util::uint_ty_to_str(t, Some(u), - ast_util::AutoSuffix).as_slice()) + ast_util::ForceSuffix).as_slice()) } ast::LitIntUnsuffixed(i) => { word(&mut self.s, format!("{}", i)) From 9f7caed2024268f6de16f99b6696d191f3ca3228 Mon Sep 17 00:00:00 2001 From: klutzy Date: Sat, 10 May 2014 17:30:55 +0900 Subject: [PATCH 32/33] rustllvm: Add LLVMRustArrayType LLVM internally uses `uint64_t` for array size, but the corresponding C API (`LLVMArrayType`) uses `unsigned int` so ths value is truncated. Therefore rustc generates wrong type for fixed-sized large vector e.g. `[0 x i8]` for `[0u8, ..(1 << 32)]`. This patch adds `LLVMRustArrayType` function for `uint64_t` support. --- src/librustc/lib/llvm.rs | 3 +-- src/librustc/middle/trans/type_.rs | 2 +- src/rustllvm/RustWrapper.cpp | 6 ++++++ src/test/run-pass/vec-fixed-length.rs | 14 +++++++++++++- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs index e3d0e78e93873..0c874bd776ed1 100644 --- a/src/librustc/lib/llvm.rs +++ b/src/librustc/lib/llvm.rs @@ -398,8 +398,7 @@ pub mod llvm { pub fn LLVMIsPackedStruct(StructTy: TypeRef) -> Bool; /* Operations on array, pointer, and vector types (sequence types) */ - pub fn LLVMArrayType(ElementType: TypeRef, ElementCount: c_uint) - -> TypeRef; + pub fn LLVMRustArrayType(ElementType: TypeRef, ElementCount: u64) -> TypeRef; pub fn LLVMPointerType(ElementType: TypeRef, AddressSpace: c_uint) -> TypeRef; pub fn LLVMVectorType(ElementType: TypeRef, ElementCount: c_uint) diff --git a/src/librustc/middle/trans/type_.rs b/src/librustc/middle/trans/type_.rs index a0744037dc008..d5a80edfaedd1 100644 --- a/src/librustc/middle/trans/type_.rs +++ b/src/librustc/middle/trans/type_.rs @@ -207,7 +207,7 @@ impl Type { } pub fn array(ty: &Type, len: u64) -> Type { - ty!(llvm::LLVMArrayType(ty.to_ref(), len as c_uint)) + ty!(llvm::LLVMRustArrayType(ty.to_ref(), len)) } pub fn vector(ty: &Type, len: u64) -> Type { diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index ec33b750358bb..717cd333a79ce 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -754,3 +754,9 @@ LLVMRustGetSectionName(LLVMSectionIteratorRef SI, const char **ptr) { *ptr = ret.data(); return ret.size(); } + +// LLVMArrayType function does not support 64-bit ElementCount +extern "C" LLVMTypeRef +LLVMRustArrayType(LLVMTypeRef ElementType, uint64_t ElementCount) { + return wrap(ArrayType::get(unwrap(ElementType), ElementCount)); +} diff --git a/src/test/run-pass/vec-fixed-length.rs b/src/test/run-pass/vec-fixed-length.rs index 8e22be1b4325c..5116b4e746a05 100644 --- a/src/test/run-pass/vec-fixed-length.rs +++ b/src/test/run-pass/vec-fixed-length.rs @@ -8,7 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::mem::size_of; + pub fn main() { let x: [int, ..4] = [1, 2, 3, 4]; - println!("{}", x[0]); + assert_eq!(x[0], 1); + assert_eq!(x[1], 2); + assert_eq!(x[2], 3); + assert_eq!(x[3], 4); + + assert_eq!(size_of::<[u8, ..4]>(), 4u); + + // FIXME #10183 + if cfg!(target_word_size = "64") { + assert_eq!(size_of::<[u8, ..(1 << 32)]>(), (1u << 32)); + } } From f09592a5d154177f0c9d739c9fe60742ec4cd951 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 5 May 2014 16:58:42 -0700 Subject: [PATCH 33/33] io: Implement process wait timeouts This implements set_timeout() for std::io::Process which will affect wait() operations on the process. This follows the same pattern as the rest of the timeouts emerging in std::io::net. The implementation was super easy for everything except libnative on unix (backwards from usual!), which required a good bit of signal handling. There's a doc comment explaining the strategy in libnative. Internally, this also required refactoring the "helper thread" implementation used by libnative to allow for an extra helper thread (not just the timer). This is a breaking change in terms of the io::Process API. It is now possible for wait() to fail, and subsequently wait_with_output(). These two functions now return IoResult due to the fact that they can time out. Additionally, the wait_with_output() function has moved from taking `&mut self` to taking `self`. If a timeout occurs while waiting with output, the semantics are undesirable in almost all cases if attempting to re-wait on the process. Equivalent functionality can still be achieved by dealing with the output handles manually. [breaking-change] cc #13523 --- src/compiletest/procsrv.rs | 7 +- src/compiletest/runtest.rs | 7 +- src/libcore/str.rs | 1 + src/liblibc/lib.rs | 18 +- src/libnative/io/c_unix.rs | 109 ++++++ src/libnative/io/helper_thread.rs | 205 ++++++++++ src/libnative/io/mod.rs | 4 +- src/libnative/io/process.rs | 363 +++++++++++++++--- src/libnative/io/timer_helper.rs | 149 ------- src/libnative/io/timer_unix.rs | 20 +- src/libnative/io/timer_win32.rs | 20 +- src/libnative/lib.rs | 1 + src/librustc/back/archive.rs | 4 +- src/librustuv/process.rs | 81 +++- src/libstd/io/process.rs | 190 ++++++--- src/libstd/rt/rtio.rs | 3 +- src/test/run-pass/backtrace.rs | 8 +- src/test/run-pass/core-run-destroy.rs | 2 +- src/test/run-pass/issue-13304.rs | 2 +- src/test/run-pass/logging-separate-lines.rs | 2 +- src/test/run-pass/process-detach.rs | 2 +- .../process-spawn-with-unicode-params.rs | 2 +- .../run-pass/sigpipe-should-be-ignored.rs | 2 +- 23 files changed, 876 insertions(+), 326 deletions(-) create mode 100644 src/libnative/io/helper_thread.rs delete mode 100644 src/libnative/io/timer_helper.rs diff --git a/src/compiletest/procsrv.rs b/src/compiletest/procsrv.rs index 7d3aa33aae853..d3642a939db04 100644 --- a/src/compiletest/procsrv.rs +++ b/src/compiletest/procsrv.rs @@ -68,7 +68,7 @@ pub fn run(lib_path: &str, input: Option<~str>) -> Option { let env = env.clone().append(target_env(lib_path, prog).as_slice()); - let mut opt_process = Process::configure(ProcessConfig { + let opt_process = Process::configure(ProcessConfig { program: prog, args: args, env: Some(env.as_slice()), @@ -76,11 +76,12 @@ pub fn run(lib_path: &str, }); match opt_process { - Ok(ref mut process) => { + Ok(mut process) => { for input in input.iter() { process.stdin.get_mut_ref().write(input.as_bytes()).unwrap(); } - let ProcessOutput { status, output, error } = process.wait_with_output(); + let ProcessOutput { status, output, error } = + process.wait_with_output().unwrap(); Some(Result { status: status, diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 29f7a771e86fb..d18f1370a9f47 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -502,7 +502,7 @@ fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path) let args = &[lldb_batchmode_script, test_executable_str, debugger_script_str]; let env = &[("PYTHONPATH".to_owned(), config.lldb_python_dir.clone().unwrap())]; - let mut opt_process = Process::configure(ProcessConfig { + let opt_process = Process::configure(ProcessConfig { program: "python", args: args, env: Some(env), @@ -510,8 +510,9 @@ fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path) }); let (status, out, err) = match opt_process { - Ok(ref mut process) => { - let ProcessOutput { status, output, error } = process.wait_with_output(); + Ok(process) => { + let ProcessOutput { status, output, error } = + process.wait_with_output().unwrap(); (status, str::from_utf8(output.as_slice()).unwrap().to_owned(), diff --git a/src/libcore/str.rs b/src/libcore/str.rs index e677c4880b58d..1481759297868 100644 --- a/src/libcore/str.rs +++ b/src/libcore/str.rs @@ -1725,6 +1725,7 @@ impl<'a> StrSlice<'a> for &'a str { #[inline] fn is_char_boundary(&self, index: uint) -> bool { if index == self.len() { return true; } + if index > self.len() { return false; } let b = self[index]; return b < 128u8 || b >= 192u8; } diff --git a/src/liblibc/lib.rs b/src/liblibc/lib.rs index a35ebb06437d9..1edd99c1d7dcc 100644 --- a/src/liblibc/lib.rs +++ b/src/liblibc/lib.rs @@ -173,7 +173,7 @@ pub use funcs::bsd43::{shutdown}; #[cfg(unix)] pub use consts::os::posix88::{EADDRINUSE, ENOENT, EISDIR, EAGAIN, EWOULDBLOCK}; #[cfg(unix)] pub use consts::os::posix88::{ECANCELED, SIGINT, EINPROGRESS}; #[cfg(unix)] pub use consts::os::posix88::{SIGTERM, SIGKILL, SIGPIPE, PROT_NONE}; -#[cfg(unix)] pub use consts::os::posix01::{SIG_IGN, WNOHANG}; +#[cfg(unix)] pub use consts::os::posix01::{SIG_IGN}; #[cfg(unix)] pub use consts::os::bsd44::{AF_UNIX}; #[cfg(unix)] pub use types::os::common::posix01::{pthread_t, timespec, timezone}; @@ -2473,8 +2473,6 @@ pub mod consts { pub static CLOCK_REALTIME: c_int = 0; pub static CLOCK_MONOTONIC: c_int = 1; - - pub static WNOHANG: c_int = 1; } pub mod posix08 { } @@ -2924,8 +2922,6 @@ pub mod consts { pub static CLOCK_REALTIME: c_int = 0; pub static CLOCK_MONOTONIC: c_int = 4; - - pub static WNOHANG: c_int = 1; } pub mod posix08 { } @@ -3313,8 +3309,6 @@ pub mod consts { pub static PTHREAD_CREATE_JOINABLE: c_int = 1; pub static PTHREAD_CREATE_DETACHED: c_int = 2; pub static PTHREAD_STACK_MIN: size_t = 8192; - - pub static WNOHANG: c_int = 1; } pub mod posix08 { } @@ -3980,16 +3974,6 @@ pub mod funcs { } } - pub mod wait { - use types::os::arch::c95::{c_int}; - use types::os::arch::posix88::{pid_t}; - - extern { - pub fn waitpid(pid: pid_t, status: *mut c_int, options: c_int) - -> pid_t; - } - } - pub mod glob { use types::os::arch::c95::{c_char, c_int}; use types::os::common::posix01::{glob_t}; diff --git a/src/libnative/io/c_unix.rs b/src/libnative/io/c_unix.rs index abb22476e5240..767090a10cda2 100644 --- a/src/libnative/io/c_unix.rs +++ b/src/libnative/io/c_unix.rs @@ -10,7 +10,12 @@ //! C definitions used by libnative that don't belong in liblibc +#![allow(dead_code)] + pub use self::select::fd_set; +pub use self::signal::{sigaction, siginfo, sigset_t}; +pub use self::signal::{SA_ONSTACK, SA_RESTART, SA_RESETHAND, SA_NOCLDSTOP}; +pub use self::signal::{SA_NODEFER, SA_NOCLDWAIT, SA_SIGINFO, SIGCHLD}; use libc; @@ -34,6 +39,8 @@ pub static MSG_DONTWAIT: libc::c_int = 0x80; #[cfg(target_os = "android")] pub static MSG_DONTWAIT: libc::c_int = 0x40; +pub static WNOHANG: libc::c_int = 1; + extern { pub fn gettimeofday(timeval: *mut libc::timeval, tzp: *libc::c_void) -> libc::c_int; @@ -49,6 +56,17 @@ extern { optlen: *mut libc::socklen_t) -> libc::c_int; pub fn ioctl(fd: libc::c_int, req: libc::c_ulong, ...) -> libc::c_int; + + pub fn waitpid(pid: libc::pid_t, status: *mut libc::c_int, + options: libc::c_int) -> libc::pid_t; + + pub fn sigaction(signum: libc::c_int, + act: *sigaction, + oldact: *mut sigaction) -> libc::c_int; + + pub fn sigaddset(set: *mut sigset_t, signum: libc::c_int) -> libc::c_int; + pub fn sigdelset(set: *mut sigset_t, signum: libc::c_int) -> libc::c_int; + pub fn sigemptyset(set: *mut sigset_t) -> libc::c_int; } #[cfg(target_os = "macos")] @@ -81,3 +99,94 @@ mod select { set.fds_bits[fd / uint::BITS] |= 1 << (fd % uint::BITS); } } + +#[cfg(target_os = "linux")] +#[cfg(target_os = "android")] +mod signal { + use libc; + + pub static SA_NOCLDSTOP: libc::c_ulong = 0x00000001; + pub static SA_NOCLDWAIT: libc::c_ulong = 0x00000002; + pub static SA_NODEFER: libc::c_ulong = 0x40000000; + pub static SA_ONSTACK: libc::c_ulong = 0x08000000; + pub static SA_RESETHAND: libc::c_ulong = 0x80000000; + pub static SA_RESTART: libc::c_ulong = 0x10000000; + pub static SA_SIGINFO: libc::c_ulong = 0x00000004; + pub static SIGCHLD: libc::c_int = 17; + + // This definition is not as accurate as it could be, {pid, uid, status} is + // actually a giant union. Currently we're only interested in these fields, + // however. + pub struct siginfo { + si_signo: libc::c_int, + si_errno: libc::c_int, + si_code: libc::c_int, + pub pid: libc::pid_t, + pub uid: libc::uid_t, + pub status: libc::c_int, + } + + pub struct sigaction { + pub sa_handler: extern fn(libc::c_int), + pub sa_mask: sigset_t, + pub sa_flags: libc::c_ulong, + sa_restorer: *mut libc::c_void, + } + + #[cfg(target_word_size = "32")] + pub struct sigset_t { + __val: [libc::c_ulong, ..32], + } + #[cfg(target_word_size = "64")] + pub struct sigset_t { + __val: [libc::c_ulong, ..16], + } +} + +#[cfg(target_os = "macos")] +#[cfg(target_os = "freebsd")] +mod signal { + use libc; + + pub static SA_ONSTACK: libc::c_int = 0x0001; + pub static SA_RESTART: libc::c_int = 0x0002; + pub static SA_RESETHAND: libc::c_int = 0x0004; + pub static SA_NOCLDSTOP: libc::c_int = 0x0008; + pub static SA_NODEFER: libc::c_int = 0x0010; + pub static SA_NOCLDWAIT: libc::c_int = 0x0020; + pub static SA_SIGINFO: libc::c_int = 0x0040; + pub static SIGCHLD: libc::c_int = 20; + + #[cfg(target_os = "macos")] + pub type sigset_t = u32; + #[cfg(target_os = "freebsd")] + pub struct sigset_t { + bits: [u32, ..4], + } + + // This structure has more fields, but we're not all that interested in + // them. + pub struct siginfo { + pub si_signo: libc::c_int, + pub si_errno: libc::c_int, + pub si_code: libc::c_int, + pub pid: libc::pid_t, + pub uid: libc::uid_t, + pub status: libc::c_int, + } + + #[cfg(target_os = "macos")] + pub struct sigaction { + pub sa_handler: extern fn(libc::c_int), + sa_tramp: *mut libc::c_void, + pub sa_mask: sigset_t, + pub sa_flags: libc::c_int, + } + + #[cfg(target_os = "freebsd")] + pub struct sigaction { + pub sa_handler: extern fn(libc::c_int), + pub sa_flags: libc::c_int, + pub sa_mask: sigset_t, + } +} diff --git a/src/libnative/io/helper_thread.rs b/src/libnative/io/helper_thread.rs new file mode 100644 index 0000000000000..2260d74e16177 --- /dev/null +++ b/src/libnative/io/helper_thread.rs @@ -0,0 +1,205 @@ +// Copyright 2013-2014 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. + +//! Implementation of the helper thread for the timer module +//! +//! This module contains the management necessary for the timer worker thread. +//! This thread is responsible for performing the send()s on channels for timers +//! that are using channels instead of a blocking call. +//! +//! The timer thread is lazily initialized, and it's shut down via the +//! `shutdown` function provided. It must be maintained as an invariant that +//! `shutdown` is only called when the entire program is finished. No new timers +//! can be created in the future and there must be no active timers at that +//! time. + +#![macro_escape] + +use std::mem; +use std::rt::bookkeeping; +use std::rt; +use std::ty::Unsafe; +use std::unstable::mutex::StaticNativeMutex; + +use task; + +/// A structure for management of a helper thread. +/// +/// This is generally a static structure which tracks the lifetime of a helper +/// thread. +/// +/// The fields of this helper are all public, but they should not be used, this +/// is for static initialization. +pub struct Helper { + /// Internal lock which protects the remaining fields + pub lock: StaticNativeMutex, + + // You'll notice that the remaining fields are Unsafe, and this is + // because all helper thread operations are done through &self, but we need + // these to be mutable (once `lock` is held). + + /// Lazily allocated channel to send messages to the helper thread. + pub chan: Unsafe<*mut Sender>, + + /// OS handle used to wake up a blocked helper thread + pub signal: Unsafe, + + /// Flag if this helper thread has booted and been initialized yet. + pub initialized: Unsafe, +} + +macro_rules! helper_init( (static mut $name:ident: Helper<$m:ty>) => ( + static mut $name: Helper<$m> = Helper { + lock: ::std::unstable::mutex::NATIVE_MUTEX_INIT, + chan: ::std::ty::Unsafe { + value: 0 as *mut Sender<$m>, + marker1: ::std::kinds::marker::InvariantType, + }, + signal: ::std::ty::Unsafe { + value: 0, + marker1: ::std::kinds::marker::InvariantType, + }, + initialized: ::std::ty::Unsafe { + value: false, + marker1: ::std::kinds::marker::InvariantType, + }, + }; +) ) + +impl Helper { + /// Lazily boots a helper thread, becoming a no-op if the helper has already + /// been spawned. + /// + /// This function will check to see if the thread has been initialized, and + /// if it has it returns quickly. If initialization has not happened yet, + /// the closure `f` will be run (inside of the initialization lock) and + /// passed to the helper thread in a separate task. + /// + /// This function is safe to be called many times. + pub fn boot(&'static self, + f: || -> T, + helper: fn(imp::signal, Receiver, T)) { + unsafe { + let _guard = self.lock.lock(); + if !*self.initialized.get() { + let (tx, rx) = channel(); + *self.chan.get() = mem::transmute(box tx); + let (receive, send) = imp::new(); + *self.signal.get() = send as uint; + + let t = f(); + task::spawn(proc() { + bookkeeping::decrement(); + helper(receive, rx, t); + self.lock.lock().signal() + }); + + rt::at_exit(proc() { self.shutdown() }); + *self.initialized.get() = true; + } + } + } + + /// Sends a message to a spawned worker thread. + /// + /// This is only valid if the worker thread has previously booted + pub fn send(&'static self, msg: M) { + unsafe { + let _guard = self.lock.lock(); + + // Must send and *then* signal to ensure that the child receives the + // message. Otherwise it could wake up and go to sleep before we + // send the message. + assert!(!self.chan.get().is_null()); + (**self.chan.get()).send(msg); + imp::signal(*self.signal.get() as imp::signal); + } + } + + fn shutdown(&'static self) { + unsafe { + // Shut down, but make sure this is done inside our lock to ensure + // that we'll always receive the exit signal when the thread + // returns. + let guard = self.lock.lock(); + + // Close the channel by destroying it + let chan: Box> = mem::transmute(*self.chan.get()); + *self.chan.get() = 0 as *mut Sender; + drop(chan); + imp::signal(*self.signal.get() as imp::signal); + + // Wait for the child to exit + guard.wait(); + drop(guard); + + // Clean up after ourselves + self.lock.destroy(); + imp::close(*self.signal.get() as imp::signal); + *self.signal.get() = 0; + } + } +} + +#[cfg(unix)] +mod imp { + use libc; + use std::os; + + use io::file::FileDesc; + + pub type signal = libc::c_int; + + pub fn new() -> (signal, signal) { + let pipe = os::pipe(); + (pipe.input, pipe.out) + } + + pub fn signal(fd: libc::c_int) { + FileDesc::new(fd, false).inner_write([0]).unwrap(); + } + + pub fn close(fd: libc::c_int) { + let _fd = FileDesc::new(fd, true); + } +} + +#[cfg(windows)] +mod imp { + use libc::{BOOL, LPCSTR, HANDLE, LPSECURITY_ATTRIBUTES, CloseHandle}; + use std::ptr; + use libc; + + pub type signal = HANDLE; + + pub fn new() -> (HANDLE, HANDLE) { + unsafe { + let handle = CreateEventA(ptr::mut_null(), libc::FALSE, libc::FALSE, + ptr::null()); + (handle, handle) + } + } + + pub fn signal(handle: HANDLE) { + assert!(unsafe { SetEvent(handle) != 0 }); + } + + pub fn close(handle: HANDLE) { + assert!(unsafe { CloseHandle(handle) != 0 }); + } + + extern "system" { + fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES, + bManualReset: BOOL, + bInitialState: BOOL, + lpName: LPCSTR) -> HANDLE; + fn SetEvent(hEvent: HANDLE) -> BOOL; + } +} diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs index f2c2c66e1425f..a9aca656319ef 100644 --- a/src/libnative/io/mod.rs +++ b/src/libnative/io/mod.rs @@ -40,6 +40,8 @@ use ai = std::io::net::addrinfo; pub use self::file::FileDesc; pub use self::process::Process; +mod helper_thread; + // Native I/O implementations pub mod addrinfo; pub mod net; @@ -75,8 +77,6 @@ pub mod pipe; #[cfg(unix)] #[path = "c_unix.rs"] mod c; #[cfg(windows)] #[path = "c_win32.rs"] mod c; -mod timer_helper; - pub type IoResult = Result; fn unimpl() -> IoError { diff --git a/src/libnative/io/process.rs b/src/libnative/io/process.rs index 14ea1f12a5ca0..799db64e68897 100644 --- a/src/libnative/io/process.rs +++ b/src/libnative/io/process.rs @@ -8,20 +8,27 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::io; use libc::{pid_t, c_void, c_int}; use libc; +use std::io; +use std::mem; use std::os; use std::ptr; use std::rt::rtio; use p = std::io::process; + use super::IoResult; use super::file; +use super::util; -#[cfg(windows)] use std::mem; #[cfg(windows)] use std::strbuf::StrBuf; -#[cfg(not(windows))] use super::retry; +#[cfg(unix)] use super::c; +#[cfg(unix)] use super::retry; +#[cfg(unix)] use io::helper_thread::Helper; + +#[cfg(unix)] +helper_init!(static mut HELPER: Helper) /** * A value representing a child process. @@ -44,6 +51,14 @@ pub struct Process { /// Manually delivered signal exit_signal: Option, + + /// Deadline after which wait() will return + deadline: u64, +} + +#[cfg(unix)] +enum Req { + NewChild(libc::pid_t, Sender, u64), } impl Process { @@ -116,6 +131,7 @@ impl Process { handle: res.handle, exit_code: None, exit_signal: None, + deadline: 0, }, ret_io)) } @@ -131,11 +147,15 @@ impl Process { impl rtio::RtioProcess for Process { fn id(&self) -> pid_t { self.pid } - fn wait(&mut self) -> p::ProcessExit { + fn set_timeout(&mut self, timeout: Option) { + self.deadline = timeout.map(|i| i + ::io::timer::now()).unwrap_or(0); + } + + fn wait(&mut self) -> IoResult { match self.exit_code { - Some(code) => code, + Some(code) => Ok(code), None => { - let code = waitpid(self.pid); + let code = try!(waitpid(self.pid, self.deadline)); // On windows, waitpid will never return a signal. If a signal // was successfully delivered to the process, however, we can // consider it as having died via a signal. @@ -145,7 +165,7 @@ impl rtio::RtioProcess for Process { Some(..) => code, }; self.exit_code = Some(code); - code + Ok(code) } } } @@ -762,61 +782,301 @@ fn translate_status(status: c_int) -> p::ProcessExit { * operate on a none-existent process or, even worse, on a newer process * with the same id. */ -fn waitpid(pid: pid_t) -> p::ProcessExit { - return waitpid_os(pid); - - #[cfg(windows)] - fn waitpid_os(pid: pid_t) -> p::ProcessExit { - use libc::types::os::arch::extra::DWORD; - use libc::consts::os::extra::{ - SYNCHRONIZE, - PROCESS_QUERY_INFORMATION, - FALSE, - STILL_ACTIVE, - INFINITE, - WAIT_FAILED - }; - use libc::funcs::extra::kernel32::{ - OpenProcess, - GetExitCodeProcess, - CloseHandle, - WaitForSingleObject - }; +#[cfg(windows)] +fn waitpid(pid: pid_t, deadline: u64) -> IoResult { + use libc::types::os::arch::extra::DWORD; + use libc::consts::os::extra::{ + SYNCHRONIZE, + PROCESS_QUERY_INFORMATION, + FALSE, + STILL_ACTIVE, + INFINITE, + WAIT_TIMEOUT, + WAIT_OBJECT_0, + }; + use libc::funcs::extra::kernel32::{ + OpenProcess, + GetExitCodeProcess, + CloseHandle, + WaitForSingleObject, + }; - unsafe { + unsafe { + let process = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, + FALSE, + pid as DWORD); + if process.is_null() { + return Err(super::last_error()) + } - let process = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, - FALSE, - pid as DWORD); - if process.is_null() { - fail!("failure in OpenProcess: {}", os::last_os_error()); + loop { + let mut status = 0; + if GetExitCodeProcess(process, &mut status) == FALSE { + let err = Err(super::last_error()); + assert!(CloseHandle(process) != 0); + return err; } - - loop { - let mut status = 0; - if GetExitCodeProcess(process, &mut status) == FALSE { - assert!(CloseHandle(process) != 0); - fail!("failure in GetExitCodeProcess: {}", os::last_os_error()); - } - if status != STILL_ACTIVE { + if status != STILL_ACTIVE { + assert!(CloseHandle(process) != 0); + return Ok(p::ExitStatus(status as int)); + } + let interval = if deadline == 0 { + INFINITE + } else { + let now = ::io::timer::now(); + if deadline < now {0} else {(deadline - now) as u32} + }; + match WaitForSingleObject(process, interval) { + WAIT_OBJECT_0 => {} + WAIT_TIMEOUT => { assert!(CloseHandle(process) != 0); - return p::ExitStatus(status as int); + return Err(util::timeout("process wait timed out")) } - if WaitForSingleObject(process, INFINITE) == WAIT_FAILED { + _ => { + let err = Err(super::last_error()); assert!(CloseHandle(process) != 0); - fail!("failure in WaitForSingleObject: {}", os::last_os_error()); + return err } } } } +} - #[cfg(unix)] - fn waitpid_os(pid: pid_t) -> p::ProcessExit { - use libc::funcs::posix01::wait; - let mut status = 0 as c_int; - match retry(|| unsafe { wait::waitpid(pid, &mut status, 0) }) { +#[cfg(unix)] +fn waitpid(pid: pid_t, deadline: u64) -> IoResult { + use std::cmp; + use std::comm; + + static mut WRITE_FD: libc::c_int = 0; + + let mut status = 0 as c_int; + if deadline == 0 { + return match retry(|| unsafe { c::waitpid(pid, &mut status, 0) }) { -1 => fail!("unknown waitpid error: {}", super::last_error()), - _ => translate_status(status), + _ => Ok(translate_status(status)), + } + } + + // On unix, wait() and its friends have no timeout parameters, so there is + // no way to time out a thread in wait(). From some googling and some + // thinking, it appears that there are a few ways to handle timeouts in + // wait(), but the only real reasonable one for a multi-threaded program is + // to listen for SIGCHLD. + // + // With this in mind, the waiting mechanism with a timeout barely uses + // waitpid() at all. There are a few times that waitpid() is invoked with + // WNOHANG, but otherwise all the necessary blocking is done by waiting for + // a SIGCHLD to arrive (and that blocking has a timeout). Note, however, + // that waitpid() is still used to actually reap the child. + // + // Signal handling is super tricky in general, and this is no exception. Due + // to the async nature of SIGCHLD, we use the self-pipe trick to transmit + // data out of the signal handler to the rest of the application. The first + // idea would be to have each thread waiting with a timeout to read this + // output file descriptor, but a write() is akin to a signal(), not a + // broadcast(), so it would only wake up one thread, and possibly the wrong + // thread. Hence a helper thread is used. + // + // The helper thread here is responsible for farming requests for a + // waitpid() with a timeout, and then processing all of the wait requests. + // By guaranteeing that only this helper thread is reading half of the + // self-pipe, we're sure that we'll never lose a SIGCHLD. This helper thread + // is also responsible for select() to wait for incoming messages or + // incoming SIGCHLD messages, along with passing an appropriate timeout to + // select() to wake things up as necessary. + // + // The ordering of the following statements is also very purposeful. First, + // we must be guaranteed that the helper thread is booted and available to + // receive SIGCHLD signals, and then we must also ensure that we do a + // nonblocking waitpid() at least once before we go ask the sigchld helper. + // This prevents the race where the child exits, we boot the helper, and + // then we ask for the child's exit status (never seeing a sigchld). + // + // The actual communication between the helper thread and this thread is + // quite simple, just a channel moving data around. + + unsafe { HELPER.boot(register_sigchld, waitpid_helper) } + + match waitpid_nowait(pid) { + Some(ret) => return Ok(ret), + None => {} + } + + let (tx, rx) = channel(); + unsafe { HELPER.send(NewChild(pid, tx, deadline)); } + return match rx.recv_opt() { + Ok(e) => Ok(e), + Err(()) => Err(util::timeout("wait timed out")), + }; + + // Register a new SIGCHLD handler, returning the reading half of the + // self-pipe plus the old handler registered (return value of sigaction). + fn register_sigchld() -> (libc::c_int, c::sigaction) { + unsafe { + let mut old: c::sigaction = mem::init(); + let mut new: c::sigaction = mem::init(); + new.sa_handler = sigchld_handler; + new.sa_flags = c::SA_NOCLDSTOP; + assert_eq!(c::sigaction(c::SIGCHLD, &new, &mut old), 0); + + let mut pipes = [0, ..2]; + assert_eq!(libc::pipe(pipes.as_mut_ptr()), 0); + util::set_nonblocking(pipes[0], true).unwrap(); + util::set_nonblocking(pipes[1], true).unwrap(); + WRITE_FD = pipes[1]; + (pipes[0], old) + } + } + + // Helper thread for processing SIGCHLD messages + fn waitpid_helper(input: libc::c_int, + messages: Receiver, + (read_fd, old): (libc::c_int, c::sigaction)) { + util::set_nonblocking(input, true).unwrap(); + let mut set: c::fd_set = unsafe { mem::init() }; + let mut tv: libc::timeval; + let mut active = Vec::<(libc::pid_t, Sender, u64)>::new(); + let max = cmp::max(input, read_fd) + 1; + + 'outer: loop { + // Figure out the timeout of our syscall-to-happen. If we're waiting + // for some processes, then they'll have a timeout, otherwise we + // wait indefinitely for a message to arrive. + // + // FIXME: sure would be nice to not have to scan the entire array + let min = active.iter().map(|a| *a.ref2()).enumerate().min_by(|p| { + p.val1() + }); + let (p, idx) = match min { + Some((idx, deadline)) => { + let now = ::io::timer::now(); + let ms = if now < deadline {deadline - now} else {0}; + tv = util::ms_to_timeval(ms); + (&tv as *_, idx) + } + None => (ptr::null(), -1), + }; + + // Wait for something to happen + c::fd_set(&mut set, input); + c::fd_set(&mut set, read_fd); + match unsafe { c::select(max, &set, ptr::null(), ptr::null(), p) } { + // interrupted, retry + -1 if os::errno() == libc::EINTR as int => continue, + + // We read something, break out and process + 1 | 2 => {} + + // Timeout, the pending request is removed + 0 => { + drop(active.remove(idx)); + continue + } + + n => fail!("error in select {} ({})", os::errno(), n), + } + + // Process any pending messages + if drain(input) { + loop { + match messages.try_recv() { + Ok(NewChild(pid, tx, deadline)) => { + active.push((pid, tx, deadline)); + } + Err(comm::Disconnected) => { + assert!(active.len() == 0); + break 'outer; + } + Err(comm::Empty) => break, + } + } + } + + // If a child exited (somehow received SIGCHLD), then poll all + // children to see if any of them exited. + // + // We also attempt to be responsible netizens when dealing with + // SIGCHLD by invoking any previous SIGCHLD handler instead of just + // ignoring any previous SIGCHLD handler. Note that we don't provide + // a 1:1 mapping of our handler invocations to the previous handler + // invocations because we drain the `read_fd` entirely. This is + // probably OK because the kernel is already allowed to coalesce + // simultaneous signals, we're just doing some extra coalescing. + // + // Another point of note is that this likely runs the signal handler + // on a different thread than the one that received the signal. I + // *think* this is ok at this time. + // + // The main reason for doing this is to allow stdtest to run native + // tests as well. Both libgreen and libnative are running around + // with process timeouts, but libgreen should get there first + // (currently libuv doesn't handle old signal handlers). + if drain(read_fd) { + let i: uint = unsafe { mem::transmute(old.sa_handler) }; + if i != 0 { + assert!(old.sa_flags & c::SA_SIGINFO == 0); + (old.sa_handler)(c::SIGCHLD); + } + + // FIXME: sure would be nice to not have to scan the entire + // array... + active.retain(|&(pid, ref tx, _)| { + match waitpid_nowait(pid) { + Some(msg) => { tx.send(msg); false } + None => true, + } + }); + } + } + + // Once this helper thread is done, we re-register the old sigchld + // handler and close our intermediate file descriptors. + unsafe { + assert_eq!(c::sigaction(c::SIGCHLD, &old, ptr::mut_null()), 0); + let _ = libc::close(read_fd); + let _ = libc::close(WRITE_FD); + WRITE_FD = -1; + } + } + + // Drain all pending data from the file descriptor, returning if any data + // could be drained. This requires that the file descriptor is in + // nonblocking mode. + fn drain(fd: libc::c_int) -> bool { + let mut ret = false; + loop { + let mut buf = [0u8, ..1]; + match unsafe { + libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, + buf.len() as libc::size_t) + } { + n if n > 0 => { ret = true; } + 0 => return true, + -1 if util::wouldblock() => return ret, + n => fail!("bad read {} ({})", os::last_os_error(), n), + } + } + } + + // Signal handler for SIGCHLD signals, must be async-signal-safe! + // + // This function will write to the writing half of the "self pipe" to wake + // up the helper thread if it's waiting. Note that this write must be + // nonblocking because if it blocks and the reader is the thread we + // interrupted, then we'll deadlock. + // + // When writing, if the write returns EWOULDBLOCK then we choose to ignore + // it. At that point we're guaranteed that there's something in the pipe + // which will wake up the other end at some point, so we just allow this + // signal to be coalesced with the pending signals on the pipe. + extern fn sigchld_handler(_signum: libc::c_int) { + let mut msg = 1; + match unsafe { + libc::write(WRITE_FD, &mut msg as *mut _ as *libc::c_void, 1) + } { + 1 => {} + -1 if util::wouldblock() => {} // see above comments + n => fail!("bad error on write fd: {} {}", n, os::errno()), } } } @@ -830,10 +1090,9 @@ fn waitpid_nowait(pid: pid_t) -> Option { #[cfg(unix)] fn waitpid_os(pid: pid_t) -> Option { - use libc::funcs::posix01::wait; let mut status = 0 as c_int; match retry(|| unsafe { - wait::waitpid(pid, &mut status, libc::WNOHANG) + c::waitpid(pid, &mut status, c::WNOHANG) }) { n if n == pid => Some(translate_status(status)), 0 => None, diff --git a/src/libnative/io/timer_helper.rs b/src/libnative/io/timer_helper.rs deleted file mode 100644 index 95b2620f3c798..0000000000000 --- a/src/libnative/io/timer_helper.rs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2013-2014 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. - -//! Implementation of the helper thread for the timer module -//! -//! This module contains the management necessary for the timer worker thread. -//! This thread is responsible for performing the send()s on channels for timers -//! that are using channels instead of a blocking call. -//! -//! The timer thread is lazily initialized, and it's shut down via the -//! `shutdown` function provided. It must be maintained as an invariant that -//! `shutdown` is only called when the entire program is finished. No new timers -//! can be created in the future and there must be no active timers at that -//! time. - -use std::mem; -use std::rt::bookkeeping; -use std::rt; -use std::unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; - -use io::timer::{Req, Shutdown}; -use task; - -// You'll note that these variables are *not* protected by a lock. These -// variables are initialized with a Once before any Timer is created and are -// only torn down after everything else has exited. This means that these -// variables are read-only during use (after initialization) and both of which -// are safe to use concurrently. -static mut HELPER_CHAN: *mut Sender = 0 as *mut Sender; -static mut HELPER_SIGNAL: imp::signal = 0 as imp::signal; - -static mut TIMER_HELPER_EXIT: StaticNativeMutex = NATIVE_MUTEX_INIT; - -pub fn boot(helper: fn(imp::signal, Receiver)) { - static mut LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; - static mut INITIALIZED: bool = false; - - unsafe { - let mut _guard = LOCK.lock(); - if !INITIALIZED { - let (tx, rx) = channel(); - // promote this to a shared channel - drop(tx.clone()); - HELPER_CHAN = mem::transmute(box tx); - let (receive, send) = imp::new(); - HELPER_SIGNAL = send; - - task::spawn(proc() { - bookkeeping::decrement(); - helper(receive, rx); - TIMER_HELPER_EXIT.lock().signal() - }); - - rt::at_exit(proc() { shutdown() }); - INITIALIZED = true; - } - } -} - -pub fn send(req: Req) { - unsafe { - assert!(!HELPER_CHAN.is_null()); - (*HELPER_CHAN).send(req); - imp::signal(HELPER_SIGNAL); - } -} - -fn shutdown() { - // Request a shutdown, and then wait for the task to exit - unsafe { - let guard = TIMER_HELPER_EXIT.lock(); - send(Shutdown); - guard.wait(); - drop(guard); - TIMER_HELPER_EXIT.destroy(); - } - - - // Clean up after ther helper thread - unsafe { - imp::close(HELPER_SIGNAL); - let _chan: Box> = mem::transmute(HELPER_CHAN); - HELPER_CHAN = 0 as *mut Sender; - HELPER_SIGNAL = 0 as imp::signal; - } -} - -#[cfg(unix)] -mod imp { - use libc; - use std::os; - - use io::file::FileDesc; - - pub type signal = libc::c_int; - - pub fn new() -> (signal, signal) { - let pipe = os::pipe(); - (pipe.input, pipe.out) - } - - pub fn signal(fd: libc::c_int) { - FileDesc::new(fd, false).inner_write([0]).unwrap(); - } - - pub fn close(fd: libc::c_int) { - let _fd = FileDesc::new(fd, true); - } -} - -#[cfg(windows)] -mod imp { - use libc::{BOOL, LPCSTR, HANDLE, LPSECURITY_ATTRIBUTES, CloseHandle}; - use std::ptr; - use libc; - - pub type signal = HANDLE; - - pub fn new() -> (HANDLE, HANDLE) { - unsafe { - let handle = CreateEventA(ptr::mut_null(), libc::FALSE, libc::FALSE, - ptr::null()); - (handle, handle) - } - } - - pub fn signal(handle: HANDLE) { - assert!(unsafe { SetEvent(handle) != 0 }); - } - - pub fn close(handle: HANDLE) { - assert!(unsafe { CloseHandle(handle) != 0 }); - } - - extern "system" { - fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES, - bManualReset: BOOL, - bInitialState: BOOL, - lpName: LPCSTR) -> HANDLE; - fn SetEvent(hEvent: HANDLE) -> BOOL; - } -} diff --git a/src/libnative/io/timer_unix.rs b/src/libnative/io/timer_unix.rs index e008e6fb9e905..2c5b798482777 100644 --- a/src/libnative/io/timer_unix.rs +++ b/src/libnative/io/timer_unix.rs @@ -52,11 +52,14 @@ use std::os; use std::ptr; use std::rt::rtio; use std::sync::atomics; +use std::comm; use io::IoResult; use io::c; use io::file::FileDesc; -use io::timer_helper; +use io::helper_thread::Helper; + +helper_init!(static mut HELPER: Helper) pub struct Timer { id: uint, @@ -79,9 +82,6 @@ pub enum Req { // Remove a timer based on its id and then send it back on the channel // provided RemoveTimer(uint, Sender>), - - // Shut down the loop and then ACK this channel once it's shut down - Shutdown, } // returns the current time (in milliseconds) @@ -93,7 +93,7 @@ pub fn now() -> u64 { } } -fn helper(input: libc::c_int, messages: Receiver) { +fn helper(input: libc::c_int, messages: Receiver, _: ()) { let mut set: c::fd_set = unsafe { mem::init() }; let mut fd = FileDesc::new(input, true); @@ -163,7 +163,7 @@ fn helper(input: libc::c_int, messages: Receiver) { 1 => { loop { match messages.try_recv() { - Ok(Shutdown) => { + Err(comm::Disconnected) => { assert!(active.len() == 0); break 'outer; } @@ -202,7 +202,7 @@ fn helper(input: libc::c_int, messages: Receiver) { impl Timer { pub fn new() -> IoResult { - timer_helper::boot(helper); + unsafe { HELPER.boot(|| {}, helper); } static mut ID: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT; let id = unsafe { ID.fetch_add(1, atomics::Relaxed) }; @@ -235,7 +235,7 @@ impl Timer { Some(i) => i, None => { let (tx, rx) = channel(); - timer_helper::send(RemoveTimer(self.id, tx)); + unsafe { HELPER.send(RemoveTimer(self.id, tx)); } rx.recv() } } @@ -261,7 +261,7 @@ impl rtio::RtioTimer for Timer { inner.interval = msecs; inner.target = now + msecs; - timer_helper::send(NewTimer(inner)); + unsafe { HELPER.send(NewTimer(inner)); } return rx; } @@ -275,7 +275,7 @@ impl rtio::RtioTimer for Timer { inner.interval = msecs; inner.target = now + msecs; - timer_helper::send(NewTimer(inner)); + unsafe { HELPER.send(NewTimer(inner)); } return rx; } } diff --git a/src/libnative/io/timer_win32.rs b/src/libnative/io/timer_win32.rs index 15e6e62421a5b..e7130de05c26d 100644 --- a/src/libnative/io/timer_win32.rs +++ b/src/libnative/io/timer_win32.rs @@ -23,10 +23,13 @@ use libc; use std::ptr; use std::rt::rtio; +use std::comm; -use io::timer_helper; +use io::helper_thread::Helper; use io::IoResult; +helper_init!(static mut HELPER: Helper) + pub struct Timer { obj: libc::HANDLE, on_worker: bool, @@ -35,10 +38,9 @@ pub struct Timer { pub enum Req { NewTimer(libc::HANDLE, Sender<()>, bool), RemoveTimer(libc::HANDLE, Sender<()>), - Shutdown, } -fn helper(input: libc::HANDLE, messages: Receiver) { +fn helper(input: libc::HANDLE, messages: Receiver, _: ()) { let mut objs = vec![input]; let mut chans = vec![]; @@ -67,12 +69,12 @@ fn helper(input: libc::HANDLE, messages: Receiver) { None => {} } } - Ok(Shutdown) => { + Err(comm::Disconnected) => { assert_eq!(objs.len(), 1); assert_eq!(chans.len(), 0); break 'outer; } - _ => break + Err(..) => break } } } else { @@ -102,7 +104,7 @@ pub fn now() -> u64 { impl Timer { pub fn new() -> IoResult { - timer_helper::boot(helper); + unsafe { HELPER.boot(|| {}, helper) } let obj = unsafe { imp::CreateWaitableTimerA(ptr::mut_null(), 0, ptr::null()) @@ -124,7 +126,7 @@ impl Timer { if !self.on_worker { return } let (tx, rx) = channel(); - timer_helper::send(RemoveTimer(self.obj, tx)); + unsafe { HELPER.send(RemoveTimer(self.obj, tx)) } rx.recv(); self.on_worker = false; @@ -157,7 +159,7 @@ impl rtio::RtioTimer for Timer { ptr::mut_null(), 0) }, 1); - timer_helper::send(NewTimer(self.obj, tx, true)); + unsafe { HELPER.send(NewTimer(self.obj, tx, true)) } self.on_worker = true; return rx; } @@ -173,7 +175,7 @@ impl rtio::RtioTimer for Timer { ptr::null(), ptr::mut_null(), 0) }, 1); - timer_helper::send(NewTimer(self.obj, tx, false)); + unsafe { HELPER.send(NewTimer(self.obj, tx, false)) } self.on_worker = true; return rx; diff --git a/src/libnative/lib.rs b/src/libnative/lib.rs index 05cf415ec78b0..8ba0613336924 100644 --- a/src/libnative/lib.rs +++ b/src/libnative/lib.rs @@ -55,6 +55,7 @@ // NB this crate explicitly does *not* allow glob imports, please seriously // consider whether they're needed before adding that feature here (the // answer is that you don't need them) +#![feature(macro_rules)] extern crate libc; diff --git a/src/librustc/back/archive.rs b/src/librustc/back/archive.rs index 67140ac1920e3..04ce4f88831f4 100644 --- a/src/librustc/back/archive.rs +++ b/src/librustc/back/archive.rs @@ -54,8 +54,8 @@ fn run_ar(sess: &Session, args: &str, cwd: Option<&Path>, cwd: cwd.map(|a| &*a), .. ProcessConfig::new() }) { - Ok(mut prog) => { - let o = prog.wait_with_output(); + Ok(prog) => { + let o = prog.wait_with_output().unwrap(); if !o.status.success() { sess.err(format!("{} {} failed with: {}", ar, args.connect(" "), o.status)); diff --git a/src/librustuv/process.rs b/src/librustuv/process.rs index d671e20868c5c..7afac6801519b 100644 --- a/src/librustuv/process.rs +++ b/src/librustuv/process.rs @@ -19,7 +19,8 @@ use std::rt::task::BlockedTask; use homing::{HomingIO, HomeHandle}; use pipe::PipeWatcher; use super::{UvHandle, UvError, uv_error_to_io_error, - wait_until_woken_after, wakeup}; + wait_until_woken_after, wakeup, Loop}; +use timer::TimerWatcher; use uvio::UvIoFactory; use uvll; @@ -32,6 +33,16 @@ pub struct Process { /// Collected from the exit_cb exit_status: Option, + + /// Lazily initialized timeout timer + timer: Option>, + timeout_state: TimeoutState, +} + +enum TimeoutState { + NoTimeout, + TimeoutPending, + TimeoutElapsed, } impl Process { @@ -92,6 +103,8 @@ impl Process { home: io_loop.make_handle(), to_wake: None, exit_status: None, + timer: None, + timeout_state: NoTimeout, }; match unsafe { uvll::uv_spawn(io_loop.uv_loop(), handle, &options) @@ -223,21 +236,71 @@ impl RtioProcess for Process { } } - fn wait(&mut self) -> process::ProcessExit { + fn wait(&mut self) -> Result { // Make sure (on the home scheduler) that we have an exit status listed let _m = self.fire_homing_missile(); match self.exit_status { - Some(..) => {} - None => { - // If there's no exit code previously listed, then the - // process's exit callback has yet to be invoked. We just - // need to deschedule ourselves and wait to be reawoken. + Some(status) => return Ok(status), + None => {} + } + + // If there's no exit code previously listed, then the process's exit + // callback has yet to be invoked. We just need to deschedule ourselves + // and wait to be reawoken. + match self.timeout_state { + NoTimeout | TimeoutPending => { wait_until_woken_after(&mut self.to_wake, &self.uv_loop(), || {}); - assert!(self.exit_status.is_some()); } + TimeoutElapsed => {} + } + + // If there's still no exit status listed, then we timed out, and we + // need to return. + match self.exit_status { + Some(status) => Ok(status), + None => Err(uv_error_to_io_error(UvError(uvll::ECANCELED))) + } + } + + fn set_timeout(&mut self, timeout: Option) { + let _m = self.fire_homing_missile(); + self.timeout_state = NoTimeout; + let ms = match timeout { + Some(ms) => ms, + None => { + match self.timer { + Some(ref mut timer) => timer.stop(), + None => {} + } + return + } + }; + if self.timer.is_none() { + let loop_ = Loop::wrap(unsafe { + uvll::get_loop_for_uv_handle(self.uv_handle()) + }); + let mut timer = box TimerWatcher::new_home(&loop_, self.home().clone()); + unsafe { + timer.set_data(self as *mut _ as *Process); + } + self.timer = Some(timer); } - self.exit_status.unwrap() + let timer = self.timer.get_mut_ref(); + timer.stop(); + timer.start(timer_cb, ms, 0); + self.timeout_state = TimeoutPending; + + extern fn timer_cb(timer: *uvll::uv_timer_t) { + let p: &mut Process = unsafe { + &mut *(uvll::get_data_for_uv_handle(timer) as *mut Process) + }; + p.timeout_state = TimeoutElapsed; + match p.to_wake.take() { + Some(task) => { let _t = task.wake().map(|t| t.reawaken()); } + None => {} + } + } } } diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs index 529fd25dc50e9..349cac723ff5b 100644 --- a/src/libstd/io/process.rs +++ b/src/libstd/io/process.rs @@ -10,6 +10,8 @@ //! Bindings for executing child processes +#![allow(experimental)] + use prelude::*; use fmt; @@ -50,7 +52,7 @@ use rt::rtio::{RtioProcess, IoFactory, LocalIo}; /// }; /// /// let contents = child.stdout.get_mut_ref().read_to_end(); -/// assert!(child.wait().success()); +/// assert!(child.wait().unwrap().success()); /// ``` pub struct Process { handle: Box, @@ -284,7 +286,7 @@ impl Process { /// println!("stderr: {}", str::from_utf8_lossy(output.error.as_slice())); /// ``` pub fn output(prog: &str, args: &[~str]) -> IoResult { - Process::new(prog, args).map(|mut p| p.wait_with_output()) + Process::new(prog, args).and_then(|p| p.wait_with_output()) } /// Executes a child process and collects its exit status. This will block @@ -303,7 +305,7 @@ impl Process { /// println!("process exited with: {}", status); /// ``` pub fn status(prog: &str, args: &[~str]) -> IoResult { - Process::new(prog, args).map(|mut p| p.wait()) + Process::new(prog, args).and_then(|mut p| p.wait()) } /// Creates a new process with the specified configuration. @@ -378,17 +380,72 @@ impl Process { /// after it has been called at least once. /// /// The stdin handle to the child process will be closed before waiting. - pub fn wait(&mut self) -> ProcessExit { + /// + /// # Errors + /// + /// This function can fail if a timeout was previously specified via + /// `set_timeout` and the timeout expires before the child exits. + pub fn wait(&mut self) -> IoResult { drop(self.stdin.take()); self.handle.wait() } + /// Sets a timeout, in milliseconds, for future calls to wait(). + /// + /// The argument specified is a relative distance into the future, in + /// milliseconds, after which any call to wait() will return immediately + /// with a timeout error, and all future calls to wait() will not block. + /// + /// A value of `None` will clear any previous timeout, and a value of `Some` + /// will override any previously set timeout. + /// + /// # Example + /// + /// ```no_run + /// # #![allow(experimental)] + /// use std::io::process::{Process, ProcessExit}; + /// use std::io::IoResult; + /// + /// fn run_gracefully(prog: &str) -> IoResult { + /// let mut p = try!(Process::new("long-running-process", [])); + /// + /// // give the process 10 seconds to finish completely + /// p.set_timeout(Some(10_000)); + /// match p.wait() { + /// Ok(status) => return Ok(status), + /// Err(..) => {} + /// } + /// + /// // Attempt to exit gracefully, but don't wait for it too long + /// try!(p.signal_exit()); + /// p.set_timeout(Some(1_000)); + /// match p.wait() { + /// Ok(status) => return Ok(status), + /// Err(..) => {} + /// } + /// + /// // Well, we did our best, forcefully kill the process + /// try!(p.signal_kill()); + /// p.set_timeout(None); + /// p.wait() + /// } + /// ``` + #[experimental = "the type of the timeout is likely to change"] + pub fn set_timeout(&mut self, timeout_ms: Option) { + self.handle.set_timeout(timeout_ms) + } + /// Simultaneously wait for the child to exit and collect all remaining /// output on the stdout/stderr handles, returning a `ProcessOutput` /// instance. /// /// The stdin handle to the child is closed before waiting. - pub fn wait_with_output(&mut self) -> ProcessOutput { + /// + /// # Errors + /// + /// This function can fail for any of the same reasons that `wait()` can + /// fail. + pub fn wait_with_output(mut self) -> IoResult { drop(self.stdin.take()); fn read(stream: Option) -> Receiver>> { let (tx, rx) = channel(); @@ -404,11 +461,13 @@ impl Process { let stdout = read(self.stdout.take()); let stderr = read(self.stderr.take()); - let status = self.wait(); + let status = try!(self.wait()); - ProcessOutput { status: status, - output: stdout.recv().ok().unwrap_or(Vec::new()), - error: stderr.recv().ok().unwrap_or(Vec::new()) } + Ok(ProcessOutput { + status: status, + output: stdout.recv().ok().unwrap_or(Vec::new()), + error: stderr.recv().ok().unwrap_or(Vec::new()), + }) } } @@ -421,7 +480,8 @@ impl Drop for Process { drop(self.stderr.take()); drop(mem::replace(&mut self.extra_io, Vec::new())); - self.wait(); + self.set_timeout(None); + let _ = self.wait().unwrap(); } } @@ -441,7 +501,7 @@ mod tests { let p = Process::configure(args); assert!(p.is_ok()); let mut p = p.unwrap(); - assert!(p.wait().success()); + assert!(p.wait().unwrap().success()); }) #[cfg(not(target_os="android"))] @@ -465,7 +525,7 @@ mod tests { let p = Process::configure(args); assert!(p.is_ok()); let mut p = p.unwrap(); - assert!(p.wait().matches_exit_status(1)); + assert!(p.wait().unwrap().matches_exit_status(1)); drop(p.wait().clone()); }) @@ -479,7 +539,7 @@ mod tests { let p = Process::configure(args); assert!(p.is_ok()); let mut p = p.unwrap(); - match p.wait() { + match p.wait().unwrap() { process::ExitSignal(1) => {}, result => fail!("not terminated by signal 1 (instead, {})", result), } @@ -495,7 +555,7 @@ mod tests { let mut p = p.unwrap(); assert!(p.stdout.is_some()); let ret = read_all(p.stdout.get_mut_ref() as &mut Reader); - assert!(p.wait().success()); + assert!(p.wait().unwrap().success()); return ret; } @@ -536,7 +596,7 @@ mod tests { p.stdin.get_mut_ref().write("foobar".as_bytes()).unwrap(); drop(p.stdin.take()); let out = read_all(p.stdout.get_mut_ref() as &mut Reader); - assert!(p.wait().success()); + assert!(p.wait().unwrap().success()); assert_eq!(out, "foobar\n".to_owned()); }) @@ -548,7 +608,7 @@ mod tests { .. ProcessConfig::new() }; let mut p = Process::configure(args).unwrap(); - assert!(p.wait().success()); + assert!(p.wait().unwrap().success()); }) #[cfg(windows)] @@ -572,7 +632,7 @@ mod tests { .. ProcessConfig::new() }; let mut p = Process::configure(args).unwrap(); - assert!(p.wait().success()); + assert!(p.wait().unwrap().success()); }) #[cfg(unix, not(target_os="android"))] @@ -635,21 +695,21 @@ mod tests { #[cfg(not(target_os="android"))] iotest!(fn test_finish_once() { let mut prog = Process::new("false", []).unwrap(); - assert!(prog.wait().matches_exit_status(1)); + assert!(prog.wait().unwrap().matches_exit_status(1)); }) #[cfg(not(target_os="android"))] iotest!(fn test_finish_twice() { let mut prog = Process::new("false", []).unwrap(); - assert!(prog.wait().matches_exit_status(1)); - assert!(prog.wait().matches_exit_status(1)); + assert!(prog.wait().unwrap().matches_exit_status(1)); + assert!(prog.wait().unwrap().matches_exit_status(1)); }) #[cfg(not(target_os="android"))] iotest!(fn test_wait_with_output_once() { - let mut prog = Process::new("echo", ["hello".to_owned()]).unwrap(); - let ProcessOutput {status, output, error} = prog.wait_with_output(); + let prog = Process::new("echo", ["hello".to_owned()]).unwrap(); + let ProcessOutput {status, output, error} = prog.wait_with_output().unwrap(); let output_str = str::from_utf8(output.as_slice()).unwrap(); assert!(status.success()); @@ -660,30 +720,6 @@ mod tests { } }) - #[cfg(not(target_os="android"))] - iotest!(fn test_wait_with_output_twice() { - let mut prog = Process::new("echo", ["hello".to_owned()]).unwrap(); - let ProcessOutput {status, output, error} = prog.wait_with_output(); - - let output_str = str::from_utf8(output.as_slice()).unwrap(); - - assert!(status.success()); - assert_eq!(output_str.trim().to_owned(), "hello".to_owned()); - // FIXME #7224 - if !running_on_valgrind() { - assert_eq!(error, Vec::new()); - } - - let ProcessOutput {status, output, error} = prog.wait_with_output(); - - assert!(status.success()); - assert_eq!(output, Vec::new()); - // FIXME #7224 - if !running_on_valgrind() { - assert_eq!(error, Vec::new()); - } - }) - #[cfg(unix,not(target_os="android"))] pub fn run_pwd(dir: Option<&Path>) -> Process { Process::configure(ProcessConfig { @@ -714,9 +750,10 @@ mod tests { iotest!(fn test_keep_current_working_dir() { use os; - let mut prog = run_pwd(None); + let prog = run_pwd(None); - let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned(); + let output = str::from_utf8(prog.wait_with_output().unwrap() + .output.as_slice()).unwrap().to_owned(); let parent_dir = os::getcwd(); let child_dir = Path::new(output.trim()); @@ -732,9 +769,10 @@ mod tests { // test changing to the parent of os::getcwd() because we know // the path exists (and os::getcwd() is not expected to be root) let parent_dir = os::getcwd().dir_path(); - let mut prog = run_pwd(Some(&parent_dir)); + let prog = run_pwd(Some(&parent_dir)); - let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned(); + let output = str::from_utf8(prog.wait_with_output().unwrap() + .output.as_slice()).unwrap().to_owned(); let child_dir = Path::new(output.trim()); let parent_stat = parent_dir.stat().unwrap(); @@ -777,8 +815,9 @@ mod tests { use os; if running_on_valgrind() { return; } - let mut prog = run_env(None); - let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned(); + let prog = run_env(None); + let output = str::from_utf8(prog.wait_with_output().unwrap() + .output.as_slice()).unwrap().to_owned(); let r = os::env(); for &(ref k, ref v) in r.iter() { @@ -791,8 +830,10 @@ mod tests { use os; if running_on_valgrind() { return; } - let mut prog = run_env(None); - let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned(); + let prog = run_env(None); + let output = str::from_utf8(prog.wait_with_output() + .unwrap().output.as_slice()) + .unwrap().to_owned(); let r = os::env(); for &(ref k, ref v) in r.iter() { @@ -807,8 +848,8 @@ mod tests { iotest!(fn test_add_to_env() { let new_env = box [("RUN_TEST_NEW_ENV".to_owned(), "123".to_owned())]; - let mut prog = run_env(Some(new_env)); - let result = prog.wait_with_output(); + let prog = run_env(Some(new_env)); + let result = prog.wait_with_output().unwrap(); let output = str::from_utf8_lossy(result.output.as_slice()).into_owned(); assert!(output.contains("RUN_TEST_NEW_ENV=123"), @@ -830,14 +871,14 @@ mod tests { iotest!(fn test_kill() { let mut p = sleeper(); Process::kill(p.id(), PleaseExitSignal).unwrap(); - assert!(!p.wait().success()); + assert!(!p.wait().unwrap().success()); }) iotest!(fn test_exists() { let mut p = sleeper(); assert!(Process::kill(p.id(), 0).is_ok()); p.signal_kill().unwrap(); - assert!(!p.wait().success()); + assert!(!p.wait().unwrap().success()); }) iotest!(fn test_zero() { @@ -845,11 +886,42 @@ mod tests { p.signal_kill().unwrap(); for _ in range(0, 20) { if p.signal(0).is_err() { - assert!(!p.wait().success()); + assert!(!p.wait().unwrap().success()); return } timer::sleep(100); } fail!("never saw the child go away"); }) + + iotest!(fn wait_timeout() { + let mut p = sleeper(); + p.set_timeout(Some(10)); + assert_eq!(p.wait().err().unwrap().kind, TimedOut); + assert_eq!(p.wait().err().unwrap().kind, TimedOut); + p.signal_kill().unwrap(); + p.set_timeout(None); + assert!(p.wait().is_ok()); + }) + + iotest!(fn wait_timeout2() { + let (tx, rx) = channel(); + let tx2 = tx.clone(); + spawn(proc() { + let mut p = sleeper(); + p.set_timeout(Some(10)); + assert_eq!(p.wait().err().unwrap().kind, TimedOut); + p.signal_kill().unwrap(); + tx.send(()); + }); + spawn(proc() { + let mut p = sleeper(); + p.set_timeout(Some(10)); + assert_eq!(p.wait().err().unwrap().kind, TimedOut); + p.signal_kill().unwrap(); + tx2.send(()); + }); + rx.recv(); + rx.recv(); + }) } diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index d23d327d55881..90f97e59caada 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -275,7 +275,8 @@ pub trait RtioFileStream { pub trait RtioProcess { fn id(&self) -> libc::pid_t; fn kill(&mut self, signal: int) -> IoResult<()>; - fn wait(&mut self) -> ProcessExit; + fn wait(&mut self) -> IoResult; + fn set_timeout(&mut self, timeout: Option); } pub trait RtioPipe { diff --git a/src/test/run-pass/backtrace.rs b/src/test/run-pass/backtrace.rs index 989453d8570d1..53fe91cff3745 100644 --- a/src/test/run-pass/backtrace.rs +++ b/src/test/run-pass/backtrace.rs @@ -50,7 +50,7 @@ fn runtest(me: &str) { env: Some(env.as_slice()), .. ProcessConfig::new() }).unwrap(); - let out = p.wait_with_output(); + let out = p.wait_with_output().unwrap(); assert!(!out.status.success()); let s = str::from_utf8(out.error.as_slice()).unwrap(); assert!(s.contains("stack backtrace") && s.contains("foo::h"), @@ -62,7 +62,7 @@ fn runtest(me: &str) { args: ["fail".to_owned()], .. ProcessConfig::new() }).unwrap(); - let out = p.wait_with_output(); + let out = p.wait_with_output().unwrap(); assert!(!out.status.success()); let s = str::from_utf8(out.error.as_slice()).unwrap(); assert!(!s.contains("stack backtrace") && !s.contains("foo::h"), @@ -74,7 +74,7 @@ fn runtest(me: &str) { args: ["double-fail".to_owned()], .. ProcessConfig::new() }).unwrap(); - let out = p.wait_with_output(); + let out = p.wait_with_output().unwrap(); assert!(!out.status.success()); let s = str::from_utf8(out.error.as_slice()).unwrap(); assert!(s.contains("stack backtrace") && s.contains("double::h"), @@ -87,7 +87,7 @@ fn runtest(me: &str) { env: Some(env.as_slice()), .. ProcessConfig::new() }).unwrap(); - let out = p.wait_with_output(); + let out = p.wait_with_output().unwrap(); assert!(!out.status.success()); let s = str::from_utf8(out.error.as_slice()).unwrap(); let mut i = 0; diff --git a/src/test/run-pass/core-run-destroy.rs b/src/test/run-pass/core-run-destroy.rs index 83d3b51f74a4f..01a71d862b4ff 100644 --- a/src/test/run-pass/core-run-destroy.rs +++ b/src/test/run-pass/core-run-destroy.rs @@ -120,7 +120,7 @@ pub fn test_destroy_actually_kills(force: bool) { () = rx1.recv() => {} } }); - match p.wait() { + match p.wait().unwrap() { ExitStatus(..) => fail!("expected a signal"), ExitSignal(..) => tx.send(()), } diff --git a/src/test/run-pass/issue-13304.rs b/src/test/run-pass/issue-13304.rs index f66b943d85f64..fc1825d22cd94 100644 --- a/src/test/run-pass/issue-13304.rs +++ b/src/test/run-pass/issue-13304.rs @@ -52,7 +52,7 @@ fn parent(flavor: ~str) { let args = args.as_slice(); let mut p = io::Process::new(args[0].as_slice(), ["child".to_owned(), flavor]).unwrap(); p.stdin.get_mut_ref().write_str("test1\ntest2\ntest3").unwrap(); - let out = p.wait_with_output(); + let out = p.wait_with_output().unwrap(); assert!(out.status.success()); let s = str::from_utf8(out.output.as_slice()).unwrap(); assert_eq!(s, "test1\n\ntest2\n\ntest3\n"); diff --git a/src/test/run-pass/logging-separate-lines.rs b/src/test/run-pass/logging-separate-lines.rs index a5e632b94a288..f87c22bdb57c3 100644 --- a/src/test/run-pass/logging-separate-lines.rs +++ b/src/test/run-pass/logging-separate-lines.rs @@ -36,7 +36,7 @@ fn main() { env: Some(env.as_slice()), ..ProcessConfig::new() }; - let p = Process::configure(config).unwrap().wait_with_output(); + let p = Process::configure(config).unwrap().wait_with_output().unwrap(); assert!(p.status.success()); let mut lines = str::from_utf8(p.error.as_slice()).unwrap().lines(); assert!(lines.next().unwrap().contains("foo")); diff --git a/src/test/run-pass/process-detach.rs b/src/test/run-pass/process-detach.rs index 2a814956631d4..f41f2619032bc 100644 --- a/src/test/run-pass/process-detach.rs +++ b/src/test/run-pass/process-detach.rs @@ -54,7 +54,7 @@ fn main() { // Wait for the child process to die (terminate it's stdin and the read // should fail). drop(p.stdin.take()); - match p.wait() { + match p.wait().unwrap() { process::ExitStatus(..) => {} process::ExitSignal(..) => fail!() } diff --git a/src/test/run-pass/process-spawn-with-unicode-params.rs b/src/test/run-pass/process-spawn-with-unicode-params.rs index f9839ed39e752..45af7d5de3481 100644 --- a/src/test/run-pass/process-spawn-with-unicode-params.rs +++ b/src/test/run-pass/process-spawn-with-unicode-params.rs @@ -62,7 +62,7 @@ fn main() { cwd: Some(&cwd), env: Some(my_env.append_one(env).as_slice()), .. ProcessConfig::new() - }).unwrap().wait_with_output(); + }).unwrap().wait_with_output().unwrap(); // display the output assert!(io::stdout().write(p.output.as_slice()).is_ok()); diff --git a/src/test/run-pass/sigpipe-should-be-ignored.rs b/src/test/run-pass/sigpipe-should-be-ignored.rs index 34d1f5e66c678..2b42e3ada5428 100644 --- a/src/test/run-pass/sigpipe-should-be-ignored.rs +++ b/src/test/run-pass/sigpipe-should-be-ignored.rs @@ -31,5 +31,5 @@ fn main() { } let mut p = Process::new(args[0], ["test".to_owned()]).unwrap(); - assert!(p.wait().success()); + assert!(p.wait().unwrap().success()); }