diff --git a/src/doc/reference.md b/src/doc/reference.md index 64ddb3ffdd39..326946837bf6 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2432,6 +2432,8 @@ The currently implemented features of the reference compiler are: * `simd` - Allows use of the `#[simd]` attribute, which is overly simple and not the SIMD interface we want to expose in the long term. +* `staged_api` - Allows usage of stability markers and `#![staged_api]` in a crate + * `struct_inherit` - Allows using struct inheritance, which is barely implemented and will probably be removed. Don't use this. @@ -2459,6 +2461,11 @@ The currently implemented features of the reference compiler are: which is considered wildly unsafe and will be obsoleted by language improvements. +* `unmarked_api` - Allows use of items within a `#![staged_api]` crate + which have not been marked with a stability marker. + Such items should not be allowed by the compiler to exist, + so if you need this there probably is a compiler bug. + * `associated_types` - Allows type aliases in traits. Experimental. If a feature is promoted to a language feature, then all existing programs will diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 504b58d8ad10..340a8d59612f 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -45,22 +45,18 @@ #![stable(feature = "rust1", since = "1.0.0")] +use core::prelude::*; + use core::any::Any; -use core::clone::Clone; -use core::cmp::{PartialEq, PartialOrd, Eq, Ord, Ordering}; +use core::cmp::Ordering; use core::default::Default; use core::error::{Error, FromError}; use core::fmt; use core::hash::{self, Hash}; -use core::iter::Iterator; -use core::marker::Sized; use core::mem; use core::ops::{Deref, DerefMut}; -use core::option::Option; use core::ptr::Unique; use core::raw::TraitObject; -use core::result::Result::{Ok, Err}; -use core::result::Result; /// A value that represents the heap. This is the default place that the `box` keyword allocates /// into when no place is supplied. @@ -296,18 +292,20 @@ impl DerefMut for Box { fn deref_mut(&mut self) -> &mut T { &mut **self } } -impl<'a, T> Iterator for Box + 'a> { - type Item = T; - - fn next(&mut self) -> Option { - (**self).next() - } - - fn size_hint(&self) -> (usize, Option) { - (**self).size_hint() - } +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Box { + type Item = I::Item; + fn next(&mut self) -> Option { (**self).next() } + fn size_hint(&self) -> (usize, Option) { (**self).size_hint() } } +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for Box { + fn next_back(&mut self) -> Option { (**self).next_back() } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Box {} +#[stable(feature = "rust1", since = "1.0.0")] impl<'a, E: Error + 'a> FromError for Box { fn from_error(err: E) -> Box { Box::new(err) diff --git a/src/libcollections/vec_map.rs b/src/libcollections/vec_map.rs index c74c870eef5d..abcf358a1926 100644 --- a/src/libcollections/vec_map.rs +++ b/src/libcollections/vec_map.rs @@ -13,6 +13,8 @@ #![allow(missing_docs)] +pub use self::Entry::*; + use core::prelude::*; use core::cmp::Ordering; @@ -66,6 +68,32 @@ pub struct VecMap { v: Vec>, } +/// A view into a single entry in a map, which may either be vacant or occupied. +#[unstable(feature = "collections", + reason = "precise API still under development")] +pub enum Entry<'a, V:'a> { + /// A vacant Entry + Vacant(VacantEntry<'a, V>), + /// An occupied Entry + Occupied(OccupiedEntry<'a, V>), +} + +/// A vacant Entry. +#[unstable(feature = "collections", + reason = "precise API still under development")] +pub struct VacantEntry<'a, V:'a> { + map: &'a mut VecMap, + index: usize, +} + +/// An occupied Entry. +#[unstable(feature = "collections", + reason = "precise API still under development")] +pub struct OccupiedEntry<'a, V:'a> { + map: &'a mut VecMap, + index: usize, +} + #[stable(feature = "rust1", since = "1.0.0")] impl Default for VecMap { #[stable(feature = "rust1", since = "1.0.0")] @@ -485,6 +513,119 @@ impl VecMap { let result = &mut self.v[*key]; result.take() } + + /// Gets the given key's corresponding entry in the map for in-place manipulation. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecMap; + /// use std::collections::vec_map::Entry; + /// + /// let mut count: VecMap = VecMap::new(); + /// + /// // count the number of occurrences of numbers in the vec + /// for x in vec![1, 2, 1, 2, 3, 4, 1, 2, 4].iter() { + /// match count.entry(*x) { + /// Entry::Vacant(view) => { + /// view.insert(1); + /// }, + /// Entry::Occupied(mut view) => { + /// let v = view.get_mut(); + /// *v += 1; + /// }, + /// } + /// } + /// + /// assert_eq!(count[1], 3); + /// ``` + #[unstable(feature = "collections", + reason = "precise API still under development")] + pub fn entry(&mut self, key: usize) -> Entry { + // FIXME(Gankro): this is basically the dumbest implementation of + // entry possible, because weird non-lexical borrows issues make it + // completely insane to do any other way. That said, Entry is a border-line + // useless construct on VecMap, so it's hardly a big loss. + if self.contains_key(&key) { + Occupied(OccupiedEntry { + map: self, + index: key, + }) + } else { + Vacant(VacantEntry { + map: self, + index: key, + }) + } + } +} + + +impl<'a, V> Entry<'a, V> { + #[unstable(feature = "collections", + reason = "matches collection reform v2 specification, waiting for dust to settle")] + /// Returns a mutable reference to the entry if occupied, or the VacantEntry if vacant + pub fn get(self) -> Result<&'a mut V, VacantEntry<'a, V>> { + match self { + Occupied(entry) => Ok(entry.into_mut()), + Vacant(entry) => Err(entry), + } + } +} + +impl<'a, V> VacantEntry<'a, V> { + /// Sets the value of the entry with the VacantEntry's key, + /// and returns a mutable reference to it. + #[unstable(feature = "collections", + reason = "matches collection reform v2 specification, waiting for dust to settle")] + pub fn insert(self, value: V) -> &'a mut V { + let index = self.index; + self.map.insert(index, value); + &mut self.map[index] + } +} + +impl<'a, V> OccupiedEntry<'a, V> { + /// Gets a reference to the value in the entry. + #[unstable(feature = "collections", + reason = "matches collection reform v2 specification, waiting for dust to settle")] + pub fn get(&self) -> &V { + let index = self.index; + &self.map[index] + } + + /// Gets a mutable reference to the value in the entry. + #[unstable(feature = "collections", + reason = "matches collection reform v2 specification, waiting for dust to settle")] + pub fn get_mut(&mut self) -> &mut V { + let index = self.index; + &mut self.map[index] + } + + /// Converts the entry into a mutable reference to its value. + #[unstable(feature = "collections", + reason = "matches collection reform v2 specification, waiting for dust to settle")] + pub fn into_mut(self) -> &'a mut V { + let index = self.index; + &mut self.map[index] + } + + /// Sets the value of the entry with the OccupiedEntry's key, + /// and returns the entry's old value. + #[unstable(feature = "collections", + reason = "matches collection reform v2 specification, waiting for dust to settle")] + pub fn insert(&mut self, value: V) -> V { + let index = self.index; + self.map.insert(index, value).unwrap() + } + + /// Takes the value of the entry out of the map, and returns it. + #[unstable(feature = "collections", + reason = "matches collection reform v2 specification, waiting for dust to settle")] + pub fn remove(self) -> V { + let index = self.index; + self.map.remove(&index).unwrap() + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -783,7 +924,7 @@ mod test_map { use prelude::*; use core::hash::{hash, SipHasher}; - use super::VecMap; + use super::{VecMap, Occupied, Vacant}; #[test] fn test_get_mut() { @@ -1135,6 +1276,57 @@ mod test_map { map[4]; } + + #[test] + fn test_entry(){ + let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; + + let mut map: VecMap = xs.iter().map(|&x| x).collect(); + + // Existing key (insert) + match map.entry(1) { + Vacant(_) => unreachable!(), + Occupied(mut view) => { + assert_eq!(view.get(), &10); + assert_eq!(view.insert(100), 10); + } + } + assert_eq!(map.get(&1).unwrap(), &100); + assert_eq!(map.len(), 6); + + + // Existing key (update) + match map.entry(2) { + Vacant(_) => unreachable!(), + Occupied(mut view) => { + let v = view.get_mut(); + *v *= 10; + } + } + assert_eq!(map.get(&2).unwrap(), &200); + assert_eq!(map.len(), 6); + + // Existing key (take) + match map.entry(3) { + Vacant(_) => unreachable!(), + Occupied(view) => { + assert_eq!(view.remove(), 30); + } + } + assert_eq!(map.get(&3), None); + assert_eq!(map.len(), 5); + + + // Inexistent key (insert) + match map.entry(10) { + Occupied(_) => unreachable!(), + Vacant(view) => { + assert_eq!(*view.insert(1000), 1000); + } + } + assert_eq!(map.get(&10).unwrap(), &1000); + assert_eq!(map.len(), 6); + } } #[cfg(test)] diff --git a/src/libcore/error.rs b/src/libcore/error.rs index 71d5e88cccff..161f6c789216 100644 --- a/src/libcore/error.rs +++ b/src/libcore/error.rs @@ -51,7 +51,7 @@ //! use std::error::FromError; //! use std::old_io::{File, IoError}; //! use std::os::{MemoryMap, MapError}; -//! use std::path::Path; +//! use std::old_path::Path; //! //! enum MyError { //! Io(IoError), diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 551277bae5c6..60262857765e 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -264,6 +264,7 @@ pub trait Show { #[lang = "debug_trait"] pub trait Debug { /// Formats the value using the given formatter. + #[stable(feature = "rust1", since = "1.0.0")] fn fmt(&self, &mut Formatter) -> Result; } @@ -290,6 +291,7 @@ pub trait String { #[stable(feature = "rust1", since = "1.0.0")] pub trait Display { /// Formats the value using the given formatter. + #[stable(feature = "rust1", since = "1.0.0")] fn fmt(&self, &mut Formatter) -> Result; } diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index d0734f9c0395..23157072d536 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -101,16 +101,11 @@ pub trait Iterator { fn size_hint(&self) -> (usize, Option) { (0, None) } } -impl<'a, T> Iterator for &'a mut (Iterator + 'a) { - type Item = T; - - fn next(&mut self) -> Option { - (**self).next() - } - - fn size_hint(&self) -> (usize, Option) { - (**self).size_hint() - } +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, I: Iterator + ?Sized> Iterator for &'a mut I { + type Item = I::Item; + fn next(&mut self) -> Option { (**self).next() } + fn size_hint(&self) -> (usize, Option) { (**self).size_hint() } } /// Conversion from an `Iterator` @@ -119,6 +114,7 @@ impl<'a, T> Iterator for &'a mut (Iterator + 'a) { built from an iterator over elements of type `{A}`"] pub trait FromIterator { /// Build a container with elements from an external iterator. + #[stable(feature = "rust1", since = "1.0.0")] fn from_iter>(iterator: T) -> Self; } @@ -548,9 +544,7 @@ pub trait IteratorExt: Iterator + Sized { /// assert!(it.next() == Some(5)); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - fn by_ref<'r>(&'r mut self) -> ByRef<'r, Self> { - ByRef{iter: self} - } + fn by_ref(&mut self) -> &mut Self { self } /// Loops through the entire iterator, collecting all of the elements into /// a container implementing `FromIterator`. @@ -723,11 +717,12 @@ pub trait IteratorExt: Iterator + Sized { P: FnMut(Self::Item) -> bool, Self: ExactSizeIterator + DoubleEndedIterator { - let len = self.len(); - for i in (0..len).rev() { - if predicate(self.next_back().expect("rposition: incorrect ExactSizeIterator")) { + let mut i = self.len() - 1; + while let Some(v) = self.next_back() { + if predicate(v) { return Some(i); } + i -= 1; } None } @@ -1017,15 +1012,22 @@ impl IteratorExt for I where I: Iterator {} /// A range iterator able to yield elements from both ends /// -/// A `DoubleEndedIterator` can be thought of as a deque in that `next()` and `next_back()` exhaust -/// elements from the *same* range, and do not work independently of each other. +/// A `DoubleEndedIterator` can be thought of as a deque in that `next()` and +/// `next_back()` exhaust elements from the *same* range, and do not work +/// independently of each other. #[stable(feature = "rust1", since = "1.0.0")] pub trait DoubleEndedIterator: Iterator { - /// Yield an element from the end of the range, returning `None` if the range is empty. + /// Yield an element from the end of the range, returning `None` if the + /// range is empty. #[stable(feature = "rust1", since = "1.0.0")] fn next_back(&mut self) -> Option; } +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, I: DoubleEndedIterator + ?Sized> DoubleEndedIterator for &'a mut I { + fn next_back(&mut self) -> Option { (**self).next_back() } +} + /// An object implementing random access indexing by `usize` /// /// A `RandomAccessIterator` should be either infinite or a `DoubleEndedIterator`. @@ -1065,6 +1067,9 @@ pub trait ExactSizeIterator: Iterator { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, I: ExactSizeIterator + ?Sized> ExactSizeIterator for &'a mut I {} + // All adaptors that preserve the size of the wrapped iterator are fine // Adaptors that may overflow in `size_hint` are not, i.e. `Chain`. #[stable(feature = "rust1", since = "1.0.0")] @@ -1117,32 +1122,6 @@ impl RandomAccessIterator for Rev where I: DoubleEndedIterator + RandomAcc } } -/// A mutable reference to an iterator -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct ByRef<'a, I:'a> { - iter: &'a mut I, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, I> Iterator for ByRef<'a, I> where I: 'a + Iterator { - type Item = ::Item; - - #[inline] - fn next(&mut self) -> Option<::Item> { self.iter.next() } - #[inline] - fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, I> DoubleEndedIterator for ByRef<'a, I> where I: 'a + DoubleEndedIterator { - #[inline] - fn next_back(&mut self) -> Option<::Item> { self.iter.next_back() } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, I> ExactSizeIterator for ByRef<'a, I> where I: 'a + ExactSizeIterator {} - /// A trait for iterators over elements which can be added together #[unstable(feature = "core", reason = "needs to be re-evaluated as part of numerics reform")] @@ -1821,6 +1800,7 @@ impl Peekable { /// Return a reference to the next element of the iterator with out /// advancing it, or None if the iterator is exhausted. #[inline] + #[stable(feature = "rust1", since = "1.0.0")] pub fn peek(&mut self) -> Option<&I::Item> { if self.peeked.is_none() { self.peeked = self.iter.next(); diff --git a/src/libgraphviz/maybe_owned_vec.rs b/src/libgraphviz/maybe_owned_vec.rs index 71f117835935..1c931856fa17 100644 --- a/src/libgraphviz/maybe_owned_vec.rs +++ b/src/libgraphviz/maybe_owned_vec.rs @@ -17,7 +17,7 @@ use std::cmp::Ordering; use std::default::Default; use std::fmt; use std::iter::FromIterator; -use std::path::BytesContainer; +use std::old_path::BytesContainer; use std::slice; // Note 1: It is not clear whether the flexibility of providing both diff --git a/src/librustc/middle/check_loop.rs b/src/librustc/middle/check_loop.rs index 41ef55933cda..ea584407944a 100644 --- a/src/librustc/middle/check_loop.rs +++ b/src/librustc/middle/check_loop.rs @@ -45,7 +45,7 @@ impl<'a, 'v> Visitor<'v> for CheckLoopVisitor<'a> { ast::ExprLoop(ref b, _) => { self.with_context(Loop, |v| v.visit_block(&**b)); } - ast::ExprClosure(_, _, _, ref b) => { + ast::ExprClosure(_, _, ref b) => { self.with_context(Closure, |v| v.visit_block(&**b)); } ast::ExprBreak(_) => self.require_loop("break", e.span), diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index e40e04bdee86..c0fabb2a3481 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -959,7 +959,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.propagate_through_expr(&**e, succ) } - ast::ExprClosure(_, _, _, ref blk) => { + ast::ExprClosure(_, _, ref blk) => { debug!("{} is an ExprClosure", expr_to_string(expr)); diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 1ae483be2696..156ff43e2bab 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -739,7 +739,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { }; match fn_expr.node { - ast::ExprClosure(_, _, _, ref body) => body.id, + ast::ExprClosure(_, _, ref body) => body.id, _ => unreachable!() } }; diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 5028a1322cac..3304bd4ae295 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -14,6 +14,7 @@ use session::Session; use lint; use middle::ty; +use middle::privacy::PublicItems; use metadata::csearch; use syntax::parse::token::InternedString; use syntax::codemap::{Span, DUMMY_SP}; @@ -44,15 +45,16 @@ pub struct Index { // A private tree-walker for producing an Index. struct Annotator<'a> { sess: &'a Session, - index: Index, - parent: Option + index: &'a mut Index, + parent: Option, + export_map: &'a PublicItems, } impl<'a> Annotator<'a> { // Determine the stability for a node based on its attributes and inherited // stability. The stability is recorded in the index and used as the parent. fn annotate(&mut self, id: NodeId, use_parent: bool, - attrs: &Vec, item_sp: Span, f: F) where + attrs: &Vec, item_sp: Span, f: F, required: bool) where F: FnOnce(&mut Annotator), { match attr::find_stability(self.sess.diagnostic(), attrs.as_slice(), item_sp) { @@ -70,7 +72,14 @@ impl<'a> Annotator<'a> { } None => { if use_parent { - self.parent.clone().map(|stab| self.index.local.insert(id, stab)); + if let Some(stab) = self.parent.clone() { + self.index.local.insert(id, stab); + } else if self.index.staged_api && required + && self.export_map.contains(&id) + && !self.sess.opts.test { + self.sess.span_err(item_sp, + "This node does not have a stability attribute"); + } } f(self); } @@ -93,11 +102,19 @@ impl<'a, 'v> Visitor<'v> for Annotator<'a> { _ => true, }; - self.annotate(i.id, use_parent, &i.attrs, i.span, |v| visit::walk_item(v, i)); + // In case of a `pub use ;`, we should not error since the stability + // is inherited from the module itself + let required = match i.node { + ast::ItemUse(_) => i.vis != ast::Public, + _ => true + }; + + self.annotate(i.id, use_parent, &i.attrs, i.span, + |v| visit::walk_item(v, i), required); if let ast::ItemStruct(ref sd, _) = i.node { sd.ctor_id.map(|id| { - self.annotate(id, true, &i.attrs, i.span, |_| {}) + self.annotate(id, true, &i.attrs, i.span, |_| {}, true) }); } } @@ -106,7 +123,7 @@ impl<'a, 'v> Visitor<'v> for Annotator<'a> { _: &'v Block, sp: Span, _: NodeId) { if let FkMethod(_, _, meth) = fk { // Methods are not already annotated, so we annotate it - self.annotate(meth.id, true, &meth.attrs, sp, |_| {}); + self.annotate(meth.id, true, &meth.attrs, sp, |_| {}, true); } // Items defined in a function body have no reason to have // a stability attribute, so we don't recurse. @@ -126,27 +143,41 @@ impl<'a, 'v> Visitor<'v> for Annotator<'a> { TypeTraitItem(ref typedef) => (typedef.ty_param.id, &typedef.attrs, typedef.ty_param.span), }; - self.annotate(id, true, attrs, sp, |v| visit::walk_trait_item(v, t)); + self.annotate(id, true, attrs, sp, |v| visit::walk_trait_item(v, t), true); } fn visit_variant(&mut self, var: &Variant, g: &'v Generics) { self.annotate(var.node.id, true, &var.node.attrs, var.span, - |v| visit::walk_variant(v, var, g)) + |v| visit::walk_variant(v, var, g), true) } fn visit_struct_field(&mut self, s: &StructField) { self.annotate(s.node.id, true, &s.node.attrs, s.span, - |v| visit::walk_struct_field(v, s)); + |v| visit::walk_struct_field(v, s), true); } fn visit_foreign_item(&mut self, i: &ast::ForeignItem) { - self.annotate(i.id, true, &i.attrs, i.span, |_| {}); + self.annotate(i.id, true, &i.attrs, i.span, |_| {}, true); } } impl Index { /// Construct the stability index for a crate being compiled. - pub fn build(sess: &Session, krate: &Crate) -> Index { + pub fn build(&mut self, sess: &Session, krate: &Crate, export_map: &PublicItems) { + if !self.staged_api { + return; + } + let mut annotator = Annotator { + sess: sess, + index: self, + parent: None, + export_map: export_map, + }; + annotator.annotate(ast::CRATE_NODE_ID, true, &krate.attrs, krate.span, + |v| visit::walk_crate(v, krate), true); + } + + pub fn new(krate: &Crate) -> Index { let mut staged_api = false; for attr in &krate.attrs { if attr.name().get() == "staged_api" { @@ -159,22 +190,11 @@ impl Index { } } } - let index = Index { + Index { staged_api: staged_api, local: NodeMap(), extern_cache: DefIdMap() - }; - if !staged_api { - return index; } - let mut annotator = Annotator { - sess: sess, - index: index, - parent: None - }; - annotator.annotate(ast::CRATE_NODE_ID, true, &krate.attrs, krate.span, - |v| visit::walk_crate(v, krate)); - annotator.index } } @@ -234,10 +254,19 @@ impl<'a, 'tcx> Checker<'a, 'tcx> { None => { // This is an 'unmarked' API, which should not exist // in the standard library. - self.tcx.sess.span_err(span, "use of unmarked library feature"); - self.tcx.sess.span_note(span, "this is either a bug in the library you are \ - using or a bug in the compiler - there is \ - no way to use this feature"); + if self.tcx.sess.features.borrow().unmarked_api { + self.tcx.sess.span_warn(span, "use of unmarked library feature"); + self.tcx.sess.span_note(span, "this is either a bug in the library you are \ + using and a bug in the compiler - please \ + report it in both places"); + } else { + self.tcx.sess.span_err(span, "use of unmarked library feature"); + self.tcx.sess.span_note(span, "this is either a bug in the library you are \ + using and a bug in the compiler - please \ + report it in both places"); + self.tcx.sess.span_note(span, "use #![feature(unmarked_api)] in the \ + crate attributes to override this"); + } } } } diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index 3ede6bbb965e..c2a451b405bb 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -80,37 +80,23 @@ pub fn poly_project_and_unify_type<'cx,'tcx>( obligation.repr(selcx.tcx())); let infcx = selcx.infcx(); - let result = infcx.try(|snapshot| { + infcx.try(|snapshot| { let (skol_predicate, skol_map) = infcx.skolemize_late_bound_regions(&obligation.predicate, snapshot); let skol_obligation = obligation.with(skol_predicate); match project_and_unify_type(selcx, &skol_obligation) { - Ok(Some(obligations)) => { + Ok(result) => { match infcx.leak_check(&skol_map, snapshot) { - Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, &obligations)), - Err(e) => Err(Some(MismatchedProjectionTypes { err: e })), + Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, &result)), + Err(e) => Err(MismatchedProjectionTypes { err: e }), } } - Ok(None) => { - // Signal ambiguity using Err just so that infcx.try() - // rolls back the snapshot. We adapt below. - Err(None) - } Err(e) => { - Err(Some(e)) + Err(e) } } - }); - - // Above, we use Err(None) to signal ambiguity so that the - // snapshot will be rolled back. But here, we want to translate to - // Ok(None). Kind of weird. - match result { - Ok(obligations) => Ok(Some(obligations)), - Err(None) => Ok(None), - Err(Some(e)) => Err(e), - } + }) } /// Evaluates constraints of the form: @@ -132,7 +118,10 @@ fn project_and_unify_type<'cx,'tcx>( obligation.cause.clone(), obligation.recursion_depth) { Some(n) => n, - None => { return Ok(None); } + None => { + consider_unification_despite_ambiguity(selcx, obligation); + return Ok(None); + } }; debug!("project_and_unify_type: normalized_ty={} obligations={}", @@ -147,6 +136,50 @@ fn project_and_unify_type<'cx,'tcx>( } } +fn consider_unification_despite_ambiguity<'cx,'tcx>(selcx: &mut SelectionContext<'cx,'tcx>, + obligation: &ProjectionObligation<'tcx>) { + debug!("consider_unification_despite_ambiguity(obligation={})", + obligation.repr(selcx.tcx())); + + let def_id = obligation.predicate.projection_ty.trait_ref.def_id; + match selcx.tcx().lang_items.fn_trait_kind(def_id) { + Some(_) => { } + None => { return; } + } + + let infcx = selcx.infcx(); + let self_ty = obligation.predicate.projection_ty.trait_ref.self_ty(); + let self_ty = infcx.shallow_resolve(self_ty); + debug!("consider_unification_despite_ambiguity: self_ty.sty={:?}", + self_ty.sty); + match self_ty.sty { + ty::ty_closure(closure_def_id, _, substs) => { + let closure_typer = selcx.closure_typer(); + let closure_type = closure_typer.closure_type(closure_def_id, substs); + let ty::Binder((_, ret_type)) = + util::closure_trait_ref_and_return_type(infcx.tcx, + def_id, + self_ty, + &closure_type.sig, + util::TupleArgumentsFlag::No); + let (ret_type, _) = + infcx.replace_late_bound_regions_with_fresh_var( + obligation.cause.span, + infer::AssocTypeProjection(obligation.predicate.projection_ty.item_name), + &ty::Binder(ret_type)); + debug!("consider_unification_despite_ambiguity: ret_type={:?}", + ret_type.repr(selcx.tcx())); + let origin = infer::RelateOutputImplTypes(obligation.cause.span); + let obligation_ty = obligation.predicate.ty; + match infer::mk_eqty(infcx, true, origin, obligation_ty, ret_type) { + Ok(()) => { } + Err(_) => { /* ignore errors */ } + } + } + _ => { } + } +} + /// Normalizes any associated type projections in `value`, replacing /// them with a fully resolved type where possible. The return value /// combines the normalized result and any additional obligations that diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 000572cdd40a..b8af91add9ef 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -233,9 +233,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // is `Vec:Iterable`, but the impl specifies // `impl Iterable for Vec`, than an error would result. - /// Evaluates whether the obligation can be satisfied. Returns an indication of whether the - /// obligation can be satisfied and, if so, by what means. Never affects surrounding typing - /// environment. + /// Attempts to satisfy the obligation. If successful, this will affect the surrounding + /// type environment by performing unification. pub fn select(&mut self, obligation: &TraitObligation<'tcx>) -> SelectionResult<'tcx, Selection<'tcx>> { debug!("select({})", obligation.repr(self.tcx())); @@ -243,11 +242,68 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let stack = self.push_stack(None, obligation); match try!(self.candidate_from_obligation(&stack)) { - None => Ok(None), + None => { + self.consider_unification_despite_ambiguity(obligation); + Ok(None) + } Some(candidate) => Ok(Some(try!(self.confirm_candidate(obligation, candidate)))), } } + /// In the particular case of unboxed closure obligations, we can + /// sometimes do some amount of unification for the + /// argument/return types even though we can't yet fully match obligation. + /// The particular case we are interesting in is an obligation of the form: + /// + /// C : FnFoo + /// + /// where `C` is an unboxed closure type and `FnFoo` is one of the + /// `Fn` traits. Because we know that users cannot write impls for closure types + /// themselves, the only way that `C : FnFoo` can fail to match is under two + /// conditions: + /// + /// 1. The closure kind for `C` is not yet known, because inference isn't complete. + /// 2. The closure kind for `C` *is* known, but doesn't match what is needed. + /// For example, `C` may be a `FnOnce` closure, but a `Fn` closure is needed. + /// + /// In either case, we always know what argument types are + /// expected by `C`, no matter what kind of `Fn` trait it + /// eventually matches. So we can go ahead and unify the argument + /// types, even though the end result is ambiguous. + /// + /// Note that this is safe *even if* the trait would never be + /// matched (case 2 above). After all, in that case, an error will + /// result, so it kind of doesn't matter what we do --- unifying + /// the argument types can only be helpful to the user, because + /// once they patch up the kind of closure that is expected, the + /// argment types won't really change. + fn consider_unification_despite_ambiguity(&mut self, obligation: &TraitObligation<'tcx>) { + // Is this a `C : FnFoo(...)` trait reference for some trait binding `FnFoo`? + match self.tcx().lang_items.fn_trait_kind(obligation.predicate.0.def_id()) { + Some(_) => { } + None => { return; } + } + + // Is the self-type a closure type? We ignore bindings here + // because if it is a closure type, it must be a closure type from + // within this current fn, and hence none of the higher-ranked + // lifetimes can appear inside the self-type. + let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + let (closure_def_id, substs) = match self_ty.sty { + ty::ty_closure(id, _, ref substs) => (id, substs.clone()), + _ => { return; } + }; + assert!(!substs.has_escaping_regions()); + + let closure_trait_ref = self.closure_trait_ref(obligation, closure_def_id, substs); + match self.confirm_poly_trait_refs(obligation.cause.clone(), + obligation.predicate.to_poly_trait_ref(), + closure_trait_ref) { + Ok(()) => { } + Err(_) => { /* Silently ignore errors. */ } + } + } + /////////////////////////////////////////////////////////////////////////// // EVALUATION // @@ -1003,7 +1059,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates: &mut SelectionCandidateSet<'tcx>) -> Result<(),SelectionError<'tcx>> { - let kind = match self.fn_family_trait_kind(obligation.predicate.0.def_id()) { + let kind = match self.tcx().lang_items.fn_trait_kind(obligation.predicate.0.def_id()) { Some(k) => k, None => { return Ok(()); } }; @@ -2303,22 +2359,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { impl_obligations } - fn fn_family_trait_kind(&self, - trait_def_id: ast::DefId) - -> Option - { - let tcx = self.tcx(); - if Some(trait_def_id) == tcx.lang_items.fn_trait() { - Some(ty::FnClosureKind) - } else if Some(trait_def_id) == tcx.lang_items.fn_mut_trait() { - Some(ty::FnMutClosureKind) - } else if Some(trait_def_id) == tcx.lang_items.fn_once_trait() { - Some(ty::FnOnceClosureKind) - } else { - None - } - } - #[allow(unused_comparisons)] fn derived_cause(&self, obligation: &TraitObligation<'tcx>, diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 4b3833b687c5..bffee9d49334 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -306,7 +306,7 @@ impl Target { use std::env; use std::ffi::OsString; use std::old_io::File; - use std::path::Path; + use std::old_path::Path; use serialize::json; fn load_file(path: &Path) -> Result { diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index e5271cfde5a4..b9d2b9ec263a 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -324,7 +324,7 @@ pub fn closure_to_block(closure_id: ast::NodeId, tcx: &ty::ctxt) -> ast::NodeId { match tcx.map.get(closure_id) { ast_map::NodeExpr(expr) => match expr.node { - ast::ExprClosure(_, _, _, ref block) => { + ast::ExprClosure(_, _, ref block) => { block.id } _ => { diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 9b9cc14c4761..8ede037594a0 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -594,9 +594,6 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, time(time_passes, "loop checking", (), |_| middle::check_loop::check_crate(&sess, krate)); - let stability_index = time(time_passes, "stability index", (), |_| - stability::Index::build(&sess, krate)); - time(time_passes, "static item recursion checking", (), |_| middle::check_static_recursion::check_crate(&sess, krate, &def_map, &ast_map)); @@ -608,7 +605,7 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, freevars, region_map, lang_items, - stability_index); + stability::Index::new(krate)); // passes are timed inside typeck typeck::check_crate(&ty_cx, trait_map); @@ -628,6 +625,10 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, time(time_passes, "privacy checking", maps, |(a, b)| rustc_privacy::check_crate(&ty_cx, &export_map, a, b)); + // Do not move this check past lint + time(time_passes, "stability index", (), |_| + ty_cx.stability.borrow_mut().build(&ty_cx.sess, krate, &public_items)); + time(time_passes, "intrinsic checking", (), |_| middle::intrinsicck::check_crate(&ty_cx)); diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 20bf77190be7..7dc0d9be5392 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -125,7 +125,6 @@ fn test_env(source_string: &str, resolve::resolve_crate(&sess, &ast_map, &lang_items, krate, resolve::MakeGlobMap::No); let named_region_map = resolve_lifetime::krate(&sess, krate, &def_map); let region_map = region::resolve_crate(&sess, krate); - let stability_index = stability::Index::build(&sess, krate); let tcx = ty::mk_ctxt(sess, &arenas, def_map, @@ -134,7 +133,7 @@ fn test_env(source_string: &str, freevars, region_map, lang_items, - stability_index); + stability::Index::new(krate)); let infcx = infer::new_infer_ctxt(&tcx); body(Env { infcx: &infcx }); infcx.resolve_regions_and_report_errors(ast::CRATE_NODE_ID); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index d87039cbaefc..dd739059ed0d 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -4521,7 +4521,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { visit::walk_expr(self, expr); } - ExprClosure(_, _, ref fn_decl, ref block) => { + ExprClosure(_, ref fn_decl, ref block) => { self.resolve_function(ClosureRibKind(expr.id), Some(&**fn_decl), NoTypeParameters, &**block); diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 7758039e40a4..b0ce9641cf44 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -1394,7 +1394,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> { type, found {:?}", ty)[]), } }, - ast::ExprClosure(_, _, ref decl, ref body) => { + ast::ExprClosure(_, ref decl, ref body) => { if generated_code(body.span) { return } diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 9e561fc883bb..6901eb25b31f 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -1340,7 +1340,7 @@ fn build_cfg(tcx: &ty::ctxt, id: ast::NodeId) -> (ast::NodeId, Option) } Some(ast_map::NodeExpr(e)) => { match e.node { - ast::ExprClosure(_, _, _, ref blk) => { + ast::ExprClosure(_, _, ref blk) => { blk } _ => tcx.sess.bug("unexpected expr variant in has_nested_returns") diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs index 66bb299273d9..c6b70e1a1abc 100644 --- a/src/librustc_trans/trans/debuginfo.rs +++ b/src/librustc_trans/trans/debuginfo.rs @@ -1283,7 +1283,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } ast_map::NodeExpr(ref expr) => { match expr.node { - ast::ExprClosure(_, _, ref fn_decl, ref top_level_block) => { + ast::ExprClosure(_, ref fn_decl, ref top_level_block) => { let name = format!("fn{}", token::gensym("fn")); let name = token::str_to_ident(&name[]); (name, &**fn_decl, @@ -1590,7 +1590,7 @@ fn compile_unit_metadata(cx: &CrateContext) -> DIDescriptor { Some(ref p) if p.is_relative() => { // prepend "./" if necessary let dotdot = b".."; - let prefix: &[u8] = &[dotdot[0], ::std::path::SEP_BYTE]; + let prefix: &[u8] = &[dotdot[0], ::std::old_path::SEP_BYTE]; let mut path_bytes = p.as_vec().to_vec(); if &path_bytes[..2] != prefix && @@ -3595,7 +3595,7 @@ fn create_scope_map(cx: &CrateContext, }) } - ast::ExprClosure(_, _, ref decl, ref block) => { + ast::ExprClosure(_, ref decl, ref block) => { with_new_scope(cx, block.span, scope_stack, diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 689d0c231b5b..44eb5b190e1a 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -1095,7 +1095,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ast::ExprVec(..) | ast::ExprRepeat(..) => { tvec::trans_fixed_vstore(bcx, expr, dest) } - ast::ExprClosure(_, _, ref decl, ref body) => { + ast::ExprClosure(_, ref decl, ref body) => { closure::trans_closure_expr(bcx, &**decl, &**body, expr.id, dest) } ast::ExprCall(ref f, ref args) => { diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 808dbd4b3191..b2a676e878e6 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -25,7 +25,6 @@ use util::ppaux::Repr; pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &ast::Expr, _capture: ast::CaptureClause, - opt_kind: Option, decl: &'tcx ast::FnDecl, body: &'tcx ast::Block, expected: Expectation<'tcx>) { @@ -33,38 +32,14 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr.repr(fcx.tcx()), expected.repr(fcx.tcx())); - let expected_sig_and_kind = expected.to_option(fcx).and_then(|ty| { - deduce_expectations_from_expected_type(fcx, ty) - }); - - match opt_kind { - None => { - // If users didn't specify what sort of closure they want, - // examine the expected type. For now, if we see explicit - // evidence than an unboxed closure is desired, we'll use - // that. Otherwise, we leave it unspecified, to be filled - // in by upvar inference. - match expected_sig_and_kind { - None => { // don't have information about the kind, request explicit annotation - check_closure(fcx, expr, None, decl, body, None); - }, - Some((sig, kind)) => { - check_closure(fcx, expr, Some(kind), decl, body, Some(sig)); - } - } - } - - Some(kind) => { - let kind = match kind { - ast::FnClosureKind => ty::FnClosureKind, - ast::FnMutClosureKind => ty::FnMutClosureKind, - ast::FnOnceClosureKind => ty::FnOnceClosureKind, - }; - - let expected_sig = expected_sig_and_kind.map(|t| t.0); - check_closure(fcx, expr, Some(kind), decl, body, expected_sig); - } - } + // It's always helpful for inference if we know the kind of + // closure sooner rather than later, so first examine the expected + // type, and see if can glean a closure kind from there. + let (expected_sig,expected_kind) = match expected.to_option(fcx) { + Some(ty) => deduce_expectations_from_expected_type(fcx, ty), + None => (None, None) + }; + check_closure(fcx, expr, expected_kind, decl, body, expected_sig) } fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, @@ -133,21 +108,30 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, fn deduce_expectations_from_expected_type<'a,'tcx>( fcx: &FnCtxt<'a,'tcx>, expected_ty: Ty<'tcx>) - -> Option<(ty::FnSig<'tcx>,ty::ClosureKind)> + -> (Option>,Option) { + debug!("deduce_expectations_from_expected_type(expected_ty={})", + expected_ty.repr(fcx.tcx())); + match expected_ty.sty { ty::ty_trait(ref object_type) => { let proj_bounds = object_type.projection_bounds_with_self_ty(fcx.tcx(), fcx.tcx().types.err); - proj_bounds.iter() - .filter_map(|pb| deduce_expectations_from_projection(fcx, pb)) - .next() + let expectations = + proj_bounds.iter() + .filter_map(|pb| deduce_expectations_from_projection(fcx, pb)) + .next(); + + match expectations { + Some((sig, kind)) => (Some(sig), Some(kind)), + None => (None, None) + } } ty::ty_infer(ty::TyVar(vid)) => { deduce_expectations_from_obligations(fcx, vid) } _ => { - None + (None, None) } } } @@ -155,33 +139,61 @@ fn deduce_expectations_from_expected_type<'a,'tcx>( fn deduce_expectations_from_obligations<'a,'tcx>( fcx: &FnCtxt<'a,'tcx>, expected_vid: ty::TyVid) - -> Option<(ty::FnSig<'tcx>, ty::ClosureKind)> + -> (Option>, Option) { let fulfillment_cx = fcx.inh.fulfillment_cx.borrow(); // Here `expected_ty` is known to be a type inference variable. - fulfillment_cx.pending_obligations() - .iter() - .filter_map(|obligation| { - match obligation.predicate { - ty::Predicate::Projection(ref proj_predicate) => { - let trait_ref = proj_predicate.to_poly_trait_ref(); - let self_ty = fcx.infcx().shallow_resolve(trait_ref.self_ty()); - match self_ty.sty { - ty::ty_infer(ty::TyVar(v)) if expected_vid == v => { - deduce_expectations_from_projection(fcx, proj_predicate) - } - _ => { - None - } - } - } - _ => { - None - } - } - }) - .next() + let expected_sig_and_kind = + fulfillment_cx + .pending_obligations() + .iter() + .filter_map(|obligation| { + debug!("deduce_expectations_from_obligations: obligation.predicate={}", + obligation.predicate.repr(fcx.tcx())); + + match obligation.predicate { + // Given a Projection predicate, we can potentially infer + // the complete signature. + ty::Predicate::Projection(ref proj_predicate) => { + let trait_ref = proj_predicate.to_poly_trait_ref(); + self_type_matches_expected_vid(fcx, trait_ref, expected_vid) + .and_then(|_| deduce_expectations_from_projection(fcx, proj_predicate)) + } + _ => { + None + } + } + }) + .next(); + + match expected_sig_and_kind { + Some((sig, kind)) => { return (Some(sig), Some(kind)); } + None => { } + } + + // Even if we can't infer the full signature, we may be able to + // infer the kind. This can occur if there is a trait-reference + // like `F : Fn`. + let expected_kind = + fulfillment_cx + .pending_obligations() + .iter() + .filter_map(|obligation| { + let opt_trait_ref = match obligation.predicate { + ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref()), + ty::Predicate::Trait(ref data) => Some(data.to_poly_trait_ref()), + ty::Predicate::Equate(..) => None, + ty::Predicate::RegionOutlives(..) => None, + ty::Predicate::TypeOutlives(..) => None, + }; + opt_trait_ref + .and_then(|trait_ref| self_type_matches_expected_vid(fcx, trait_ref, expected_vid)) + .and_then(|trait_ref| fcx.tcx().lang_items.fn_trait_kind(trait_ref.def_id())) + }) + .next(); + + (None, expected_kind) } /// Given a projection like "::Result == Y", we can deduce @@ -229,3 +241,20 @@ fn deduce_expectations_from_projection<'a,'tcx>( return Some((fn_sig, kind)); } +fn self_type_matches_expected_vid<'a,'tcx>( + fcx: &FnCtxt<'a,'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + expected_vid: ty::TyVid) + -> Option> +{ + let self_ty = fcx.infcx().shallow_resolve(trait_ref.self_ty()); + debug!("self_type_matches_expected_vid(trait_ref={}, self_ty={})", + trait_ref.repr(fcx.tcx()), + self_ty.repr(fcx.tcx())); + match self_ty.sty { + ty::ty_infer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref), + _ => None, + } +} + + diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 07b67d543a87..adf15fbf28a8 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3736,8 +3736,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, ast::ExprMatch(ref discrim, ref arms, match_src) => { _match::check_match(fcx, expr, &**discrim, arms.as_slice(), expected, match_src); } - ast::ExprClosure(capture, opt_kind, ref decl, ref body) => { - closure::check_expr_closure(fcx, expr, capture, opt_kind, &**decl, &**body, expected); + ast::ExprClosure(capture, ref decl, ref body) => { + closure::check_expr_closure(fcx, expr, capture, &**decl, &**body, expected); } ast::ExprBlock(ref b) => { check_block_with_expected(fcx, &**b, expected); diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index d5baff3a0c4a..9df0403794d7 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -638,7 +638,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { visit::walk_expr(rcx, expr); } - ast::ExprClosure(_, _, _, ref body) => { + ast::ExprClosure(_, _, ref body) => { check_expr_fn_block(rcx, expr, &**body); } diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index b52e01f9a7a7..f452c8488ce1 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -83,7 +83,7 @@ struct SeedBorrowKind<'a,'tcx:'a> { impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> { fn visit_expr(&mut self, expr: &ast::Expr) { match expr.node { - ast::ExprClosure(cc, _, _, ref body) => { + ast::ExprClosure(cc, _, ref body) => { self.check_closure(expr, cc, &**body); } diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 52b1eb490cc2..f047a36c5609 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -118,7 +118,7 @@ impl<'cx, 'tcx, 'v> Visitor<'v> for WritebackCx<'cx, 'tcx> { MethodCall::expr(e.id)); match e.node { - ast::ExprClosure(_, _, ref decl, _) => { + ast::ExprClosure(_, ref decl, _) => { for input in &decl.inputs { let _ = self.visit_node_id(ResolvingExpr(e.span), input.id); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 07679480bfb3..57eaf042aa02 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -49,7 +49,7 @@ use rustc::middle::stability; use std::rc::Rc; use std::u32; use std::str::Str as StrTrait; // Conflicts with Str variant -use std::path::Path as FsPath; // Conflicts with Path struct +use std::old_path::Path as FsPath; // Conflicts with Path struct use core::DocContext; use doctree; diff --git a/src/libserialize/serialize.rs b/src/libserialize/serialize.rs index 3d7c91ad1885..517907bcf58e 100644 --- a/src/libserialize/serialize.rs +++ b/src/libserialize/serialize.rs @@ -14,7 +14,7 @@ Core encoding and decoding interfaces. */ -use std::path; +use std::old_path; use std::rc::Rc; use std::cell::{Cell, RefCell}; use std::sync::Arc; @@ -538,29 +538,29 @@ macro_rules! tuple { tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } -impl Encodable for path::posix::Path { +impl Encodable for old_path::posix::Path { fn encode(&self, e: &mut S) -> Result<(), S::Error> { self.as_vec().encode(e) } } -impl Decodable for path::posix::Path { - fn decode(d: &mut D) -> Result { +impl Decodable for old_path::posix::Path { + fn decode(d: &mut D) -> Result { let bytes: Vec = try!(Decodable::decode(d)); - Ok(path::posix::Path::new(bytes)) + Ok(old_path::posix::Path::new(bytes)) } } -impl Encodable for path::windows::Path { +impl Encodable for old_path::windows::Path { fn encode(&self, e: &mut S) -> Result<(), S::Error> { self.as_vec().encode(e) } } -impl Decodable for path::windows::Path { - fn decode(d: &mut D) -> Result { +impl Decodable for old_path::windows::Path { + fn decode(d: &mut D) -> Result { let bytes: Vec = try!(Decodable::decode(d)); - Ok(path::windows::Path::new(bytes)) + Ok(old_path::windows::Path::new(bytes)) } } diff --git a/src/libstd/env.rs b/src/libstd/env.rs index 5070f8c547ab..559a68542efc 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -57,7 +57,7 @@ pub fn current_dir() -> IoResult { /// /// ```rust /// use std::env; -/// use std::path::Path; +/// use std::old_path::Path; /// /// let root = Path::new("/"); /// assert!(env::set_current_dir(&root).is_ok()); diff --git a/src/libstd/ffi/mod.rs b/src/libstd/ffi/mod.rs index 76f925a23f17..07a4f17796c4 100644 --- a/src/libstd/ffi/mod.rs +++ b/src/libstd/ffi/mod.rs @@ -24,6 +24,7 @@ pub use self::os_str::OsStr; mod c_str; mod os_str; +// FIXME (#21670): these should be defined in the os_str module /// Freely convertible to an `&OsStr` slice. pub trait AsOsStr { /// Convert to an `&OsStr` slice. diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs index b8d770e6ad69..4d7292b6eb41 100644 --- a/src/libstd/ffi/os_str.rs +++ b/src/libstd/ffi/os_str.rs @@ -41,7 +41,7 @@ use string::{String, CowString}; use ops; use cmp; use hash::{Hash, Hasher, Writer}; -use path::{Path, GenericPath}; +use old_path::{Path, GenericPath}; use sys::os_str::{Buf, Slice}; use sys_common::{AsInner, IntoInner, FromInner}; diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs new file mode 100644 index 000000000000..2fd6631ecc43 --- /dev/null +++ b/src/libstd/io/buffered.rs @@ -0,0 +1,676 @@ +// Copyright 2013 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. +// +// ignore-lexer-test FIXME #15883 + +//! Buffering wrappers for I/O traits + +use prelude::v1::*; +use io::prelude::*; + +use cmp; +use error::Error as StdError; +use error::FromError; +use fmt; +use io::{self, Cursor, DEFAULT_BUF_SIZE, Error, ErrorKind}; +use ptr; + +/// Wraps a `Read` and buffers input from it +/// +/// It can be excessively inefficient to work directly with a `Read` instance. +/// For example, every call to `read` on `TcpStream` results in a system call. +/// A `BufReader` performs large, infrequent reads on the underlying `Read` +/// and maintains an in-memory buffer of the results. +pub struct BufReader { + inner: R, + buf: Cursor>, +} + +impl BufReader { + /// Creates a new `BufReader` with a default buffer capacity + pub fn new(inner: R) -> BufReader { + BufReader::with_capacity(DEFAULT_BUF_SIZE, inner) + } + + /// Creates a new `BufReader` with the specified buffer capacity + pub fn with_capacity(cap: usize, inner: R) -> BufReader { + BufReader { + inner: inner, + buf: Cursor::new(Vec::with_capacity(cap)), + } + } + + /// Gets a reference to the underlying reader. + pub fn get_ref<'a>(&self) -> &R { &self.inner } + + /// Gets a mutable reference to the underlying reader. + /// + /// # Warning + /// + /// It is inadvisable to directly read from the underlying reader. + pub fn get_mut(&mut self) -> &mut R { &mut self.inner } + + /// Unwraps this `BufReader`, returning the underlying reader. + /// + /// Note that any leftover data in the internal buffer is lost. + pub fn into_inner(self) -> R { self.inner } +} + +impl Read for BufReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + // If we don't have any buffered data and we're doing a massive read + // (larger than our internal buffer), bypass our internal buffer + // entirely. + if self.buf.get_ref().len() == self.buf.position() as usize && + buf.len() >= self.buf.get_ref().capacity() { + return self.inner.read(buf); + } + try!(self.fill_buf()); + self.buf.read(buf) + } +} + +impl BufRead for BufReader { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + // If we've reached the end of our internal buffer then we need to fetch + // some more data from the underlying reader. + if self.buf.position() as usize == self.buf.get_ref().len() { + self.buf.set_position(0); + let v = self.buf.get_mut(); + v.truncate(0); + let inner = &mut self.inner; + try!(super::with_end_to_cap(v, |b| inner.read(b))); + } + self.buf.fill_buf() + } + + fn consume(&mut self, amt: uint) { + self.buf.consume(amt) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for BufReader where R: fmt::Debug { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "BufReader {{ reader: {:?}, buffer: {}/{} }}", + self.inner, self.buf.position(), self.buf.get_ref().len()) + } +} + +/// Wraps a Writer and buffers output to it +/// +/// It can be excessively inefficient to work directly with a `Write`. For +/// example, every call to `write` on `TcpStream` results in a system call. A +/// `BufWriter` keeps an in memory buffer of data and writes it to the +/// underlying `Write` in large, infrequent batches. +/// +/// This writer will be flushed when it is dropped. +pub struct BufWriter { + inner: Option, + buf: Vec, +} + +/// An error returned by `into_inner` which indicates whether a flush error +/// happened or not. +#[derive(Debug)] +pub struct IntoInnerError(W, Error); + +impl BufWriter { + /// Creates a new `BufWriter` with a default buffer capacity + pub fn new(inner: W) -> BufWriter { + BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner) + } + + /// Creates a new `BufWriter` with the specified buffer capacity + pub fn with_capacity(cap: usize, inner: W) -> BufWriter { + BufWriter { + inner: Some(inner), + buf: Vec::with_capacity(cap), + } + } + + fn flush_buf(&mut self) -> io::Result<()> { + let mut written = 0; + let len = self.buf.len(); + let mut ret = Ok(()); + while written < len { + match self.inner.as_mut().unwrap().write(&self.buf[written..]) { + Ok(0) => { + ret = Err(Error::new(ErrorKind::WriteZero, + "failed to flush", None)); + break; + } + Ok(n) => written += n, + Err(e) => { ret = Err(e); break } + + } + } + if written > 0 { + // NB: would be better expressed as .remove(0..n) if it existed + unsafe { + ptr::copy_memory(self.buf.as_mut_ptr(), + self.buf.as_ptr().offset(written as isize), + len - written); + } + } + self.buf.truncate(len - written); + ret + } + + /// Gets a reference to the underlying writer. + pub fn get_ref(&self) -> &W { self.inner.as_ref().unwrap() } + + /// Gets a mutable reference to the underlying write. + /// + /// # Warning + /// + /// It is inadvisable to directly read from the underlying writer. + pub fn get_mut(&mut self) -> &mut W { self.inner.as_mut().unwrap() } + + /// Unwraps this `BufWriter`, returning the underlying writer. + /// + /// The buffer is flushed before returning the writer. + pub fn into_inner(mut self) -> Result>> { + match self.flush_buf() { + Err(e) => Err(IntoInnerError(self, e)), + Ok(()) => Ok(self.inner.take().unwrap()) + } + } +} + +impl Write for BufWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + if self.buf.len() + buf.len() > self.buf.capacity() { + try!(self.flush_buf()); + } + if buf.len() >= self.buf.capacity() { + self.inner.as_mut().unwrap().write(buf) + } else { + let amt = cmp::min(buf.len(), self.buf.capacity()); + Write::write(&mut self.buf, &buf[..amt]) + } + } + fn flush(&mut self) -> io::Result<()> { + self.flush_buf().and_then(|()| self.get_mut().flush()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for BufWriter where W: fmt::Debug { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "BufWriter {{ writer: {:?}, buffer: {}/{} }}", + self.inner.as_ref().unwrap(), self.buf.len(), self.buf.capacity()) + } +} + +#[unsafe_destructor] +impl Drop for BufWriter { + fn drop(&mut self) { + if self.inner.is_some() { + // dtors should not panic, so we ignore a failed flush + let _r = self.flush_buf(); + } + } +} + +impl IntoInnerError { + /// Returns the error which caused the call to `into_inner` to fail. + /// + /// This error was returned when attempting to flush the internal buffer. + pub fn error(&self) -> &Error { &self.1 } + + /// Returns the underlying `BufWriter` instance which generated the error. + /// + /// The returned object can be used to retry a flush or re-inspect the + /// buffer. + pub fn into_inner(self) -> W { self.0 } +} + +impl FromError> for Error { + fn from_error(iie: IntoInnerError) -> Error { iie.1 } +} + +impl StdError for IntoInnerError { + fn description(&self) -> &str { self.error().description() } +} + +impl fmt::Display for IntoInnerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.error().fmt(f) + } +} + +/// Wraps a Writer and buffers output to it, flushing whenever a newline +/// (`0x0a`, `'\n'`) is detected. +/// +/// This writer will be flushed when it is dropped. +pub struct LineWriter { + inner: BufWriter, +} + +impl LineWriter { + /// Creates a new `LineWriter` + pub fn new(inner: W) -> LineWriter { + // Lines typically aren't that long, don't use a giant buffer + LineWriter { inner: BufWriter::with_capacity(1024, inner) } + } + + /// Gets a reference to the underlying writer. + /// + /// This type does not expose the ability to get a mutable reference to the + /// underlying reader because that could possibly corrupt the buffer. + pub fn get_ref<'a>(&'a self) -> &'a W { self.inner.get_ref() } + + /// Unwraps this `LineWriter`, returning the underlying writer. + /// + /// The internal buffer is flushed before returning the writer. + pub fn into_inner(self) -> Result>> { + self.inner.into_inner().map_err(|IntoInnerError(buf, e)| { + IntoInnerError(LineWriter { inner: buf }, e) + }) + } +} + +impl Write for LineWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + match buf.rposition_elem(&b'\n') { + Some(i) => { + let n = try!(self.inner.write(&buf[..i + 1])); + if n != i + 1 { return Ok(n) } + try!(self.inner.flush()); + self.inner.write(&buf[i + 1..]).map(|i| n + i) + } + None => self.inner.write(buf), + } + } + + fn flush(&mut self) -> io::Result<()> { self.inner.flush() } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for LineWriter where W: fmt::Debug { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "LineWriter {{ writer: {:?}, buffer: {}/{} }}", + self.inner.inner, self.inner.buf.len(), + self.inner.buf.capacity()) + } +} + +struct InternalBufWriter(BufWriter); + +impl InternalBufWriter { + fn get_mut(&mut self) -> &mut BufWriter { + let InternalBufWriter(ref mut w) = *self; + return w; + } +} + +impl Read for InternalBufWriter { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.get_mut().inner.as_mut().unwrap().read(buf) + } +} + +/// Wraps a Stream and buffers input and output to and from it. +/// +/// It can be excessively inefficient to work directly with a `Stream`. For +/// example, every call to `read` or `write` on `TcpStream` results in a system +/// call. A `BufStream` keeps in memory buffers of data, making large, +/// infrequent calls to `read` and `write` on the underlying `Stream`. +/// +/// The output half will be flushed when this stream is dropped. +pub struct BufStream { + inner: BufReader> +} + +impl BufStream { + /// Creates a new buffered stream with explicitly listed capacities for the + /// reader/writer buffer. + pub fn with_capacities(reader_cap: usize, writer_cap: usize, inner: S) + -> BufStream { + let writer = BufWriter::with_capacity(writer_cap, inner); + let internal_writer = InternalBufWriter(writer); + let reader = BufReader::with_capacity(reader_cap, internal_writer); + BufStream { inner: reader } + } + + /// Creates a new buffered stream with the default reader/writer buffer + /// capacities. + pub fn new(inner: S) -> BufStream { + BufStream::with_capacities(DEFAULT_BUF_SIZE, DEFAULT_BUF_SIZE, inner) + } + + /// Gets a reference to the underlying stream. + pub fn get_ref(&self) -> &S { + let InternalBufWriter(ref w) = self.inner.inner; + w.get_ref() + } + + /// Gets a mutable reference to the underlying stream. + /// + /// # Warning + /// + /// It is inadvisable to read directly from or write directly to the + /// underlying stream. + pub fn get_mut(&mut self) -> &mut S { + let InternalBufWriter(ref mut w) = self.inner.inner; + w.get_mut() + } + + /// Unwraps this `BufStream`, returning the underlying stream. + /// + /// The internal buffer is flushed before returning the stream. Any leftover + /// data in the read buffer is lost. + pub fn into_inner(self) -> Result>> { + let BufReader { inner: InternalBufWriter(w), buf } = self.inner; + w.into_inner().map_err(|IntoInnerError(w, e)| { + IntoInnerError(BufStream { + inner: BufReader { inner: InternalBufWriter(w), buf: buf }, + }, e) + }) + } +} + +impl BufRead for BufStream { + fn fill_buf(&mut self) -> io::Result<&[u8]> { self.inner.fill_buf() } + fn consume(&mut self, amt: uint) { self.inner.consume(amt) } +} + +impl Read for BufStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } +} + +impl Write for BufStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.inner.inner.get_mut().write(buf) + } + fn flush(&mut self) -> io::Result<()> { + self.inner.inner.get_mut().flush() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for BufStream where S: fmt::Debug { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let reader = &self.inner; + let writer = &self.inner.inner.0; + write!(fmt, "BufStream {{ stream: {:?}, write_buffer: {}/{}, read_buffer: {}/{} }}", + writer.inner, + writer.buf.len(), writer.buf.capacity(), + reader.buf.position(), reader.buf.get_ref().len()) + } +} + +#[cfg(test)] +mod tests { + use prelude::v1::*; + use io::prelude::*; + use io::{self, BufReader, BufWriter, BufStream, Cursor, LineWriter}; + use test; + + /// A dummy reader intended at testing short-reads propagation. + pub struct ShortReader { + lengths: Vec, + } + + impl Read for ShortReader { + fn read(&mut self, _: &mut [u8]) -> io::Result { + if self.lengths.is_empty() { + Ok(0) + } else { + Ok(self.lengths.remove(0)) + } + } + } + + #[test] + fn test_buffered_reader() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, inner); + + let mut buf = [0, 0, 0]; + let nread = reader.read(&mut buf); + assert_eq!(Ok(3), nread); + let b: &[_] = &[5, 6, 7]; + assert_eq!(buf, b); + + let mut buf = [0, 0]; + let nread = reader.read(&mut buf); + assert_eq!(Ok(2), nread); + let b: &[_] = &[0, 1]; + assert_eq!(buf, b); + + let mut buf = [0]; + let nread = reader.read(&mut buf); + assert_eq!(Ok(1), nread); + let b: &[_] = &[2]; + assert_eq!(buf, b); + + let mut buf = [0, 0, 0]; + let nread = reader.read(&mut buf); + assert_eq!(Ok(1), nread); + let b: &[_] = &[3, 0, 0]; + assert_eq!(buf, b); + + let nread = reader.read(&mut buf); + assert_eq!(Ok(1), nread); + let b: &[_] = &[4, 0, 0]; + assert_eq!(buf, b); + + assert_eq!(reader.read(&mut buf), Ok(0)); + } + + #[test] + fn test_buffered_writer() { + let inner = Vec::new(); + let mut writer = BufWriter::with_capacity(2, inner); + + writer.write(&[0, 1]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1]); + + writer.write(&[2]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1]); + + writer.write(&[3]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1]); + + writer.flush().unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); + + writer.write(&[4]).unwrap(); + writer.write(&[5]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); + + writer.write(&[6]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]); + + writer.write(&[7, 8]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]); + + writer.write(&[9, 10, 11]).unwrap(); + let a: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + + writer.flush().unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + } + + #[test] + fn test_buffered_writer_inner_flushes() { + let mut w = BufWriter::with_capacity(3, Vec::new()); + w.write(&[0, 1]).unwrap(); + assert_eq!(*w.get_ref(), []); + let w = w.into_inner().unwrap(); + assert_eq!(w, [0, 1]); + } + + // This is just here to make sure that we don't infinite loop in the + // newtype struct autoderef weirdness + #[test] + fn test_buffered_stream() { + struct S; + + impl Write for S { + fn write(&mut self, b: &[u8]) -> io::Result { Ok(b.len()) } + fn flush(&mut self) -> io::Result<()> { Ok(()) } + } + + impl Read for S { + fn read(&mut self, _: &mut [u8]) -> io::Result { Ok(0) } + } + + let mut stream = BufStream::new(S); + assert_eq!(stream.read(&mut [0; 10]), Ok(0)); + stream.write(&[0; 10]).unwrap(); + stream.flush().unwrap(); + } + + #[test] + fn test_read_until() { + let inner: &[u8] = &[0, 1, 2, 1, 0]; + let mut reader = BufReader::with_capacity(2, inner); + let mut v = Vec::new(); + reader.read_until(0, &mut v).unwrap(); + assert_eq!(v, [0]); + v.truncate(0); + reader.read_until(2, &mut v).unwrap(); + assert_eq!(v, [1, 2]); + v.truncate(0); + reader.read_until(1, &mut v).unwrap(); + assert_eq!(v, [1]); + v.truncate(0); + reader.read_until(8, &mut v).unwrap(); + assert_eq!(v, [0]); + v.truncate(0); + reader.read_until(9, &mut v).unwrap(); + assert_eq!(v, []); + } + + #[test] + fn test_line_buffer() { + let mut writer = LineWriter::new(Vec::new()); + writer.write(&[0]).unwrap(); + assert_eq!(*writer.get_ref(), []); + writer.write(&[1]).unwrap(); + assert_eq!(*writer.get_ref(), []); + writer.flush().unwrap(); + assert_eq!(*writer.get_ref(), [0, 1]); + writer.write(&[0, b'\n', 1, b'\n', 2]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n']); + writer.flush().unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2]); + writer.write(&[3, b'\n']).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']); + } + + #[test] + fn test_read_line() { + let in_buf = b"a\nb\nc"; + let mut reader = BufReader::with_capacity(2, in_buf); + let mut s = String::new(); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, "a\n"); + s.truncate(0); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, "b\n"); + s.truncate(0); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, "c"); + s.truncate(0); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, ""); + } + + #[test] + fn test_lines() { + let in_buf = b"a\nb\nc"; + let mut reader = BufReader::with_capacity(2, in_buf); + let mut it = reader.lines(); + assert_eq!(it.next(), Some(Ok("a".to_string()))); + assert_eq!(it.next(), Some(Ok("b".to_string()))); + assert_eq!(it.next(), Some(Ok("c".to_string()))); + assert_eq!(it.next(), None); + } + + #[test] + fn test_short_reads() { + let inner = ShortReader{lengths: vec![0, 1, 2, 0, 1, 0]}; + let mut reader = BufReader::new(inner); + let mut buf = [0, 0]; + assert_eq!(reader.read(&mut buf), Ok(0)); + assert_eq!(reader.read(&mut buf), Ok(1)); + assert_eq!(reader.read(&mut buf), Ok(2)); + assert_eq!(reader.read(&mut buf), Ok(0)); + assert_eq!(reader.read(&mut buf), Ok(1)); + assert_eq!(reader.read(&mut buf), Ok(0)); + assert_eq!(reader.read(&mut buf), Ok(0)); + } + + #[test] + fn read_char_buffered() { + let buf = [195u8, 159u8]; + let mut reader = BufReader::with_capacity(1, &buf[]); + assert_eq!(reader.chars().next(), Some(Ok('ß'))); + } + + #[test] + fn test_chars() { + let buf = [195u8, 159u8, b'a']; + let mut reader = BufReader::with_capacity(1, &buf[]); + let mut it = reader.chars(); + assert_eq!(it.next(), Some(Ok('ß'))); + assert_eq!(it.next(), Some(Ok('a'))); + assert_eq!(it.next(), None); + } + + #[test] + #[should_fail] + fn dont_panic_in_drop_on_panicked_flush() { + struct FailFlushWriter; + + impl Write for FailFlushWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { Ok(buf.len()) } + fn flush(&mut self) -> io::Result<()> { + Err(io::Error::last_os_error()) + } + } + + let writer = FailFlushWriter; + let _writer = BufWriter::new(writer); + + // If writer panics *again* due to the flush error then the process will + // abort. + panic!(); + } + + #[bench] + fn bench_buffered_reader(b: &mut test::Bencher) { + b.iter(|| { + BufReader::new(io::empty()) + }); + } + + #[bench] + fn bench_buffered_writer(b: &mut test::Bencher) { + b.iter(|| { + BufWriter::new(io::sink()) + }); + } + + #[bench] + fn bench_buffered_stream(b: &mut test::Bencher) { + let mut buf = Cursor::new(Vec::new()); + b.iter(|| { + BufStream::new(&mut buf); + }); + } +} diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs new file mode 100644 index 000000000000..9f3655de20fc --- /dev/null +++ b/src/libstd/io/cursor.rs @@ -0,0 +1,408 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(missing_copy_implementations)] + +use prelude::v1::*; +use io::prelude::*; + +use cmp; +use io::{self, SeekFrom, Error, ErrorKind}; +use iter::repeat; +use num::Int; +use slice; + +/// A `Cursor` is a type which wraps another I/O object to provide a `Seek` +/// implementation. +/// +/// Cursors are currently typically used with memory buffer objects in order to +/// allow `Seek` plus `Read` and `Write` implementations. For example, common +/// cursor types include: +/// +/// * `Cursor>` +/// * `Cursor<&[u8]>` +/// +/// Implementations of the I/O traits for `Cursor` are not currently generic +/// over `T` itself. Instead, specific implementations are provided for various +/// in-memory buffer types like `Vec` and `&[u8]`. +pub struct Cursor { + inner: T, + pos: u64, +} + +impl Cursor { + /// Create a new cursor wrapping the provided underlying I/O object. + pub fn new(inner: T) -> Cursor { + Cursor { pos: 0, inner: inner } + } + + /// Consume this cursor, returning the underlying value. + pub fn into_inner(self) -> T { self.inner } + + /// Get a reference to the underlying value in this cursor. + pub fn get_ref(&self) -> &T { &self.inner } + + /// Get a mutable reference to the underlying value in this cursor. + /// + /// Care should be taken to avoid modifying the internal I/O state of the + /// underlying value as it may corrupt this cursor's position. + pub fn get_mut(&mut self) -> &mut T { &mut self.inner } + + /// Returns the current value of this cursor + pub fn position(&self) -> u64 { self.pos } + + /// Sets the value of this cursor + pub fn set_position(&mut self, pos: u64) { self.pos = pos; } +} + +macro_rules! seek { + () => { + fn seek(&mut self, style: SeekFrom) -> io::Result { + let pos = match style { + SeekFrom::Start(n) => { self.pos = n; return Ok(n) } + SeekFrom::End(n) => self.inner.len() as i64 + n, + SeekFrom::Current(n) => self.pos as i64 + n, + }; + + if pos < 0 { + Err(Error::new(ErrorKind::InvalidInput, + "invalid seek to a negative position", + None)) + } else { + self.pos = pos as u64; + Ok(self.pos) + } + } + } +} + +impl<'a> io::Seek for Cursor<&'a [u8]> { seek!(); } +impl<'a> io::Seek for Cursor<&'a mut [u8]> { seek!(); } +impl io::Seek for Cursor> { seek!(); } + +macro_rules! read { + () => { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let n = try!(Read::read(&mut try!(self.fill_buf()), buf)); + self.pos += n as u64; + Ok(n) + } + } +} + +impl<'a> Read for Cursor<&'a [u8]> { read!(); } +impl<'a> Read for Cursor<&'a mut [u8]> { read!(); } +impl Read for Cursor> { read!(); } + +macro_rules! buffer { + () => { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + let amt = cmp::min(self.pos, self.inner.len() as u64); + Ok(&self.inner[(amt as usize)..]) + } + fn consume(&mut self, amt: usize) { self.pos += amt as u64; } + } +} + +impl<'a> BufRead for Cursor<&'a [u8]> { buffer!(); } +impl<'a> BufRead for Cursor<&'a mut [u8]> { buffer!(); } +impl<'a> BufRead for Cursor> { buffer!(); } + +impl<'a> Write for Cursor<&'a mut [u8]> { + fn write(&mut self, data: &[u8]) -> io::Result { + let pos = cmp::min(self.pos, self.inner.len() as u64); + let amt = try!((&mut self.inner[(pos as usize)..]).write(data)); + self.pos += amt as u64; + Ok(amt) + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } +} + +impl Write for Cursor> { + fn write(&mut self, buf: &[u8]) -> io::Result { + // Make sure the internal buffer is as least as big as where we + // currently are + let pos = self.position(); + let amt = pos.saturating_sub(self.inner.len() as u64); + self.inner.extend(repeat(0).take(amt as usize)); + + // Figure out what bytes will be used to overwrite what's currently + // there (left), and what will be appended on the end (right) + let space = self.inner.len() - pos as usize; + let (left, right) = buf.split_at(cmp::min(space, buf.len())); + slice::bytes::copy_memory(&mut self.inner[(pos as usize)..], left); + self.inner.push_all(right); + + // Bump us forward + self.set_position(pos + buf.len() as u64); + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } +} + + +#[cfg(test)] +mod tests { + use core::prelude::*; + + use io::prelude::*; + use io::{Cursor, SeekFrom}; + use vec::Vec; + + #[test] + fn test_vec_writer() { + let mut writer = Vec::new(); + assert_eq!(writer.write(&[0]), Ok(1)); + assert_eq!(writer.write(&[1, 2, 3]), Ok(3)); + assert_eq!(writer.write(&[4, 5, 6, 7]), Ok(4)); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(writer, b); + } + + #[test] + fn test_mem_writer() { + let mut writer = Cursor::new(Vec::new()); + assert_eq!(writer.write(&[0]), Ok(1)); + assert_eq!(writer.write(&[1, 2, 3]), Ok(3)); + assert_eq!(writer.write(&[4, 5, 6, 7]), Ok(4)); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(&writer.get_ref()[], b); + } + + #[test] + fn test_buf_writer() { + let mut buf = [0 as u8; 9]; + { + let mut writer = Cursor::new(&mut buf[]); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[0]), Ok(1)); + assert_eq!(writer.position(), 1); + assert_eq!(writer.write(&[1, 2, 3]), Ok(3)); + assert_eq!(writer.write(&[4, 5, 6, 7]), Ok(4)); + assert_eq!(writer.position(), 8); + assert_eq!(writer.write(&[]), Ok(0)); + assert_eq!(writer.position(), 8); + + assert_eq!(writer.write(&[8, 9]), Ok(1)); + assert_eq!(writer.write(&[10]), Ok(0)); + } + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; + assert_eq!(buf, b); + } + + #[test] + fn test_buf_writer_seek() { + let mut buf = [0 as u8; 8]; + { + let mut writer = Cursor::new(&mut buf[]); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[1]), Ok(1)); + assert_eq!(writer.position(), 1); + + assert_eq!(writer.seek(SeekFrom::Start(2)), Ok(2)); + assert_eq!(writer.position(), 2); + assert_eq!(writer.write(&[2]), Ok(1)); + assert_eq!(writer.position(), 3); + + assert_eq!(writer.seek(SeekFrom::Current(-2)), Ok(1)); + assert_eq!(writer.position(), 1); + assert_eq!(writer.write(&[3]), Ok(1)); + assert_eq!(writer.position(), 2); + + assert_eq!(writer.seek(SeekFrom::End(-1)), Ok(7)); + assert_eq!(writer.position(), 7); + assert_eq!(writer.write(&[4]), Ok(1)); + assert_eq!(writer.position(), 8); + + } + let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4]; + assert_eq!(buf, b); + } + + #[test] + fn test_buf_writer_error() { + let mut buf = [0 as u8; 2]; + let mut writer = Cursor::new(&mut buf[]); + assert_eq!(writer.write(&[0]), Ok(1)); + assert_eq!(writer.write(&[0, 0]), Ok(1)); + assert_eq!(writer.write(&[0, 0]), Ok(0)); + } + + #[test] + fn test_mem_reader() { + let mut reader = Cursor::new(vec!(0u8, 1, 2, 3, 4, 5, 6, 7)); + let mut buf = []; + assert_eq!(reader.read(&mut buf), Ok(0)); + assert_eq!(reader.position(), 0); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf), Ok(1)); + assert_eq!(reader.position(), 1); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf), Ok(4)); + assert_eq!(reader.position(), 5); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf, b); + assert_eq!(reader.read(&mut buf), Ok(3)); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert_eq!(reader.read(&mut buf), Ok(0)); + } + + #[test] + fn read_to_end() { + let mut reader = Cursor::new(vec!(0u8, 1, 2, 3, 4, 5, 6, 7)); + let mut v = Vec::new(); + reader.read_to_end(&mut v).ok().unwrap(); + assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]); + } + + #[test] + fn test_slice_reader() { + let in_buf = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; + let mut reader = &mut in_buf.as_slice(); + let mut buf = []; + assert_eq!(reader.read(&mut buf), Ok(0)); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf), Ok(1)); + assert_eq!(reader.len(), 7); + let b: &[_] = &[0]; + assert_eq!(buf.as_slice(), b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf), Ok(4)); + assert_eq!(reader.len(), 3); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf.as_slice(), b); + assert_eq!(reader.read(&mut buf), Ok(3)); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert_eq!(reader.read(&mut buf), Ok(0)); + } + + #[test] + fn test_buf_reader() { + let in_buf = vec![0u8, 1, 2, 3, 4, 5, 6, 7]; + let mut reader = Cursor::new(in_buf.as_slice()); + let mut buf = []; + assert_eq!(reader.read(&mut buf), Ok(0)); + assert_eq!(reader.position(), 0); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf), Ok(1)); + assert_eq!(reader.position(), 1); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf), Ok(4)); + assert_eq!(reader.position(), 5); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf, b); + assert_eq!(reader.read(&mut buf), Ok(3)); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert_eq!(reader.read(&mut buf), Ok(0)); + } + + #[test] + fn test_read_char() { + let b = b"Vi\xE1\xBB\x87t"; + let mut c = Cursor::new(b).chars(); + assert_eq!(c.next(), Some(Ok('V'))); + assert_eq!(c.next(), Some(Ok('i'))); + assert_eq!(c.next(), Some(Ok('ệ'))); + assert_eq!(c.next(), Some(Ok('t'))); + assert_eq!(c.next(), None); + } + + #[test] + fn test_read_bad_char() { + let b = b"\x80"; + let mut c = Cursor::new(b).chars(); + assert!(c.next().unwrap().is_err()); + } + + #[test] + fn seek_past_end() { + let buf = [0xff]; + let mut r = Cursor::new(&buf[]); + assert_eq!(r.seek(SeekFrom::Start(10)), Ok(10)); + assert_eq!(r.read(&mut [0]), Ok(0)); + + let mut r = Cursor::new(vec!(10u8)); + assert_eq!(r.seek(SeekFrom::Start(10)), Ok(10)); + assert_eq!(r.read(&mut [0]), Ok(0)); + + let mut buf = [0]; + let mut r = Cursor::new(&mut buf[]); + assert_eq!(r.seek(SeekFrom::Start(10)), Ok(10)); + assert_eq!(r.write(&[3]), Ok(0)); + } + + #[test] + fn seek_before_0() { + let buf = [0xff_u8]; + let mut r = Cursor::new(&buf[]); + assert!(r.seek(SeekFrom::End(-2)).is_err()); + + let mut r = Cursor::new(vec!(10u8)); + assert!(r.seek(SeekFrom::End(-2)).is_err()); + + let mut buf = [0]; + let mut r = Cursor::new(&mut buf[]); + assert!(r.seek(SeekFrom::End(-2)).is_err()); + } + + #[test] + fn test_seekable_mem_writer() { + let mut writer = Cursor::new(Vec::::new()); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[0]), Ok(1)); + assert_eq!(writer.position(), 1); + assert_eq!(writer.write(&[1, 2, 3]), Ok(3)); + assert_eq!(writer.write(&[4, 5, 6, 7]), Ok(4)); + assert_eq!(writer.position(), 8); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(&writer.get_ref()[], b); + + assert_eq!(writer.seek(SeekFrom::Start(0)), Ok(0)); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[3, 4]), Ok(2)); + let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7]; + assert_eq!(&writer.get_ref()[], b); + + assert_eq!(writer.seek(SeekFrom::Current(1)), Ok(3)); + assert_eq!(writer.write(&[0, 1]), Ok(2)); + let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7]; + assert_eq!(&writer.get_ref()[], b); + + assert_eq!(writer.seek(SeekFrom::End(-1)), Ok(7)); + assert_eq!(writer.write(&[1, 2]), Ok(2)); + let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2]; + assert_eq!(&writer.get_ref()[], b); + + assert_eq!(writer.seek(SeekFrom::End(1)), Ok(10)); + assert_eq!(writer.write(&[1]), Ok(1)); + let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1]; + assert_eq!(&writer.get_ref()[], b); + } + + #[test] + fn vec_seek_past_end() { + let mut r = Cursor::new(Vec::new()); + assert_eq!(r.seek(SeekFrom::Start(10)), Ok(10)); + assert_eq!(r.write(&[3]), Ok(1)); + } + + #[test] + fn vec_seek_before_0() { + let mut r = Cursor::new(Vec::new()); + assert!(r.seek(SeekFrom::End(-2)).is_err()); + } +} diff --git a/src/libstd/io/error.rs b/src/libstd/io/error.rs new file mode 100644 index 000000000000..9f3cd8c8b15d --- /dev/null +++ b/src/libstd/io/error.rs @@ -0,0 +1,183 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use boxed::Box; +use clone::Clone; +use error::Error as StdError; +use fmt; +use option::Option::{self, Some, None}; +use result; +use string::String; +use sys; + +/// A type for results generated by I/O related functions where the `Err` type +/// is hard-wired to `io::Error`. +/// +/// This typedef is generally used to avoid writing out `io::Error` directly and +/// is otherwise a direct mapping to `std::result::Result`. +pub type Result = result::Result; + +/// The error type for I/O operations of the `Read`, `Write`, `Seek`, and +/// associated traits. +/// +/// Errors mostly originate from the underlying OS, but custom instances of +/// `Error` can be created with crafted error messages and a particular value of +/// `ErrorKind`. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct Error { + repr: Repr, +} + +#[derive(PartialEq, Eq, Clone, Debug)] +enum Repr { + Os(i32), + Custom(Box), +} + +#[derive(PartialEq, Eq, Clone, Debug)] +struct Custom { + kind: ErrorKind, + desc: &'static str, + detail: Option +} + +/// A list specifying general categories of I/O error. +#[derive(Copy, PartialEq, Eq, Clone, Debug)] +pub enum ErrorKind { + /// The file was not found. + FileNotFound, + /// The file permissions disallowed access to this file. + PermissionDenied, + /// The connection was refused by the remote server. + ConnectionRefused, + /// The connection was reset by the remote server. + ConnectionReset, + /// The connection was aborted (terminated) by the remote server. + ConnectionAborted, + /// The network operation failed because it was not connected yet. + NotConnected, + /// The operation failed because a pipe was closed. + BrokenPipe, + /// A file already existed with that name. + PathAlreadyExists, + /// No file exists at that location. + PathDoesntExist, + /// The path did not specify the type of file that this operation required. + /// For example, attempting to copy a directory with the `fs::copy()` + /// operation will fail with this error. + MismatchedFileTypeForOperation, + /// The operation temporarily failed (for example, because a signal was + /// received), and retrying may succeed. + ResourceUnavailable, + /// A parameter was incorrect in a way that caused an I/O error not part of + /// this list. + InvalidInput, + /// The I/O operation's timeout expired, causing it to be canceled. + TimedOut, + /// An error returned when an operation could not be completed because a + /// call to `write` returned `Ok(0)`. + /// + /// This typically means that an operation could only succeed if it wrote a + /// particular number of bytes but only a smaller number of bytes could be + /// written. + WriteZero, + /// This operation was interrupted + Interrupted, + /// Any I/O error not part of this list. + Other, +} + +impl Error { + /// Creates a new custom error from a specified kind/description/detail. + pub fn new(kind: ErrorKind, + description: &'static str, + detail: Option) -> Error { + Error { + repr: Repr::Custom(Box::new(Custom { + kind: kind, + desc: description, + detail: detail, + })) + } + } + + /// Returns an error representing the last OS error which occurred. + /// + /// This function reads the value of `errno` for the target platform (e.g. + /// `GetLastError` on Windows) and will return a corresponding instance of + /// `Error` for the error code. + pub fn last_os_error() -> Error { + Error::from_os_error(sys::os::errno() as i32) + } + + /// Creates a new instance of an `Error` from a particular OS error code. + pub fn from_os_error(code: i32) -> Error { + Error { repr: Repr::Os(code) } + } + + /// Return the corresponding `ErrorKind` for this error. + pub fn kind(&self) -> ErrorKind { + match self.repr { + Repr::Os(code) => sys::decode_error_kind(code), + Repr::Custom(ref c) => c.kind, + } + } + + /// Returns a short description for this error message + pub fn description(&self) -> &str { + match self.repr { + Repr::Os(..) => "os error", + Repr::Custom(ref c) => c.desc, + } + } + + /// Returns a detailed error message for this error (if one is available) + pub fn detail(&self) -> Option { + match self.repr { + Repr::Os(code) => Some(sys::os::error_string(code)), + Repr::Custom(ref s) => s.detail.clone(), + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self.repr { + Repr::Os(code) => { + let detail = sys::os::error_string(code); + write!(fmt, "{} (os error {})", detail, code) + } + Repr::Custom(ref c) => { + match **c { + Custom { + kind: ErrorKind::Other, + desc: "unknown error", + detail: Some(ref detail) + } => { + write!(fmt, "{}", detail) + } + Custom { detail: None, desc, .. } => + write!(fmt, "{}", desc), + Custom { detail: Some(ref detail), desc, .. } => + write!(fmt, "{} ({})", desc, detail) + } + } + } + } +} + +impl StdError for Error { + fn description(&self) -> &str { + match self.repr { + Repr::Os(..) => "os error", + Repr::Custom(ref c) => c.desc, + } + } +} diff --git a/src/libstd/io/impls.rs b/src/libstd/io/impls.rs new file mode 100644 index 000000000000..7f3ce7924c1c --- /dev/null +++ b/src/libstd/io/impls.rs @@ -0,0 +1,88 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::prelude::*; + +use boxed::Box; +use cmp; +use io::{self, SeekFrom, Read, Write, Seek, BufRead}; +use mem; +use slice; +use vec::Vec; + +// ============================================================================= +// Forwarding implementations + +impl<'a, R: Read + ?Sized> Read for &'a mut R { + fn read(&mut self, buf: &mut [u8]) -> io::Result { (**self).read(buf) } +} +impl<'a, W: Write + ?Sized> Write for &'a mut W { + fn write(&mut self, buf: &[u8]) -> io::Result { (**self).write(buf) } + fn flush(&mut self) -> io::Result<()> { (**self).flush() } +} +impl<'a, S: Seek + ?Sized> Seek for &'a mut S { + fn seek(&mut self, pos: SeekFrom) -> io::Result { (**self).seek(pos) } +} +impl<'a, B: BufRead + ?Sized> BufRead for &'a mut B { + fn fill_buf(&mut self) -> io::Result<&[u8]> { (**self).fill_buf() } + fn consume(&mut self, amt: usize) { (**self).consume(amt) } +} + +impl Read for Box { + fn read(&mut self, buf: &mut [u8]) -> io::Result { (**self).read(buf) } +} +impl Write for Box { + fn write(&mut self, buf: &[u8]) -> io::Result { (**self).write(buf) } + fn flush(&mut self) -> io::Result<()> { (**self).flush() } +} +impl Seek for Box { + fn seek(&mut self, pos: SeekFrom) -> io::Result { (**self).seek(pos) } +} +impl BufRead for Box { + fn fill_buf(&mut self) -> io::Result<&[u8]> { (**self).fill_buf() } + fn consume(&mut self, amt: usize) { (**self).consume(amt) } +} + +// ============================================================================= +// In-memory buffer implementations + +impl<'a> Read for &'a [u8] { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let amt = cmp::min(buf.len(), self.len()); + let (a, b) = self.split_at(amt); + slice::bytes::copy_memory(buf, a); + *self = b; + Ok(amt) + } +} + +impl<'a> BufRead for &'a [u8] { + fn fill_buf(&mut self) -> io::Result<&[u8]> { Ok(*self) } + fn consume(&mut self, amt: usize) { *self = &self[amt..]; } +} + +impl<'a> Write for &'a mut [u8] { + fn write(&mut self, data: &[u8]) -> io::Result { + let amt = cmp::min(data.len(), self.len()); + let (a, b) = mem::replace(self, &mut []).split_at_mut(amt); + slice::bytes::copy_memory(a, &data[..amt]); + *self = b; + Ok(amt) + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } +} + +impl Write for Vec { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.push_all(buf); + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } +} diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs new file mode 100644 index 000000000000..0832206a48b6 --- /dev/null +++ b/src/libstd/io/mod.rs @@ -0,0 +1,948 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Traits, helpers, and type definitions for core I/O functionality. +//! +//! > **NOTE**: This module is very much a work in progress and is under active +//! > development. At this time it is still recommended to use the `old_io` +//! > module while the details of this module shake out. + +#![unstable(feature = "io", + reason = "this new I/O module is still under active deveopment and \ + APIs are subject to tweaks fairly regularly")] + +use cmp; +use unicode::str as core_str; +use error::Error as StdError; +use fmt; +use iter::Iterator; +use marker::Sized; +use mem; +use ops::{Drop, FnOnce}; +use option::Option::{self, Some, None}; +use ptr::PtrExt; +use result::Result::{Ok, Err}; +use result; +use slice::{self, SliceExt}; +use string::String; +use str::{self, StrExt}; +use vec::Vec; + +pub use self::buffered::{BufReader, BufWriter, BufStream, LineWriter}; +pub use self::buffered::IntoInnerError; +pub use self::cursor::Cursor; +pub use self::error::{Result, Error, ErrorKind}; +pub use self::util::{copy, sink, Sink, empty, Empty, repeat, Repeat}; + +pub mod prelude; +mod buffered; +mod cursor; +mod error; +mod impls; +mod util; + +const DEFAULT_BUF_SIZE: usize = 64 * 1024; + +// Acquires a slice of the vector `v` from its length to its capacity +// (uninitialized data), reads into it, and then updates the length. +// +// This function is leveraged to efficiently read some bytes into a destination +// vector without extra copying and taking advantage of the space that's already +// in `v`. +// +// The buffer we're passing down, however, is pointing at uninitialized data +// (the end of a `Vec`), and many operations will be *much* faster if we don't +// have to zero it out. In order to prevent LLVM from generating an `undef` +// value when reads happen from this uninitialized memory, we force LLVM to +// think it's initialized by sending it through a black box. This should prevent +// actual undefined behavior after optimizations. +fn with_end_to_cap(v: &mut Vec, f: F) -> Result + where F: FnOnce(&mut [u8]) -> Result +{ + unsafe { + let n = try!(f({ + let base = v.as_mut_ptr().offset(v.len() as isize); + black_box(slice::from_raw_mut_buf(mem::copy_lifetime(v, &base), + v.capacity() - v.len())) + })); + + // If the closure (typically a `read` implementation) reported that it + // read a larger number of bytes than the vector actually has, we need + // to be sure to clamp the vector to at most its capacity. + let new_len = cmp::min(v.capacity(), v.len() + n); + v.set_len(new_len); + return Ok(n); + } + + // Semi-hack used to prevent LLVM from retaining any assumptions about + // `dummy` over this function call + unsafe fn black_box(mut dummy: T) -> T { + asm!("" :: "r"(&mut dummy) : "memory"); + dummy + } +} + +// A few methods below (read_to_string, read_line) will append data into a +// `String` buffer, but we need to be pretty careful when doing this. The +// implementation will just call `.as_mut_vec()` and then delegate to a +// byte-oriented reading method, but we must ensure that when returning we never +// leave `buf` in a state such that it contains invalid UTF-8 in its bounds. +// +// To this end, we use an RAII guard (to protect against panics) which updates +// the length of the string when it is dropped. This guard initially truncates +// the string to the prior length and only afer we've validated that the +// new contents are valid UTF-8 do we allow it to set a longer length. +// +// The unsafety in this function is twofold: +// +// 1. We're looking at the raw bytes of `buf`, so we take on the burden of UTF-8 +// checks. +// 2. We're passing a raw buffer to the function `f`, and it is expected that +// the function only *appends* bytes to the buffer. We'll get undefined +// behavior if existing bytes are overwritten to have non-UTF-8 data. +fn append_to_string(buf: &mut String, f: F) -> Result<()> + where F: FnOnce(&mut Vec) -> Result<()> +{ + struct Guard<'a> { s: &'a mut Vec, len: usize } + #[unsafe_destructor] + impl<'a> Drop for Guard<'a> { + fn drop(&mut self) { + unsafe { self.s.set_len(self.len); } + } + } + + unsafe { + let mut g = Guard { len: buf.len(), s: buf.as_mut_vec() }; + let ret = f(g.s); + if str::from_utf8(&g.s[g.len..]).is_err() { + ret.and_then(|()| { + Err(Error::new(ErrorKind::InvalidInput, + "stream did not contain valid UTF-8", None)) + }) + } else { + g.len = g.s.len(); + ret + } + } +} + +fn read_to_end(r: &mut R, buf: &mut Vec) -> Result<()> { + loop { + if buf.capacity() == buf.len() { + buf.reserve(DEFAULT_BUF_SIZE); + } + match with_end_to_cap(buf, |b| r.read(b)) { + Ok(0) => return Ok(()), + Ok(_) => {} + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } +} + +/// A trait for objects which are byte-oriented sources. +/// +/// Readers are defined by one method, `read`. Each call to `read` will attempt +/// to pull bytes from this source into a provided buffer. +/// +/// Readers are intended to be composable with one another. Many objects +/// throughout the I/O and related libraries take and provide types which +/// implement the `Read` trait. +pub trait Read { + /// Pull some bytes from this source into the specified buffer, returning + /// how many bytes were read. + /// + /// This function does not provide any guarantees about whether it blocks + /// waiting for data, but if an object needs to block for a read but cannot + /// it will typically signal this via an `Err` return value. + /// + /// If the return value of this method is `Ok(n)`, then it must be + /// guaranteed that `0 <= n <= buf.len()`. A nonzero `n` value indicates + /// that the buffer `buf` has ben filled in with `n` bytes of data from this + /// source. If `n` is `0`, then it can indicate one of two scenarios: + /// + /// 1. This reader has reached its "end of file" and will likely no longer + /// be able to produce bytes. Note that this does not mean that the + /// reader will *always* no longer be able to produce bytes. + /// 2. The buffer specified was 0 bytes in length. + /// + /// No guarantees are provided about the contents of `buf` when this + /// function is called, implementations cannot rely on any property of the + /// contents of `buf` being true. It is recommended that implementations + /// only write data to `buf` instead of reading its contents. + /// + /// # Errors + /// + /// If this function encounters any form of I/O or other error, an error + /// variant will be returned. If an error is returned then it must be + /// guaranteed that no bytes were read. + fn read(&mut self, buf: &mut [u8]) -> Result; + + /// Read all bytes until EOF in this source, placing them into `buf`. + /// + /// All bytes read from this source will be appended to the specified buffer + /// `buf`. This function will return a call to `read` either: + /// + /// 1. Returns `Ok(0)`. + /// 2. Returns an error which is not of the kind `ErrorKind::Interrupted`. + /// + /// Until one of these conditions is met the function will continuously + /// invoke `read` to append more data to `buf`. + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// `ErrorKind::Interrupted` then the error is ignored and the operation + /// will continue. + /// + /// If any other read error is encountered then this function immediately + /// returns. Any bytes which have already been read will be appended to + /// `buf`. + fn read_to_end(&mut self, buf: &mut Vec) -> Result<()> { + read_to_end(self, buf) + } + + /// Read all bytes until EOF in this source, placing them into `buf`. + /// + /// # Errors + /// + /// If the data in this stream is *not* valid UTF-8 then an error is + /// returned and `buf` is unchanged. + /// + /// See `read_to_end` for other error semantics. + fn read_to_string(&mut self, buf: &mut String) -> Result<()> { + // Note that we do *not* call `.read_to_end()` here. We are passing + // `&mut Vec` (the raw contents of `buf`) into the `read_to_end` + // method to fill it up. An arbitrary implementation could overwrite the + // entire contents of the vector, not just append to it (which is what + // we are expecting). + // + // To prevent extraneously checking the UTF-8-ness of the entire buffer + // we pass it to our hardcoded `read_to_end` implementation which we + // know is guaranteed to only read data into the end of the buffer. + append_to_string(buf, |b| read_to_end(self, b)) + } +} + +/// Extension methods for all instances of `Read`, typically imported through +/// `std::io::prelude::*`. +pub trait ReadExt: Read + Sized { + /// Create a "by reference" adaptor for this instance of `Read`. + /// + /// The returned adaptor also implements `Read` and will simply borrow this + /// current reader. + fn by_ref(&mut self) -> &mut Self { self } + + /// Transform this `Read` instance to an `Iterator` over its bytes. + /// + /// The returned type implements `Iterator` where the `Item` is `Result`. The yielded item is `Ok` if a byte was successfully read and + /// `Err` otherwise for I/O errors. EOF is mapped to returning `None` from + /// this iterator. + fn bytes(self) -> Bytes { + Bytes { inner: self } + } + + /// Transform this `Read` instance to an `Iterator` over `char`s. + /// + /// This adaptor will attempt to interpret this reader as an UTF-8 encoded + /// sequence of characters. The returned iterator will return `None` once + /// EOF is reached for this reader. Otherwise each element yielded will be a + /// `Result` where `E` may contain information about what I/O error + /// occurred or where decoding failed. + /// + /// Currently this adaptor will discard intermediate data read, and should + /// be avoided if this is not desired. + fn chars(self) -> Chars { + Chars { inner: self } + } + + /// Create an adaptor which will chain this stream with another. + /// + /// The returned `Read` instance will first read all bytes from this object + /// until EOF is encountered. Afterwards the output is equivalent to the + /// output of `next`. + fn chain(self, next: R) -> Chain { + Chain { first: self, second: next, done_first: false } + } + + /// Create an adaptor which will read at most `limit` bytes from it. + /// + /// This function returns a new instance of `Read` which will read at most + /// `limit` bytes, after which it will always return EOF (`Ok(0)`). Any + /// read errors will not count towards the number of bytes read and future + /// calls to `read` may succeed. + fn take(self, limit: u64) -> Take { + Take { inner: self, limit: limit } + } + + /// Creates a reader adaptor which will write all read data into the given + /// output stream. + /// + /// Whenever the returned `Read` instance is read it will write the read + /// data to `out`. The current semantics of this implementation imply that + /// a `write` error will not report how much data was initially read. + fn tee(self, out: W) -> Tee { + Tee { reader: self, writer: out } + } +} + +impl ReadExt for T {} + +/// A trait for objects which are byte-oriented sinks. +/// +/// The `write` method will attempt to write some data into the object, +/// returning how many bytes were successfully written. +/// +/// The `flush` method is useful for adaptors and explicit buffers themselves +/// for ensuring that all buffered data has been pushed out to the "true sink". +/// +/// Writers are intended to be composable with one another. Many objects +/// throughout the I/O and related libraries take and provide types which +/// implement the `Write` trait. +pub trait Write { + /// Write a buffer into this object, returning how many bytes were written. + /// + /// This function will attempt to write the entire contents of `buf`, but + /// the entire write may not succeed, or the write may also generate an + /// error. A call to `write` represents *at most one* attempt to write to + /// any wrapped object. + /// + /// Calls to `write` are not guaranteed to block waiting for data to be + /// written, and a write which would otherwise block can indicated through + /// an `Err` variant. + /// + /// If the return value is `Ok(n)` then it must be guaranteed that + /// `0 <= n <= buf.len()`. A return value of `0` typically means that the + /// underlying object is no longer able to accept bytes and will likely not + /// be able to in the future as well, or that the buffer provided is empty. + /// + /// # Errors + /// + /// Each call to `write` may generate an I/O error indicating that the + /// operation could not be completed. If an error is returned then no bytes + /// in the buffer were written to this writer. + /// + /// It is **not** considered an error if the entire buffer could not be + /// written to this writer. + fn write(&mut self, buf: &[u8]) -> Result; + + /// Flush this output stream, ensuring that all intermediately buffered + /// contents reach their destination. + /// + /// # Errors + /// + /// It is considered an error if not all bytes could be written due to + /// I/O errors or EOF being reached. + fn flush(&mut self) -> Result<()>; + + /// Attempts to write an entire buffer into this write. + /// + /// This method will continuously call `write` while there is more data to + /// write. This method will not return until the entire buffer has been + /// successfully written or an error occurs. The first error generated from + /// this method will be returned. + /// + /// # Errors + /// + /// This function will return the first error that `write` returns. + fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { + while buf.len() > 0 { + match self.write(buf) { + Ok(0) => return Err(Error::new(ErrorKind::WriteZero, + "failed to write whole buffer", + None)), + Ok(n) => buf = &buf[n..], + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Writes a formatted string into this writer, returning any error + /// encountered. + /// + /// This method is primarily used to interface with the `format_args!` + /// macro, but it is rare that this should explicitly be called. The + /// `write!` macro should be favored to invoke this method instead. + /// + /// This function internally uses the `write_all` method on this trait and + /// hence will continuously write data so long as no errors are received. + /// This also means that partial writes are not indicated in this signature. + /// + /// # Errors + /// + /// This function will return any I/O error reported while formatting. + fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<()> { + // Create a shim which translates a Writer to a fmt::Writer and saves + // off I/O errors. instead of discarding them + struct Adaptor<'a, T: ?Sized + 'a> { + inner: &'a mut T, + error: Result<()>, + } + + impl<'a, T: Write + ?Sized> fmt::Writer for Adaptor<'a, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_all(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut output = Adaptor { inner: self, error: Ok(()) }; + match fmt::write(&mut output, fmt) { + Ok(()) => Ok(()), + Err(..) => output.error + } + } +} + +/// Extension methods for all instances of `Write`, typically imported through +/// `std::io::prelude::*`. +pub trait WriteExt: Write + Sized { + /// Create a "by reference" adaptor for this instance of `Write`. + /// + /// The returned adaptor also implements `Write` and will simply borrow this + /// current writer. + fn by_ref(&mut self) -> &mut Self { self } + + /// Creates a new writer which will write all data to both this writer and + /// another writer. + /// + /// All data written to the returned writer will both be written to `self` + /// as well as `other`. Note that the error semantics of the current + /// implementation do not precisely track where errors happen. For example + /// an error on the second call to `write` will not report that the first + /// call to `write` succeeded. + fn broadcast(self, other: W) -> Broadcast { + Broadcast { first: self, second: other } + } +} + +impl WriteExt for T {} + +/// An object implementing `Seek` internally has some form of cursor which can +/// be moved within a stream of bytes. +/// +/// The stream typically has a fixed size, allowing seeking relative to either +/// end or the current offset. +pub trait Seek { + /// Seek to an offset, in bytes, in a stream + /// + /// A seek beyond the end of a stream is allowed, but seeking before offset + /// 0 is an error. + /// + /// Seeking past the end of the stream does not modify the underlying + /// stream, but the next write may cause the previous data to be filled in + /// with a bit pattern. + /// + /// This method returns the new position within the stream if the seek + /// operation completed successfully. + /// + /// # Errors + /// + /// Seeking to a negative offset is considered an error + fn seek(&mut self, pos: SeekFrom) -> Result; +} + +/// Enumeration of possible methods to seek within an I/O object. +#[derive(Copy, PartialEq, Eq, Clone, Debug)] +pub enum SeekFrom { + /// Set the offset to the provided number of bytes. + Start(u64), + + /// Set the offset to the size of this object plus the specified number of + /// bytes. + /// + /// It is possible to seek beyond the end of an object, but is an error to + /// seek before byte 0. + End(i64), + + /// Set the offset to the current position plus the specified number of + /// bytes. + /// + /// It is possible to seek beyond the end of an object, but is an error to + /// seek before byte 0. + Current(i64), +} + +fn read_until(r: &mut R, delim: u8, buf: &mut Vec) + -> Result<()> { + loop { + let (done, used) = { + let available = match r.fill_buf() { + Ok(n) => n, + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e) + }; + match available.position_elem(&delim) { + Some(i) => { + buf.push_all(&available[..i + 1]); + (true, i + 1) + } + None => { + buf.push_all(available); + (false, available.len()) + } + } + }; + r.consume(used); + if done || used == 0 { + return Ok(()); + } + } +} + +/// A Buffer is a type of reader which has some form of internal buffering to +/// allow certain kinds of reading operations to be more optimized than others. +/// +/// This type extends the `Read` trait with a few methods that are not +/// possible to reasonably implement with purely a read interface. +pub trait BufRead: Read { + /// Fills the internal buffer of this object, returning the buffer contents. + /// + /// None of the contents will be "read" in the sense that later calling + /// `read` may return the same contents. + /// + /// The `consume` function must be called with the number of bytes that are + /// consumed from this buffer returned to ensure that the bytes are never + /// returned twice. + /// + /// An empty buffer returned indicates that the stream has reached EOF. + /// + /// # Errors + /// + /// This function will return an I/O error if the underlying reader was + /// read, but returned an error. + fn fill_buf(&mut self) -> Result<&[u8]>; + + /// Tells this buffer that `amt` bytes have been consumed from the buffer, + /// so they should no longer be returned in calls to `read`. + fn consume(&mut self, amt: usize); + + /// Read all bytes until the delimiter `byte` is reached. + /// + /// This function will continue to read (and buffer) bytes from the + /// underlying stream until the delimiter or EOF is found. Once found, all + /// bytes up to, and including, the delimiter (if found) will be appended to + /// `buf`. + /// + /// If this buffered reader is currently at EOF, then this function will not + /// place any more bytes into `buf` and will return `Ok(())`. + /// + /// # Errors + /// + /// This function will ignore all instances of `ErrorKind::Interrupted` and + /// will otherwise return any errors returned by `fill_buf`. + /// + /// If an I/O error is encountered then all bytes read so far will be + /// present in `buf` and its length will have been adjusted appropriately. + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result<()> { + read_until(self, byte, buf) + } + + /// Read all bytes until a newline byte (the 0xA byte) is reached. + /// + /// This function will continue to read (and buffer) bytes from the + /// underlying stream until the newline delimiter (the 0xA byte) or EOF is + /// found. Once found, all bytes up to, and including, the delimiter (if + /// found) will be appended to `buf`. + /// + /// If this reader is currently at EOF then this function will not modify + /// `buf` and will return `Ok(())`. + /// + /// # Errors + /// + /// This function has the same error semantics as `read_until` and will also + /// return an error if the read bytes are not valid UTF-8. If an I/O error + /// is encountered then `buf` may contain some bytes already read in the + /// event that all data read so far was valid UTF-8. + fn read_line(&mut self, buf: &mut String) -> Result<()> { + // Note that we are not calling the `.read_until` method here, but + // rather our hardcoded implementation. For more details as to why, see + // the comments in `read_to_end`. + append_to_string(buf, |b| read_until(self, b'\n', b)) + } +} + +/// Extension methods for all instances of `BufRead`, typically imported through +/// `std::io::prelude::*`. +pub trait BufReadExt: BufRead + Sized { + /// Returns an iterator over the contents of this reader split on the byte + /// `byte`. + /// + /// The iterator returned from this function will return instances of + /// `io::Result>`. Each vector returned will *not* have the + /// delimiter byte at the end. + /// + /// This function will yield errors whenever `read_until` would have also + /// yielded an error. + fn split(self, byte: u8) -> Split { + Split { buf: self, delim: byte } + } + + /// Returns an iterator over the lines of this reader. + /// + /// The iterator returned from this function will yield instances of + /// `io::Result`. Each string returned will *not* have a newline + /// byte (the 0xA byte) at the end. + /// + /// This function will yield errors whenever `read_string` would have also + /// yielded an error. + fn lines(self) -> Lines { + Lines { buf: self } + } +} + +impl BufReadExt for T {} + +/// A `Write` adaptor which will write data to multiple locations. +/// +/// For more information, see `WriteExt::broadcast`. +pub struct Broadcast { + first: T, + second: U, +} + +impl Write for Broadcast { + fn write(&mut self, data: &[u8]) -> Result { + let n = try!(self.first.write(data)); + // FIXME: what if the write fails? (we wrote something) + try!(self.second.write_all(&data[..n])); + Ok(n) + } + + fn flush(&mut self) -> Result<()> { + self.first.flush().and(self.second.flush()) + } +} + +/// Adaptor to chain together two instances of `Read`. +/// +/// For more information, see `ReadExt::chain`. +pub struct Chain { + first: T, + second: U, + done_first: bool, +} + +impl Read for Chain { + fn read(&mut self, buf: &mut [u8]) -> Result { + if !self.done_first { + match try!(self.first.read(buf)) { + 0 => { self.done_first = true; } + n => return Ok(n), + } + } + self.second.read(buf) + } +} + +/// Reader adaptor which limits the bytes read from an underlying reader. +/// +/// For more information, see `ReadExt::take`. +pub struct Take { + inner: T, + limit: u64, +} + +impl Take { + /// Returns the number of bytes that can be read before this instance will + /// return EOF. + /// + /// # Note + /// + /// This instance may reach EOF after reading fewer bytes than indiccated by + /// this method if the underlying `Read` instance reaches EOF. + pub fn limit(&self) -> u64 { self.limit } +} + +impl Read for Take { + fn read(&mut self, buf: &mut [u8]) -> Result { + let max = cmp::min(buf.len() as u64, self.limit) as usize; + let n = try!(self.inner.read(&mut buf[..max])); + self.limit -= n as u64; + Ok(n) + } +} + +/// An adaptor which will emit all read data to a specified writer as well. +/// +/// For more information see `ReadExt::tee` +pub struct Tee { + reader: R, + writer: W, +} + +impl Read for Tee { + fn read(&mut self, buf: &mut [u8]) -> Result { + let n = try!(self.reader.read(buf)); + // FIXME: what if the write fails? (we read something) + try!(self.writer.write_all(&buf[..n])); + Ok(n) + } +} + +/// A bridge from implementations of `Read` to an `Iterator` of `u8`. +/// +/// See `ReadExt::bytes` for more information. +pub struct Bytes { + inner: R, +} + +impl Iterator for Bytes { + type Item = Result; + + fn next(&mut self) -> Option> { + let mut buf = [0]; + match self.inner.read(&mut buf) { + Ok(0) => None, + Ok(..) => Some(Ok(buf[0])), + Err(e) => Some(Err(e)), + } + } +} + +/// A bridge from implementations of `Read` to an `Iterator` of `char`. +/// +/// See `ReadExt::chars` for more information. +pub struct Chars { + inner: R, +} + +/// An enumeration of possible errors that can be generated from the `Chars` +/// adapter. +#[derive(PartialEq, Clone, Debug)] +pub enum CharsError { + /// Variant representing that the underlying stream was read successfully + /// but it did not contain valid utf8 data. + NotUtf8, + + /// Variant representing that an I/O error occurred. + Other(Error), +} + +impl Iterator for Chars { + type Item = result::Result; + + fn next(&mut self) -> Option> { + let mut buf = [0]; + let first_byte = match self.inner.read(&mut buf) { + Ok(0) => return None, + Ok(..) => buf[0], + Err(e) => return Some(Err(CharsError::Other(e))), + }; + let width = core_str::utf8_char_width(first_byte); + if width == 1 { return Some(Ok(first_byte as char)) } + if width == 0 { return Some(Err(CharsError::NotUtf8)) } + let mut buf = [first_byte, 0, 0, 0]; + { + let mut start = 1; + while start < width { + match self.inner.read(&mut buf[start..width]) { + Ok(0) => return Some(Err(CharsError::NotUtf8)), + Ok(n) => start += n, + Err(e) => return Some(Err(CharsError::Other(e))), + } + } + } + Some(match str::from_utf8(&buf[..width]).ok() { + Some(s) => Ok(s.char_at(0)), + None => Err(CharsError::NotUtf8), + }) + } +} + +impl StdError for CharsError { + fn description(&self) -> &str { + match *self { + CharsError::NotUtf8 => "invalid utf8 encoding", + CharsError::Other(ref e) => e.description(), + } + } + fn cause(&self) -> Option<&StdError> { + match *self { + CharsError::NotUtf8 => None, + CharsError::Other(ref e) => e.cause(), + } + } +} + +impl fmt::Display for CharsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + CharsError::NotUtf8 => { + "byte stream did not contain valid utf8".fmt(f) + } + CharsError::Other(ref e) => e.fmt(f), + } + } +} + +/// An iterator over the contents of an instance of `BufRead` split on a +/// particular byte. +/// +/// See `BufReadExt::split` for more information. +pub struct Split { + buf: B, + delim: u8, +} + +impl Iterator for Split { + type Item = Result>; + + fn next(&mut self) -> Option>> { + let mut buf = Vec::new(); + match self.buf.read_until(self.delim, &mut buf) { + Ok(()) if buf.len() == 0 => None, + Ok(()) => { + if buf[buf.len() - 1] == self.delim { + buf.pop(); + } + Some(Ok(buf)) + } + Err(e) => Some(Err(e)) + } + } +} + +/// An iterator over the lines of an instance of `BufRead` split on a newline +/// byte. +/// +/// See `BufReadExt::lines` for more information. +pub struct Lines { + buf: B, +} + +impl Iterator for Lines { + type Item = Result; + + fn next(&mut self) -> Option> { + let mut buf = String::new(); + match self.buf.read_line(&mut buf) { + Ok(()) if buf.len() == 0 => None, + Ok(()) => { + if buf.ends_with("\n") { + buf.pop(); + } + Some(Ok(buf)) + } + Err(e) => Some(Err(e)) + } + } +} + +#[cfg(test)] +mod tests { + use prelude::v1::*; + use io::prelude::*; + use super::Cursor; + + #[test] + fn read_until() { + let mut buf = Cursor::new(b"12"); + let mut v = Vec::new(); + assert_eq!(buf.read_until(b'3', &mut v), Ok(())); + assert_eq!(v, b"12"); + + let mut buf = Cursor::new(b"1233"); + let mut v = Vec::new(); + assert_eq!(buf.read_until(b'3', &mut v), Ok(())); + assert_eq!(v, b"123"); + v.truncate(0); + assert_eq!(buf.read_until(b'3', &mut v), Ok(())); + assert_eq!(v, b"3"); + v.truncate(0); + assert_eq!(buf.read_until(b'3', &mut v), Ok(())); + assert_eq!(v, []); + } + + #[test] + fn split() { + let mut buf = Cursor::new(b"12"); + let mut s = buf.split(b'3'); + assert_eq!(s.next(), Some(Ok(vec![b'1', b'2']))); + assert_eq!(s.next(), None); + + let mut buf = Cursor::new(b"1233"); + let mut s = buf.split(b'3'); + assert_eq!(s.next(), Some(Ok(vec![b'1', b'2']))); + assert_eq!(s.next(), Some(Ok(vec![]))); + assert_eq!(s.next(), None); + } + + #[test] + fn read_line() { + let mut buf = Cursor::new(b"12"); + let mut v = String::new(); + assert_eq!(buf.read_line(&mut v), Ok(())); + assert_eq!(v, "12"); + + let mut buf = Cursor::new(b"12\n\n"); + let mut v = String::new(); + assert_eq!(buf.read_line(&mut v), Ok(())); + assert_eq!(v, "12\n"); + v.truncate(0); + assert_eq!(buf.read_line(&mut v), Ok(())); + assert_eq!(v, "\n"); + v.truncate(0); + assert_eq!(buf.read_line(&mut v), Ok(())); + assert_eq!(v, ""); + } + + #[test] + fn lines() { + let mut buf = Cursor::new(b"12"); + let mut s = buf.lines(); + assert_eq!(s.next(), Some(Ok("12".to_string()))); + assert_eq!(s.next(), None); + + let mut buf = Cursor::new(b"12\n\n"); + let mut s = buf.lines(); + assert_eq!(s.next(), Some(Ok("12".to_string()))); + assert_eq!(s.next(), Some(Ok(String::new()))); + assert_eq!(s.next(), None); + } + + #[test] + fn read_to_end() { + let mut c = Cursor::new(b""); + let mut v = Vec::new(); + assert_eq!(c.read_to_end(&mut v), Ok(())); + assert_eq!(v, []); + + let mut c = Cursor::new(b"1"); + let mut v = Vec::new(); + assert_eq!(c.read_to_end(&mut v), Ok(())); + assert_eq!(v, b"1"); + } + + #[test] + fn read_to_string() { + let mut c = Cursor::new(b""); + let mut v = String::new(); + assert_eq!(c.read_to_string(&mut v), Ok(())); + assert_eq!(v, ""); + + let mut c = Cursor::new(b"1"); + let mut v = String::new(); + assert_eq!(c.read_to_string(&mut v), Ok(())); + assert_eq!(v, "1"); + + let mut c = Cursor::new(b"\xff"); + let mut v = String::new(); + assert!(c.read_to_string(&mut v).is_err()); + } +} diff --git a/src/libstd/io/prelude.rs b/src/libstd/io/prelude.rs new file mode 100644 index 000000000000..475ada2ff84b --- /dev/null +++ b/src/libstd/io/prelude.rs @@ -0,0 +1,27 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The I/O Prelude +//! +//! The purpose of this module is to alleviate imports of many common I/O traits +//! by adding a glob import to the top of I/O heavy modules: +//! +//! ``` +//! use std::io::prelude::*; +//! ``` +//! +//! This module contains reexports of many core I/O traits such as `Read`, +//! `Write`, `ReadExt`, and `WriteExt`. Structures and functions are not +//! contained in this module. + +pub use super::{Read, ReadExt, Write, WriteExt, BufRead, BufReadExt}; + +// FIXME: pub use as `Seek` when the name isn't in the actual prelude any more +pub use super::Seek as NewSeek; diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs new file mode 100644 index 000000000000..3d342137c62d --- /dev/null +++ b/src/libstd/io/util.rs @@ -0,0 +1,153 @@ +// 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(missing_copy_implementations)] + +use prelude::v1::*; + +use io::{self, Read, Write, ErrorKind}; + +/// Copies the entire contents of a reader into a writer. +/// +/// This function will continuously read data from `r` and then write it into +/// `w` in a streaming fashion until `r` returns EOF. +/// +/// On success the total number of bytes that were copied from `r` to `w` is +/// returned. +/// +/// # Errors +/// +/// This function will return an error immediately if any call to `read` or +/// `write` returns an error. All instances of `ErrorKind::Interrupted` are +/// handled by this function and the underlying operation is retried. +pub fn copy(r: &mut R, w: &mut W) -> io::Result { + let mut buf = [0; super::DEFAULT_BUF_SIZE]; + let mut written = 0; + loop { + let len = match r.read(&mut buf) { + Ok(0) => return Ok(written), + Ok(len) => len, + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + }; + try!(w.write_all(&buf[..len])); + written += len as u64; + } +} + +/// A reader which is always at EOF. +pub struct Empty { _priv: () } + +/// Creates an instance of an empty reader. +/// +/// All reads from the returned reader will return `Ok(0)`. +pub fn empty() -> Empty { Empty { _priv: () } } + +impl Read for Empty { + fn read(&mut self, _buf: &mut [u8]) -> io::Result { Ok(0) } +} + +/// A reader which infinitely yields one byte. +pub struct Repeat { byte: u8 } + +/// Creates an instance of a reader that infinitely repeats one byte. +/// +/// All reads from this reader will succeed by filling the specified buffer with +/// the given byte. +pub fn repeat(byte: u8) -> Repeat { Repeat { byte: byte } } + +impl Read for Repeat { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + for slot in buf.iter_mut() { + *slot = self.byte; + } + Ok(buf.len()) + } +} + +/// A writer which will move data into the void. +pub struct Sink { _priv: () } + +/// Creates an instance of a writer which will successfully consume all data. +/// +/// All calls to `write` on the returned instance will return `Ok(buf.len())` +/// and the contents of the buffer will not be inspected. +pub fn sink() -> Sink { Sink { _priv: () } } + +impl Write for Sink { + fn write(&mut self, buf: &[u8]) -> io::Result { Ok(buf.len()) } + fn flush(&mut self) -> io::Result<()> { Ok(()) } +} + +#[cfg(test)] +mod test { + use prelude::v1::*; + + use io::prelude::*; + use io::{sink, empty, repeat}; + + #[test] + fn sink_sinks() { + let mut s = sink(); + assert_eq!(s.write(&[]), Ok(0)); + assert_eq!(s.write(&[0]), Ok(1)); + assert_eq!(s.write(&[0; 1024]), Ok(1024)); + assert_eq!(s.by_ref().write(&[0; 1024]), Ok(1024)); + } + + #[test] + fn empty_reads() { + let mut e = empty(); + assert_eq!(e.read(&mut []), Ok(0)); + assert_eq!(e.read(&mut [0]), Ok(0)); + assert_eq!(e.read(&mut [0; 1024]), Ok(0)); + assert_eq!(e.by_ref().read(&mut [0; 1024]), Ok(0)); + } + + #[test] + fn repeat_repeats() { + let mut r = repeat(4); + let mut b = [0; 1024]; + assert_eq!(r.read(&mut b), Ok(1024)); + assert!(b.iter().all(|b| *b == 4)); + } + + #[test] + fn take_some_bytes() { + assert_eq!(repeat(4).take(100).bytes().count(), 100); + assert_eq!(repeat(4).take(100).bytes().next(), Some(Ok(4))); + assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20); + } + + #[test] + fn tee() { + let mut buf = [0; 10]; + { + let mut ptr: &mut [u8] = &mut buf; + assert_eq!(repeat(4).tee(&mut ptr).take(5).read(&mut [0; 10]), Ok(5)); + } + assert_eq!(buf, [4, 4, 4, 4, 4, 0, 0, 0, 0, 0]); + } + + #[test] + fn broadcast() { + let mut buf1 = [0; 10]; + let mut buf2 = [0; 10]; + { + let mut ptr1: &mut [u8] = &mut buf1; + let mut ptr2: &mut [u8] = &mut buf2; + + assert_eq!((&mut ptr1).broadcast(&mut ptr2) + .write(&[1, 2, 3]), Ok(3)); + } + assert_eq!(buf1, buf2); + assert_eq!(buf1, [1, 2, 3, 0, 0, 0, 0, 0, 0, 0]); + } +} diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 839983d336d7..a46cea7a4432 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -128,9 +128,8 @@ #![deny(missing_docs)] -#[cfg(test)] -#[macro_use] -extern crate log; +#[cfg(test)] extern crate test; +#[cfg(test)] #[macro_use] extern crate log; #[macro_use] #[macro_reexport(assert, assert_eq, debug_assert, debug_assert_eq, @@ -248,9 +247,11 @@ pub mod dynamic_lib; pub mod ffi; pub mod fmt; pub mod old_io; +pub mod io; pub mod os; pub mod env; pub mod path; +pub mod old_path; pub mod rand; pub mod time; diff --git a/src/libstd/old_io/fs.rs b/src/libstd/old_io/fs.rs index abf215988bb4..88ca6667d55d 100644 --- a/src/libstd/old_io/fs.rs +++ b/src/libstd/old_io/fs.rs @@ -61,8 +61,8 @@ use old_io; use iter::{Iterator, Extend}; use option::Option; use option::Option::{Some, None}; -use path::{Path, GenericPath}; -use path; +use old_path::{Path, GenericPath}; +use old_path; use result::Result::{Err, Ok}; use slice::SliceExt; use string::String; @@ -782,7 +782,7 @@ pub trait PathExtensions { fn is_dir(&self) -> bool; } -impl PathExtensions for path::Path { +impl PathExtensions for old_path::Path { fn stat(&self) -> IoResult { stat(self) } fn lstat(&self) -> IoResult { lstat(self) } fn exists(&self) -> bool { diff --git a/src/libstd/old_io/net/pipe.rs b/src/libstd/old_io/net/pipe.rs index 0da7670c5b49..8c4a10a55d48 100644 --- a/src/libstd/old_io/net/pipe.rs +++ b/src/libstd/old_io/net/pipe.rs @@ -23,7 +23,7 @@ use prelude::v1::*; use ffi::CString; -use path::BytesContainer; +use old_path::BytesContainer; use old_io::{Listener, Acceptor, IoResult, TimedOut, standard_error}; use sys::pipe::UnixAcceptor as UnixAcceptorImp; use sys::pipe::UnixListener as UnixListenerImp; diff --git a/src/libstd/old_io/process.rs b/src/libstd/old_io/process.rs index 61a07bc8208e..27af957e18e1 100644 --- a/src/libstd/old_io/process.rs +++ b/src/libstd/old_io/process.rs @@ -25,7 +25,7 @@ use old_io::{IoResult, IoError}; use old_io; use libc; use os; -use path::BytesContainer; +use old_path::BytesContainer; use sync::mpsc::{channel, Receiver}; use sys::fs::FileDesc; use sys::process::Process as ProcessImp; diff --git a/src/libstd/old_io/tempfile.rs b/src/libstd/old_io/tempfile.rs index 83a42549424d..a227116dfae9 100644 --- a/src/libstd/old_io/tempfile.rs +++ b/src/libstd/old_io/tempfile.rs @@ -17,7 +17,7 @@ use old_io; use ops::Drop; use option::Option::{None, Some}; use option::Option; -use path::{Path, GenericPath}; +use old_path::{Path, GenericPath}; use rand::{Rng, thread_rng}; use result::Result::{Ok, Err}; use str::StrExt; diff --git a/src/libstd/path/mod.rs b/src/libstd/old_path/mod.rs similarity index 100% rename from src/libstd/path/mod.rs rename to src/libstd/old_path/mod.rs diff --git a/src/libstd/path/posix.rs b/src/libstd/old_path/posix.rs similarity index 99% rename from src/libstd/path/posix.rs rename to src/libstd/old_path/posix.rs index 69f815e3f8b7..8bcdd89623d8 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/old_path/posix.rs @@ -445,7 +445,7 @@ mod tests { use clone::Clone; use iter::IteratorExt; use option::Option::{self, Some, None}; - use path::GenericPath; + use old_path::GenericPath; use slice::{AsSlice, SliceExt}; use str::{self, Str, StrExt}; use string::ToString; diff --git a/src/libstd/path/windows.rs b/src/libstd/old_path/windows.rs similarity index 99% rename from src/libstd/path/windows.rs rename to src/libstd/old_path/windows.rs index fcdebaf2cd3e..2e25403220d8 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/old_path/windows.rs @@ -1124,7 +1124,7 @@ mod tests { use clone::Clone; use iter::IteratorExt; use option::Option::{self, Some, None}; - use path::GenericPath; + use old_path::GenericPath; use slice::{AsSlice, SliceExt}; use str::Str; use string::ToString; diff --git a/src/libstd/os.rs b/src/libstd/os.rs index d92f361af0bf..36122b16ea07 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -48,7 +48,7 @@ use old_io::{IoResult, IoError}; use ops::{Drop, FnOnce}; use option::Option::{Some, None}; use option::Option; -use path::{Path, GenericPath, BytesContainer}; +use old_path::{Path, GenericPath, BytesContainer}; use ptr::PtrExt; use ptr; use result::Result::{Err, Ok}; @@ -267,7 +267,7 @@ pub fn split_paths(unparsed: T) -> Vec { /// /// ```rust /// use std::os; -/// use std::path::Path; +/// use std::old_path::Path; /// /// let key = "PATH"; /// let mut paths = os::getenv_as_bytes(key).map_or(Vec::new(), os::split_paths); @@ -470,7 +470,7 @@ pub fn tmpdir() -> Path { /// # Example /// ```rust /// use std::os; -/// use std::path::Path; +/// use std::old_path::Path; /// /// // Assume we're in a path like /home/someuser /// let rel_path = Path::new(".."); @@ -500,7 +500,7 @@ pub fn make_absolute(p: &Path) -> IoResult { /// # Example /// ```rust /// use std::os; -/// use std::path::Path; +/// use std::old_path::Path; /// /// let root = Path::new("/"); /// assert!(os::change_dir(&root).is_ok()); diff --git a/src/libstd/path.rs b/src/libstd/path.rs new file mode 100755 index 000000000000..3f4f1ec4c0db --- /dev/null +++ b/src/libstd/path.rs @@ -0,0 +1,2577 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Cross-platform path manipulation. +//! +//! This module provides two types, `PathBuf` and `Path` (akin to `String` and +//! `str`), for working with paths abstractly. These types are thin wrappers +//! around `OsString` and `OsStr` respectively, meaning that they work directly +//! on strings according to the local platform's path syntax. +//! +//! ## Simple usage +//! +//! Path manipulation involves both parsing components from slices and building +//! new owned paths. +//! +//! To parse a path, you can create a `Path` slice from a `str` +//! slice and start asking questions: +//! +//! ```rust +//! use std::path::Path; +//! +//! let path = Path::new("/tmp/foo/bar.txt"); +//! let file = path.file_name(); +//! let extension = path.extension(); +//! let parent_dir = path.parent(); +//! ``` +//! +//! To build or modify paths, use `PathBuf`: +//! +//! ```rust +//! use std::path::PathBuf; +//! +//! let mut path = PathBuf::new("c:\\"); +//! path.push("windows"); +//! path.push("system32"); +//! path.set_extension("dll"); +//! ``` +//! +//! ## Path components and normalization +//! +//! The path APIs are built around the notion of "components", which roughly +//! correspond to the substrings between path separators (`/` and, on Windows, +//! `\`). The APIs for path parsing are largely specified in terms of the path's +//! components, so it's important to clearly understand how those are determined. +//! +//! A path can always be reconstructed into an equivalent path by putting +//! together its components via `push`. Syntactically, the paths may differ by +//! the normalization described below. +//! +//! ### Component types +//! +//! Components come in several types: +//! +//! * Normal components are the default: standard references to files or +//! directories. The path `a/b` has two normal components, `a` and `b`. +//! +//! * Current directory components represent the `.` character. For example, +//! `a/.` has a normal component `a` and a current directory component. +//! +//! * The root directory component represents a separator that designates +//! starting from root. For example, `/a/b` has a root directory component +//! followed by normal components `a` and `b`. +//! +//! On Windows, two additional component types come into play: +//! +//! * Prefix components, of which there is a large variety. For example, `C:` +//! and `\\server\share` are prefixes. The path `C:windows` has a prefix +//! component `C:` and a normal component `windows`; the path `C:\windows` has a +//! prefix component `C:`, a root directory component, and a normal component +//! `windows`. +//! +//! * Empty components, a special case for so-called "verbatim" paths where very +//! little normalization is allowed. For example, `\\?\C:\` has a "verbatim" +//! prefix `\\?\C:`, a root component, and an empty component (as a way of +//! representing the trailing `\`. Such a trailing `\` is in fact the only +//! situation in which an empty component is produced. +//! +//! ### Normalization +//! +//! Aside from splitting on the separator(s), there is a small amount of +//! "normalization": +//! +//! * Repeated separators are ignored: `a/b` and `a//b` both have components `a` +//! and `b`. +//! +//! * Paths ending in a separator are treated as if they has a current directory +//! component at the end (or, in verbatim paths, an empty component). For +//! example, while `a/b` has components `a` and `b`, the paths `a/b/` and +//! `a/b/.` both have components `a`, `b`, and `.` (current directory). The +//! reason for this normalization is that `a/b` and `a/b/` are treated +//! differently in some contexts, but `a/b/` and `a/b/.` are always treated +//! the same. +//! +//! No other normalization takes place by default. In particular, `a/./b/` and +//! `a/b` are treated distinctly in terms of components, as are `a/c` and +//! `a/b/../c`. Further normalization is possible to build on top of the +//! components APIs, and will be included in this library very soon. + +#![unstable(feature = "path")] + +use core::prelude::*; + +use borrow::BorrowFrom; +use cmp; +use iter; +use mem; +use ops::{self, Deref}; +use string::CowString; +use vec::Vec; +use fmt; + +use ffi::{OsStr, OsString, AsOsStr}; + +use self::platform::{is_sep, is_verbatim_sep, MAIN_SEP_STR, parse_prefix, Prefix}; + +//////////////////////////////////////////////////////////////////////////////// +// GENERAL NOTES +//////////////////////////////////////////////////////////////////////////////// +// +// Parsing in this module is done by directly transmuting OsStr to [u8] slices, +// taking advantage of the fact that OsStr always encodes ASCII characters +// as-is. Eventually, this transmutation should be replaced by direct uses of +// OsStr APIs for parsing, but it will take a while for those to become +// available. + +//////////////////////////////////////////////////////////////////////////////// +// Platform-specific definitions +//////////////////////////////////////////////////////////////////////////////// + +// The following modules give the most basic tools for parsing paths on various +// platforms. The bulk of the code is devoted to parsing prefixes on Windows. + +#[cfg(unix)] +mod platform { + use core::prelude::*; + use ffi::OsStr; + + #[inline] + pub fn is_sep(b: u8) -> bool { + b == b'/' + } + + #[inline] + pub fn is_verbatim_sep(b: u8) -> bool { + b == b'/' + } + + pub fn parse_prefix(_: &OsStr) -> Option { + None + } + + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] + pub struct Prefix<'a>; + + impl<'a> Prefix<'a> { + #[inline] + pub fn len(&self) -> usize { 0 } + #[inline] + pub fn is_verbatim(&self) -> bool { false } + #[inline] + pub fn is_drive(&self) -> bool { false } + #[inline] + pub fn has_implicit_root(&self) -> bool { false } + } + + pub const MAIN_SEP_STR: &'static str = "/"; +} + +#[cfg(windows)] +mod platform { + use core::prelude::*; + + use char::CharExt as UnicodeCharExt; + use super::{os_str_as_u8_slice, u8_slice_as_os_str}; + use ascii::*; + use ffi::OsStr; + + #[inline] + pub fn is_sep(b: u8) -> bool { + b == b'/' || b == b'\\' + } + + #[inline] + pub fn is_verbatim_sep(b: u8) -> bool { + b == b'\\' + } + + pub fn parse_prefix<'a>(path: &'a OsStr) -> Option { + use self::Prefix::*; + unsafe { + // The unsafety here stems from converting between &OsStr and &[u8] + // and back. This is safe to do because (1) we only look at ASCII + // contents of the encoding and (2) new &OsStr values are produced + // only from ASCII-bounded slices of existing &OsStr values. + let mut path = os_str_as_u8_slice(path); + + if path.starts_with(br"\\") { + // \\ + path = &path[2..]; + if path.starts_with(br"?\") { + // \\?\ + path = &path[2..]; + if path.starts_with(br"UNC\") { + // \\?\UNC\server\share + path = &path[4..]; + let (server, share) = match parse_two_comps(path, is_verbatim_sep) { + Some((server, share)) => (u8_slice_as_os_str(server), + u8_slice_as_os_str(share)), + None => (u8_slice_as_os_str(path), + u8_slice_as_os_str(&[])), + }; + return Some(VerbatimUNC(server, share)); + } else { + // \\?\path + let idx = path.position_elem(&b'\\'); + if idx == Some(2) && path[1] == b':' { + let c = path[0]; + if c.is_ascii() && (c as char).is_alphabetic() { + // \\?\C:\ path + let slice = u8_slice_as_os_str(&path[0..1]); + return Some(VerbatimDisk(slice)); + } + } + let slice = &path[.. idx.unwrap_or(path.len())]; + return Some(Verbatim(u8_slice_as_os_str(slice))); + } + } else if path.starts_with(b".\\") { + // \\.\path + path = &path[2..]; + let slice = &path[.. path.position_elem(&b'\\').unwrap_or(path.len())]; + return Some(DeviceNS(u8_slice_as_os_str(slice))); + } + match parse_two_comps(path, is_sep) { + Some((server, share)) if server.len() > 0 && share.len() > 0 => { + // \\server\share + return Some(UNC(u8_slice_as_os_str(server), + u8_slice_as_os_str(share))); + } + _ => () + } + } else if path.len() > 1 && path[1] == b':' { + // C: + let c = path[0]; + if c.is_ascii() && (c as char).is_alphabetic() { + return Some(Disk(u8_slice_as_os_str(&path[0..1]))); + } + } + return None; + } + + fn parse_two_comps(mut path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> { + let first = match path.iter().position(|x| f(*x)) { + None => return None, + Some(x) => &path[.. x] + }; + path = &path[(first.len()+1)..]; + let idx = path.iter().position(|x| f(*x)); + let second = &path[.. idx.unwrap_or(path.len())]; + Some((first, second)) + } + } + + /// Windows path prefixes. + /// + /// Windows uses a variety of path styles, including references to drive + /// volumes (like `C:`), network shared (like `\\server\share`) and + /// others. In addition, some path prefixes are "verbatim", in which case + /// `/` is *not* treated as a separator and essentially no normalization is + /// performed. + #[derive(Copy, Clone, Debug, Hash, Eq)] + pub enum Prefix<'a> { + /// Prefix `\\?\`, together with the given component immediately following it. + Verbatim(&'a OsStr), + + /// Prefix `\\?\UNC\`, with the "server" and "share" components following it. + VerbatimUNC(&'a OsStr, &'a OsStr), + + /// Prefix like `\\?\C:\`, for the given drive letter + VerbatimDisk(&'a OsStr), + + /// Prefix `\\.\`, together with the given component immediately following it. + DeviceNS(&'a OsStr), + + /// Prefix `\\server\share`, with the given "server" and "share" components. + UNC(&'a OsStr, &'a OsStr), + + /// Prefix `C:` for the given disk drive. + Disk(&'a OsStr), + } + + impl<'a> Prefix<'a> { + #[inline] + pub fn len(&self) -> usize { + use self::Prefix::*; + fn os_str_len(s: &OsStr) -> usize { + os_str_as_u8_slice(s).len() + } + match *self { + Verbatim(x) => 4 + os_str_len(x), + VerbatimUNC(x,y) => 8 + os_str_len(x) + + if os_str_len(y) > 0 { 1 + os_str_len(y) } + else { 0 }, + VerbatimDisk(_) => 6, + UNC(x,y) => 2 + os_str_len(x) + + if os_str_len(y) > 0 { 1 + os_str_len(y) } + else { 0 }, + DeviceNS(x) => 4 + os_str_len(x), + Disk(_) => 2 + } + + } + + #[inline] + pub fn is_verbatim(&self) -> bool { + use self::Prefix::*; + match *self { + Verbatim(_) | VerbatimDisk(_) | VerbatimUNC(_, _) => true, + _ => false + } + } + + #[inline] + pub fn is_drive(&self) -> bool { + match *self { + Prefix::Disk(_) => true, + _ => false, + } + } + + #[inline] + pub fn has_implicit_root(&self) -> bool { + !self.is_drive() + } + } + + impl<'a> PartialEq for Prefix<'a> { + fn eq(&self, other: &Prefix<'a>) -> bool { + use self::Prefix::*; + match (*self, *other) { + (Verbatim(x), Verbatim(y)) => x == y, + (VerbatimUNC(x1, x2), VerbatimUNC(y1, y2)) => x1 == y1 && x2 == y2, + (VerbatimDisk(x), VerbatimDisk(y)) => + os_str_as_u8_slice(x).eq_ignore_ascii_case(os_str_as_u8_slice(y)), + (DeviceNS(x), DeviceNS(y)) => x == y, + (UNC(x1, x2), UNC(y1, y2)) => x1 == y1 && x2 == y2, + (Disk(x), Disk(y)) => + os_str_as_u8_slice(x).eq_ignore_ascii_case(os_str_as_u8_slice(y)), + _ => false, + } + } + } + + pub const MAIN_SEP_STR: &'static str = "\\"; +} + +//////////////////////////////////////////////////////////////////////////////// +// Misc helpers +//////////////////////////////////////////////////////////////////////////////// + +// Iterate through `iter` while it matches `prefix`; return `None` if `prefix` +// is not a prefix of `iter`, otherwise return `Some(iter_after_prefix)` giving +// `iter` after having exhausted `prefix`. +fn iter_after(mut iter: I, mut prefix: J) -> Option where + I: Iterator + Clone, J: Iterator, A: PartialEq +{ + loop { + let mut iter_next = iter.clone(); + match (iter_next.next(), prefix.next()) { + (Some(x), Some(y)) => { + if x != y { return None } + } + (Some(_), None) => return Some(iter), + (None, None) => return Some(iter), + (None, Some(_)) => return None, + } + iter = iter_next; + } +} + +// See note at the top of this module to understand why these are used: +fn os_str_as_u8_slice(s: &OsStr) -> &[u8] { + unsafe { mem::transmute(s) } +} +unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr { + mem::transmute(s) +} + +//////////////////////////////////////////////////////////////////////////////// +// Cross-platform parsing +//////////////////////////////////////////////////////////////////////////////// + +/// Says whether the path ends in a separator character and therefore needs to +/// be treated as if it ended with an additional `.` +fn has_suffix(s: &[u8], prefix: Option) -> bool { + let (prefix_len, verbatim) = if let Some(p) = prefix { + (p.len(), p.is_verbatim()) + } else { (0, false) }; + if prefix_len > 0 && prefix_len == s.len() && !verbatim { return true; } + let mut splits = s[prefix_len..].split(|b| is_sep(*b)); + let last = splits.next_back().unwrap(); + let more = splits.next_back().is_some(); + more && last == b"" +} + +/// Says whether the first byte after the prefix is a separator. +fn has_physical_root(s: &[u8], prefix: Option) -> bool { + let path = if let Some(p) = prefix { &s[p.len()..] } else { s }; + path.len() > 0 && is_sep(path[0]) +} + +fn parse_single_component(comp: &[u8]) -> Option { + match comp { + b"." => Some(Component::CurDir), + b".." => Some(Component::ParentDir), + b"" => None, + _ => Some(Component::Normal(unsafe { u8_slice_as_os_str(comp) })) + } +} + +// basic workhorse for splitting stem and extension +#[allow(unused_unsafe)] // FIXME +fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { + unsafe { + if os_str_as_u8_slice(file) == b".." { return (Some(file), None) } + + // The unsafety here stems from converting between &OsStr and &[u8] + // and back. This is safe to do because (1) we only look at ASCII + // contents of the encoding and (2) new &OsStr values are produced + // only from ASCII-bounded slices of existing &OsStr values. + + let mut iter = os_str_as_u8_slice(file).rsplitn(1, |b| *b == b'.'); + let after = iter.next(); + let before = iter.next(); + if before == Some(b"") { + (Some(file), None) + } else { + (before.map(|s| u8_slice_as_os_str(s)), + after.map(|s| u8_slice_as_os_str(s))) + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// The core iterators +//////////////////////////////////////////////////////////////////////////////// + +/// Component parsing works by a double-ended state machine; the cursors at the +/// front and back of the path each keep track of what parts of the path have +/// been consumed so far. +/// +/// Going front to back, a path is made up of a prefix, a root component, a body +/// (of normal components), and a suffix/emptycomponent (normalized `.` or `` +/// for a path ending with the separator) +#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] +enum State { + Prefix = 0, // c: + Root = 1, // / + Body = 2, // foo/bar/baz + Suffix = 3, // . + Done = 4, +} + +/// A single component of a path. +/// +/// See the module documentation for an in-depth explanation of components and +/// their role in the API. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum Component<'a> { + /// A Windows path prefix, e.g. `C:` or `\server\share` + Prefix(&'a OsStr), + + /// An empty component. Only used on Windows for the last component of + /// verbatim paths ending with a separator (e.g. the last component of + /// `\\?\C:\windows\` but not `\\?\C:\windows` or `C:\windows`). + Empty, + + /// The root directory component, appears after any prefix and before anything else + RootDir, + + /// A reference to the current directory, i.e. `.` + CurDir, + + /// A reference to the parent directory, i.e. `..` + ParentDir, + + /// A normal component, i.e. `a` and `b` in `a/b` + Normal(&'a OsStr), +} + +impl<'a> Component<'a> { + /// Extract the underlying `OsStr` slice + pub fn as_os_str(self) -> &'a OsStr { + match self { + Component::Prefix(path) => path, + Component::Empty => OsStr::from_str(""), + Component::RootDir => OsStr::from_str(MAIN_SEP_STR), + Component::CurDir => OsStr::from_str("."), + Component::ParentDir => OsStr::from_str(".."), + Component::Normal(path) => path, + } + } +} + +/// The core iterator giving the components of a path. +/// +/// See the module documentation for an in-depth explanation of components and +/// their role in the API. +#[derive(Clone)] +pub struct Components<'a> { + // The path left to parse components from + path: &'a [u8], + + // The prefix as it was originally parsed, if any + prefix: Option>, + + // true if path *physically* has a root separator; for most Windows + // prefixes, it may have a "logical" rootseparator for the purposes of + // normalization, e.g. \\server\share == \\server\share\. + has_physical_root: bool, + + // The iterator is double-ended, and these two states keep track of what has + // been produced from either end + front: State, + back: State, +} + +/// An iterator over the components of a path, as `OsStr` slices. +#[derive(Clone)] +pub struct Iter<'a> { + inner: Components<'a> +} + +impl<'a> Components<'a> { + // how long is the prefix, if any? + #[inline] + fn prefix_len(&self) -> usize { + self.prefix.as_ref().map(Prefix::len).unwrap_or(0) + } + + #[inline] + fn prefix_verbatim(&self) -> bool { + self.prefix.as_ref().map(Prefix::is_verbatim).unwrap_or(false) + } + + /// how much of the prefix is left from the point of view of iteration? + #[inline] + fn prefix_remaining(&self) -> usize { + if self.front == State::Prefix { self.prefix_len() } + else { 0 } + } + + fn prefix_and_root(&self) -> usize { + let root = if self.front <= State::Root && self.has_physical_root { 1 } else { 0 }; + self.prefix_remaining() + root + } + + // is the iteration complete? + #[inline] + fn finished(&self) -> bool { + self.front == State::Done || self.back == State::Done || self.front > self.back + } + + #[inline] + fn is_sep(&self, b: u8) -> bool { + if self.prefix_verbatim() { + is_verbatim_sep(b) + } else { + is_sep(b) + } + } + + /// Extract a slice corresponding to the portion of the path remaining for iteration. + pub fn as_path(&self) -> &'a Path { + let mut comps = self.clone(); + if comps.front == State::Body { comps.trim_left(); } + if comps.back == State::Body { comps.trim_right(); } + if comps.path.is_empty() && comps.front < comps.back && comps.back == State::Suffix { + Path::new(".") + } else { + unsafe { Path::from_u8_slice(comps.path) } + } + } + + /// Is the *original* path rooted? + fn has_root(&self) -> bool { + if self.has_physical_root { return true } + if let Some(p) = self.prefix { + if p.has_implicit_root() { return true } + } + false + } + + // parse a component from the left, saying how many bytes to consume to + // remove the component + fn parse_next_component(&self) -> (usize, Option>) { + debug_assert!(self.front == State::Body); + let (extra, comp) = match self.path.iter().position(|b| self.is_sep(*b)) { + None => (0, self.path), + Some(i) => (1, &self.path[.. i]), + }; + (comp.len() + extra, parse_single_component(comp)) + } + + // parse a component from the right, saying how many bytes to consume to + // remove the component + fn parse_next_component_back(&self) -> (usize, Option>) { + debug_assert!(self.back == State::Body); + let start = self.prefix_and_root(); + let (extra, comp) = match self.path[start..].iter().rposition(|b| self.is_sep(*b)) { + None => (0, &self.path[start ..]), + Some(i) => (1, &self.path[start + i + 1 ..]), + }; + (comp.len() + extra, parse_single_component(comp)) + } + + // trim away repeated separators (i.e. emtpy components) on the left + fn trim_left(&mut self) { + while !self.path.is_empty() { + let (size, comp) = self.parse_next_component(); + if comp.is_some() { + return; + } else { + self.path = &self.path[size ..]; + } + } + } + + // trim away repeated separators (i.e. emtpy components) on the right + fn trim_right(&mut self) { + while self.path.len() > self.prefix_and_root() { + let (size, comp) = self.parse_next_component_back(); + if comp.is_some() { + return; + } else { + self.path = &self.path[.. self.path.len() - size]; + } + } + } + + /// Examine the next component without consuming it. + pub fn peek(&self) -> Option> { + self.clone().next() + } +} + +impl<'a> Iter<'a> { + /// Extract a slice corresponding to the portion of the path remaining for iteration. + pub fn as_path(&self) -> &'a Path { + self.inner.as_path() + } +} + +impl<'a> Iterator for Iter<'a> { + type Item = &'a OsStr; + + fn next(&mut self) -> Option<&'a OsStr> { + self.inner.next().map(Component::as_os_str) + } +} + +impl<'a> DoubleEndedIterator for Iter<'a> { + fn next_back(&mut self) -> Option<&'a OsStr> { + self.inner.next_back().map(Component::as_os_str) + } +} + +impl<'a> Iterator for Components<'a> { + type Item = Component<'a>; + + fn next(&mut self) -> Option> { + while !self.finished() { + match self.front { + State::Prefix if self.prefix_len() > 0 => { + self.front = State::Root; + debug_assert!(self.prefix_len() <= self.path.len()); + let prefix = &self.path[.. self.prefix_len()]; + self.path = &self.path[self.prefix_len() .. ]; + return Some(Component::Prefix(unsafe { u8_slice_as_os_str(prefix) })) + } + State::Prefix => { + self.front = State::Root; + } + State::Root => { + self.front = State::Body; + if self.has_physical_root { + debug_assert!(self.path.len() > 0); + self.path = &self.path[1..]; + return Some(Component::RootDir) + } else if let Some(p) = self.prefix { + if p.has_implicit_root() && !p.is_verbatim() { + return Some(Component::RootDir) + } + } + } + State::Body if !self.path.is_empty() => { + let (size, comp) = self.parse_next_component(); + self.path = &self.path[size ..]; + if comp.is_some() { return comp } + } + State::Body => { + self.front = State::Suffix; + } + State::Suffix => { + self.front = State::Done; + if self.prefix_verbatim() { + return Some(Component::Empty) + } else { + return Some(Component::CurDir) + } + } + State::Done => unreachable!() + } + } + None + } +} + +impl<'a> DoubleEndedIterator for Components<'a> { + fn next_back(&mut self) -> Option> { + while !self.finished() { + match self.back { + State::Suffix => { + self.back = State::Body; + if self.prefix_verbatim() { + return Some(Component::Empty) + } else { + return Some(Component::CurDir) + } + } + State::Body if self.path.len() > self.prefix_and_root() => { + let (size, comp) = self.parse_next_component_back(); + self.path = &self.path[.. self.path.len() - size]; + if comp.is_some() { return comp } + } + State::Body => { + self.back = State::Root; + } + State::Root => { + self.back = State::Prefix; + if self.has_physical_root { + self.path = &self.path[.. self.path.len() - 1]; + return Some(Component::RootDir) + } else if let Some(p) = self.prefix { + if p.has_implicit_root() && !p.is_verbatim() { + return Some(Component::RootDir) + } + } + } + State::Prefix if self.prefix_len() > 0 => { + self.back = State::Done; + return Some(Component::Prefix(unsafe { + u8_slice_as_os_str(self.path) + })) + } + State::Prefix => { + self.back = State::Done; + return None + } + State::Done => unreachable!() + } + } + None + } +} + +fn optional_path(path: &Path) -> Option<&Path> { + if path.as_u8_slice().is_empty() { None } else { Some(path) } +} + +impl<'a> cmp::PartialEq for Components<'a> { + fn eq(&self, other: &Components<'a>) -> bool { + iter::order::eq(self.clone(), other.clone()) + } +} + +impl<'a> cmp::Eq for Components<'a> {} + +impl<'a> cmp::PartialOrd for Components<'a> { + fn partial_cmp(&self, other: &Components<'a>) -> Option { + iter::order::partial_cmp(self.clone(), other.clone()) + } +} + +impl<'a> cmp::Ord for Components<'a> { + fn cmp(&self, other: &Components<'a>) -> cmp::Ordering { + iter::order::cmp(self.clone(), other.clone()) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Basic types and traits +//////////////////////////////////////////////////////////////////////////////// + +/// An owned, mutable path (akin to `String`). +/// +/// This type provides methods like `push` and `set_extension` that mutate the +/// path in place. It also implements `Deref` to `Path`, meaning that all +/// methods on `Path` slices are available on `PathBuf` values as well. +/// +/// More details about the overall approach can be found in +/// the module documentation. +/// +/// # Example +/// +/// ```rust +/// use std::path::PathBuf; +/// +/// let mut path = PathBuf::new("c:\\"); +/// path.push("windows"); +/// path.push("system32"); +/// path.set_extension("dll"); +/// ``` +#[derive(Clone, Hash)] +pub struct PathBuf { + inner: OsString +} + +impl PathBuf { + fn as_mut_vec(&mut self) -> &mut Vec { + unsafe { mem::transmute(self) } + } + + /// Allocate a `PathBuf` with initial contents given by the + /// argument. + pub fn new(s: &S) -> PathBuf { + PathBuf { inner: s.as_os_str().to_os_string() } + } + + /// Extend `self` with `path`. + /// + /// If `path` is absolute, it replaces the current path. + /// + /// On Windows: + /// + /// * if `path` has a root but no prefix (e.g. `\windows`), it + /// replaces everything except for the prefix (if any) of `self`. + /// * if `path` has a prefix but no root, it replaces `self. + pub fn push(&mut self, path: &P) where P: AsPath { + // in general, a separator is needed if the rightmost byte is not a separator + let mut need_sep = self.as_mut_vec().last().map(|c| !is_sep(*c)).unwrap_or(false); + + // in the special case of `C:` on Windows, do *not* add a separator + { + let comps = self.components(); + if comps.prefix_len() > 0 && + comps.prefix_len() == comps.path.len() && + comps.prefix.unwrap().is_drive() + { + need_sep = false + } + } + + let path = path.as_path(); + + // absolute `path` replaces `self` + if path.is_absolute() || path.prefix().is_some() { + self.as_mut_vec().truncate(0); + + // `path` has a root but no prefix, e.g. `\windows` (Windows only) + } else if path.has_root() { + let prefix_len = self.components().prefix_remaining(); + self.as_mut_vec().truncate(prefix_len); + + // `path` is a pure relative path + } else if need_sep { + self.inner.push_os_str(OsStr::from_str(MAIN_SEP_STR)); + } + + self.inner.push_os_str(path.as_os_str()); + } + + /// Truncate `self` to `self.parent()`. + /// + /// Returns `None` and does nothing if `self.parent()` is `None`. + pub fn pop(&mut self) -> bool { + match self.parent().map(|p| p.as_u8_slice().len()) { + Some(len) => { + self.as_mut_vec().truncate(len); + true + } + None => false + } + } + + /// Updates `self.file_name()` to `file_name`. + /// + /// If `self.file_name()` was `None`, this is equivalent to pushing + /// `file_name`. + /// + /// # Examples + /// + /// ```rust + /// use std::path::{Path, PathBuf}; + /// + /// let mut buf = PathBuf::new("/foo/"); + /// assert!(buf.file_name() == None); + /// buf.set_file_name("bar"); + /// assert!(buf == PathBuf::new("/foo/bar")); + /// assert!(buf.file_name().is_some()); + /// buf.set_file_name("baz.txt"); + /// assert!(buf == PathBuf::new("/foo/baz.txt")); + /// ``` + pub fn set_file_name(&mut self, file_name: &S) where S: AsOsStr { + if self.file_name().is_some() && !self.pop() { + // Given that there is a file name, this is reachable only for + // Windows paths like c:file or paths like `foo`, but not `c:\` or + // `/`. + let prefix_len = self.components().prefix_remaining(); + self.as_mut_vec().truncate(prefix_len); + } + self.push(file_name.as_os_str()); + } + + /// Updates `self.extension()` to `extension`. + /// + /// If `self.file_name()` is `None`, does nothing and returns `false`. + /// + /// Otherwise, returns `tru`; if `self.exension()` is `None`, the extension + /// is added; otherwise it is replaced. + pub fn set_extension(&mut self, extension: &S) -> bool { + if self.file_name().is_none() { return false; } + + let mut stem = match self.file_stem() { + Some(stem) => stem.to_os_string(), + None => OsString::from_str(""), + }; + + let extension = extension.as_os_str(); + if os_str_as_u8_slice(extension).len() > 0 { + stem.push_os_str(OsStr::from_str(".")); + stem.push_os_str(extension.as_os_str()); + } + self.set_file_name(&stem); + + true + } +} + +impl<'a, P: ?Sized + 'a> iter::FromIterator<&'a P> for PathBuf where P: AsPath { + fn from_iter>(iter: I) -> PathBuf { + let mut buf = PathBuf::new(""); + buf.extend(iter); + buf + } +} + +impl<'a, P: ?Sized + 'a> iter::Extend<&'a P> for PathBuf where P: AsPath { + fn extend>(&mut self, iter: I) { + for p in iter { + self.push(p) + } + } +} + +impl fmt::Debug for PathBuf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fmt::Debug::fmt(&**self, formatter) + } +} + +impl ops::Deref for PathBuf { + type Target = Path; + + fn deref(&self) -> &Path { + unsafe { mem::transmute(&self.inner[]) } + } +} + +impl BorrowFrom for Path { + fn borrow_from(owned: &PathBuf) -> &Path { + owned.deref() + } +} + +impl cmp::PartialEq for PathBuf { + fn eq(&self, other: &PathBuf) -> bool { + self.components() == other.components() + } +} + +impl cmp::Eq for PathBuf {} + +impl cmp::PartialOrd for PathBuf { + fn partial_cmp(&self, other: &PathBuf) -> Option { + self.components().partial_cmp(&other.components()) + } +} + +impl cmp::Ord for PathBuf { + fn cmp(&self, other: &PathBuf) -> cmp::Ordering { + self.components().cmp(&other.components()) + } +} + +/// A slice of a path (akin to `str`). +/// +/// This type supports a number of operations for inspecting a path, including +/// breaking the path into its components (separated by `/` or `\`, depending on +/// the platform), extracting the file name, determining whether the path is +/// absolute, and so on. More details about the overall approach can be found in +/// the module documentation. +/// +/// This is an *unsized* type, meaning that it must always be used with behind a +/// pointer like `&` or `Box`. +/// +/// # Example +/// +/// ```rust +/// use std::path::Path; +/// +/// let path = Path::new("/tmp/foo/bar.txt"); +/// let file = path.file_name(); +/// let extension = path.extension(); +/// let parent_dir = path.parent(); +/// ``` +/// +pub struct Path { + inner: OsStr +} + +impl Path { + // The following (private!) function allows construction of a path from a u8 + // slice, which is only safe when it is known to follow the OsStr encoding. + unsafe fn from_u8_slice(s: &[u8]) -> &Path { + mem::transmute(s) + } + // The following (private!) function reveals the byte encoding used for OsStr. + fn as_u8_slice(&self) -> &[u8] { + unsafe { mem::transmute(self) } + } + + /// Directly wrap a string slice as a `Path` slice. + /// + /// This is a cost-free conversion. + pub fn new(s: &S) -> &Path { + unsafe { mem::transmute(s.as_os_str()) } + } + + /// Yield a `&str` slice if the `Path` is valid unicode. + /// + /// This conversion may entail doing a check for UTF-8 validity. + pub fn to_str(&self) -> Option<&str> { + self.inner.to_str() + } + + /// Convert a `Path` to a `CowString`. + /// + /// Any non-Unicode sequences are replaced with U+FFFD REPLACEMENT CHARACTER. + pub fn to_string_lossy(&self) -> CowString { + self.inner.to_string_lossy() + } + + /// Convert a `Path` to an owned `PathBuf`. + pub fn to_path_buf(&self) -> PathBuf { + PathBuf::new(self) + } + + /// A path is *absolute* if it is indepedent of the current directory. + /// + /// * On Unix, a path is absolute if it starts with the root, so + /// `is_absolute` and `has_root` are equivalent. + /// + /// * On Windows, a path is absolute if it has a prefix and starts with the + /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. In + /// other words, `path.is_absolute() == path.prefix().is_some() && path.has_root()`. + pub fn is_absolute(&self) -> bool { + self.has_root() && + (cfg!(unix) || self.prefix().is_some()) + } + + /// A path is *relative* if it is not absolute. + pub fn is_relative(&self) -> bool { + !self.is_absolute() + } + + /// Returns the *prefix* of a path, if any. + /// + /// Prefixes are relevant only for Windows paths, and consist of volumes + /// like `C:`, UNC prefixes like `\\server`, and others described in more + /// detail in `std::os::windows::PathExt`. + pub fn prefix(&self) -> Option<&Path> { + let iter = self.components(); + optional_path(unsafe { + Path::from_u8_slice( + &self.as_u8_slice()[.. iter.prefix_remaining()]) + }) + } + + /// A path has a root if the body of the path begins with the directory separator. + /// + /// * On Unix, a path has a root if it begins with `/`. + /// + /// * On Windows, a path has a root if it: + /// * has no prefix and begins with a separator, e.g. `\\windows` + /// * has a prefix followed by a separator, e.g. `c:\windows` but not `c:windows` + /// * has any non-disk prefix, e.g. `\\server\share` + pub fn has_root(&self) -> bool { + self.components().has_root() + } + + /// The path without its final component. + /// + /// Does nothing, returning `None` if the path consists of just a prefix + /// and/or root directory reference. + /// + /// # Examples + /// + /// ```rust + /// use std::path::Path; + /// + /// let path = Path::new("/foo/bar"); + /// let foo = path.parent().unwrap(); + /// assert!(foo == Path::new("/foo")); + /// let root = foo.parent().unwrap(); + /// assert!(root == Path::new("/")); + /// assert!(root.parent() == None); + /// ``` + pub fn parent(&self) -> Option<&Path> { + let mut comps = self.components(); + let comp = comps.next_back(); + let rest = optional_path(comps.as_path()); + + match (comp, comps.next_back()) { + (Some(Component::CurDir), Some(Component::RootDir)) => None, + (Some(Component::CurDir), Some(Component::Prefix(_))) => None, + (Some(Component::Empty), Some(Component::RootDir)) => None, + (Some(Component::Empty), Some(Component::Prefix(_))) => None, + (Some(Component::Prefix(_)), None) => None, + (Some(Component::RootDir), Some(Component::Prefix(_))) => None, + _ => rest + } + } + + /// The final component of the path, if it is a normal file. + /// + /// If the path terminates in `.`, `..`, or consists solely or a root of + /// prefix, `file` will return `None`. + pub fn file_name(&self) -> Option<&OsStr> { + self.components().next_back().and_then(|p| match p { + Component::Normal(p) => Some(p.as_os_str()), + _ => None + }) + } + + /// Returns a path that, when joined onto `base`, yields `self`. + pub fn relative_from<'a, P: ?Sized>(&'a self, base: &'a P) -> Option<&Path> where + P: AsPath + { + iter_after(self.components(), base.as_path().components()).map(|c| c.as_path()) + } + + /// Determines whether `base` is a prefix of `self`. + pub fn starts_with(&self, base: &P) -> bool where P: AsPath { + iter_after(self.components(), base.as_path().components()).is_some() + } + + /// Determines whether `base` is a suffix of `self`. + pub fn ends_with(&self, child: &P) -> bool where P: AsPath { + iter_after(self.components().rev(), child.as_path().components().rev()).is_some() + } + + /// Extract the stem (non-extension) portion of `self.file()`. + /// + /// The stem is: + /// + /// * None, if there is no file name; + /// * The entire file name if there is no embedded `.`; + /// * The entire file name if the file name begins with `.` and has no other `.`s within; + /// * Otherwise, the portion of the file name before the final `.` + pub fn file_stem(&self) -> Option<&OsStr> { + self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.or(after)) + } + + /// Extract the extension of `self.file()`, if possible. + /// + /// The extension is: + /// + /// * None, if there is no file name; + /// * None, if there is no embedded `.`; + /// * None, if the file name begins with `.` and has no other `.`s within; + /// * Otherwise, the portion of the file name after the final `.` + pub fn extension(&self) -> Option<&OsStr> { + self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.and(after)) + } + + /// Creates an owned `PathBuf` with `path` adjoined to `self`. + /// + /// See `PathBuf::push` for more details on what it means to adjoin a path. + pub fn join(&self, path: &P) -> PathBuf where P: AsPath { + let mut buf = self.to_path_buf(); + buf.push(path); + buf + } + + /// Creates an owned `PathBuf` like `self` but with the given file name. + /// + /// See `PathBuf::set_file_name` for more details. + pub fn with_file_name(&self, file_name: &S) -> PathBuf where S: AsOsStr { + let mut buf = self.to_path_buf(); + buf.set_file_name(file_name); + buf + } + + /// Creates an owned `PathBuf` like `self` but with the given extension. + /// + /// See `PathBuf::set_extension` for more details. + pub fn with_extension(&self, extension: &S) -> PathBuf where S: AsOsStr { + let mut buf = self.to_path_buf(); + buf.set_extension(extension); + buf + } + + /// Produce an iterator over the components of the path. + pub fn components(&self) -> Components { + let prefix = parse_prefix(self.as_os_str()); + Components { + path: self.as_u8_slice(), + prefix: prefix, + has_physical_root: has_physical_root(self.as_u8_slice(), prefix), + front: State::Prefix, + back: if has_suffix(self.as_u8_slice(), prefix) { State::Suffix } + else { State::Body }, + } + } + + /// Produce an iterator over the path's components viewed as `OsStr` slices. + pub fn iter(&self) -> Iter { + Iter { inner: self.components() } + } + + /// Returns an object that implements `Display` for safely printing paths + /// that may contain non-Unicode data. + pub fn display(&self) -> Display { + Display { path: self } + } +} + +impl AsOsStr for Path { + fn as_os_str(&self) -> &OsStr { + &self.inner + } +} + +impl fmt::Debug for Path { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.inner.fmt(formatter) + } +} + +/// Helper struct for safely printing paths with `format!()` and `{}` +pub struct Display<'a> { + path: &'a Path +} + +impl<'a> fmt::Debug for Display<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.path.to_string_lossy(), f) + } +} + +impl<'a> fmt::Display for Display<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.path.to_string_lossy(), f) + } +} + +impl cmp::PartialEq for Path { + fn eq(&self, other: &Path) -> bool { + iter::order::eq(self.components(), other.components()) + } +} + +impl cmp::Eq for Path {} + +impl cmp::PartialOrd for Path { + fn partial_cmp(&self, other: &Path) -> Option { + self.components().partial_cmp(&other.components()) + } +} + +impl cmp::Ord for Path { + fn cmp(&self, other: &Path) -> cmp::Ordering { + self.components().cmp(&other.components()) + } +} + +/// Freely convertible to a `Path`. +pub trait AsPath { + /// Convert to a `Path`. + fn as_path(&self) -> &Path; +} + +impl AsPath for T { + fn as_path(&self) -> &Path { Path::new(self.as_os_str()) } +} + +#[cfg(test)] +mod tests { + use super::*; + use ffi::OsStr; + use core::prelude::*; + use string::{ToString, String}; + use vec::Vec; + + macro_rules! t( + ($path:expr, iter: $iter:expr) => ( + { + let path = Path::new($path); + + // Forward iteration + let comps = path.iter() + .map(|p| p.to_string_lossy().into_owned()) + .collect::>(); + let exp: &[&str] = &$iter; + let exps = exp.iter().map(|s| s.to_string()).collect::>(); + assert!(comps == exps, "iter: Expected {:?}, found {:?}", + exps, comps); + + // Reverse iteration + let comps = Path::new($path).iter().rev() + .map(|p| p.to_string_lossy().into_owned()) + .collect::>(); + let exps = exps.into_iter().rev().collect::>(); + assert!(comps == exps, "iter().rev(): Expected {:?}, found {:?}", + exps, comps); + } + ); + + ($path:expr, has_root: $has_root:expr, is_absolute: $is_absolute:expr) => ( + { + let path = Path::new($path); + + let act_root = path.has_root(); + assert!(act_root == $has_root, "has_root: Expected {:?}, found {:?}", + $has_root, act_root); + + let act_abs = path.is_absolute(); + assert!(act_abs == $is_absolute, "is_absolute: Expected {:?}, found {:?}", + $is_absolute, act_abs); + } + ); + + ($path:expr, parent: $parent:expr, file_name: $file:expr) => ( + { + let path = Path::new($path); + + let parent = path.parent().map(|p| p.to_str().unwrap()); + let exp_parent: Option<&str> = $parent; + assert!(parent == exp_parent, "parent: Expected {:?}, found {:?}", + exp_parent, parent); + + let file = path.file_name().map(|p| p.to_str().unwrap()); + let exp_file: Option<&str> = $file; + assert!(file == exp_file, "file_name: Expected {:?}, found {:?}", + exp_file, file); + } + ); + + ($path:expr, file_stem: $file_stem:expr, extension: $extension:expr) => ( + { + let path = Path::new($path); + + let stem = path.file_stem().map(|p| p.to_str().unwrap()); + let exp_stem: Option<&str> = $file_stem; + assert!(stem == exp_stem, "file_stem: Expected {:?}, found {:?}", + exp_stem, stem); + + let ext = path.extension().map(|p| p.to_str().unwrap()); + let exp_ext: Option<&str> = $extension; + assert!(ext == exp_ext, "extension: Expected {:?}, found {:?}", + exp_ext, ext); + } + ); + + ($path:expr, iter: $iter:expr, + has_root: $has_root:expr, is_absolute: $is_absolute:expr, + parent: $parent:expr, file_name: $file:expr, + file_stem: $file_stem:expr, extension: $extension:expr) => ( + { + t!($path, iter: $iter); + t!($path, has_root: $has_root, is_absolute: $is_absolute); + t!($path, parent: $parent, file_name: $file); + t!($path, file_stem: $file_stem, extension: $extension); + } + ); + ); + + #[test] + #[cfg(unix)] + pub fn test_decompositions_unix() { + t!("", + iter: [], + has_root: false, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: None, + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("/", + iter: ["/", "."], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("/foo", + iter: ["/", "foo"], + has_root: true, + is_absolute: true, + parent: Some("/"), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("foo/", + iter: ["foo", "."], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("/foo/", + iter: ["/", "foo", "."], + has_root: true, + is_absolute: true, + parent: Some("/foo"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/bar", + iter: ["foo", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("/foo/bar", + iter: ["/", "foo", "bar"], + has_root: true, + is_absolute: true, + parent: Some("/foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("///foo///", + iter: ["/", "foo", "."], + has_root: true, + is_absolute: true, + parent: Some("///foo"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("///foo///bar", + iter: ["/", "foo", "bar"], + has_root: true, + is_absolute: true, + parent: Some("///foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("./.", + iter: [".", "."], + has_root: false, + is_absolute: false, + parent: Some("."), + file_name: None, + file_stem: None, + extension: None + ); + + t!("./.", + iter: [".", "."], + has_root: false, + is_absolute: false, + parent: Some("."), + file_name: None, + file_stem: None, + extension: None + ); + + t!("/..", + iter: ["/", ".."], + has_root: true, + is_absolute: true, + parent: Some("/"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("../", + iter: ["..", "."], + has_root: false, + is_absolute: false, + parent: Some(".."), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/.", + iter: ["foo", "."], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/..", + iter: ["foo", ".."], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/./", + iter: ["foo", ".", "."], + has_root: false, + is_absolute: false, + parent: Some("foo/."), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/./bar", + iter: ["foo", ".", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo/."), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("foo/../", + iter: ["foo", "..", "."], + has_root: false, + is_absolute: false, + parent: Some("foo/.."), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/../bar", + iter: ["foo", "..", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo/.."), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("./a", + iter: [".", "a"], + has_root: false, + is_absolute: false, + parent: Some("."), + file_name: Some("a"), + file_stem: Some("a"), + extension: None + ); + + t!(".", + iter: ["."], + has_root: false, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("./", + iter: [".", "."], + has_root: false, + is_absolute: false, + parent: Some("."), + file_name: None, + file_stem: None, + extension: None + ); + + t!("a/b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None + ); + + t!("a//b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None + ); + + t!("a/./b", + iter: ["a", ".", "b"], + has_root: false, + is_absolute: false, + parent: Some("a/."), + file_name: Some("b"), + file_stem: Some("b"), + extension: None + ); + + t!("a/b/c", + iter: ["a", "b", "c"], + has_root: false, + is_absolute: false, + parent: Some("a/b"), + file_name: Some("c"), + file_stem: Some("c"), + extension: None + ); + } + + #[test] + #[cfg(windows)] + pub fn test_decompositions_windows() { + t!("", + iter: [], + has_root: false, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: None, + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("/", + iter: ["\\", "."], + has_root: true, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\", + iter: ["\\", "."], + has_root: true, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("c:", + iter: ["c:", "."], + has_root: false, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("c:\\", + iter: ["c:", "\\", "."], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("c:\\", + iter: ["c:", "\\", "."], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("c:/", + iter: ["c:", "\\", "."], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("/foo", + iter: ["\\", "foo"], + has_root: true, + is_absolute: false, + parent: Some("/"), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("foo/", + iter: ["foo", "."], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("/foo/", + iter: ["\\", "foo", "."], + has_root: true, + is_absolute: false, + parent: Some("/foo"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/bar", + iter: ["foo", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("/foo/bar", + iter: ["\\", "foo", "bar"], + has_root: true, + is_absolute: false, + parent: Some("/foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("///foo///", + iter: ["\\", "foo", "."], + has_root: true, + is_absolute: false, + parent: Some("///foo"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("///foo///bar", + iter: ["\\", "foo", "bar"], + has_root: true, + is_absolute: false, + parent: Some("///foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("./.", + iter: [".", "."], + has_root: false, + is_absolute: false, + parent: Some("."), + file_name: None, + file_stem: None, + extension: None + ); + + t!("./.", + iter: [".", "."], + has_root: false, + is_absolute: false, + parent: Some("."), + file_name: None, + file_stem: None, + extension: None + ); + + t!("/..", + iter: ["\\", ".."], + has_root: true, + is_absolute: false, + parent: Some("/"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("../", + iter: ["..", "."], + has_root: false, + is_absolute: false, + parent: Some(".."), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/.", + iter: ["foo", "."], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/..", + iter: ["foo", ".."], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/./", + iter: ["foo", ".", "."], + has_root: false, + is_absolute: false, + parent: Some("foo/."), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/./bar", + iter: ["foo", ".", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo/."), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("foo/../", + iter: ["foo", "..", "."], + has_root: false, + is_absolute: false, + parent: Some("foo/.."), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/../bar", + iter: ["foo", "..", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo/.."), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("./a", + iter: [".", "a"], + has_root: false, + is_absolute: false, + parent: Some("."), + file_name: Some("a"), + file_stem: Some("a"), + extension: None + ); + + t!(".", + iter: ["."], + has_root: false, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("./", + iter: [".", "."], + has_root: false, + is_absolute: false, + parent: Some("."), + file_name: None, + file_stem: None, + extension: None + ); + + t!("a/b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None + ); + + t!("a//b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None + ); + + t!("a/./b", + iter: ["a", ".", "b"], + has_root: false, + is_absolute: false, + parent: Some("a/."), + file_name: Some("b"), + file_stem: Some("b"), + extension: None + ); + + t!("a/b/c", + iter: ["a", "b", "c"], + has_root: false, + is_absolute: false, + parent: Some("a/b"), + file_name: Some("c"), + file_stem: Some("c"), + extension: None); + + t!("a\\b\\c", + iter: ["a", "b", "c"], + has_root: false, + is_absolute: false, + parent: Some("a\\b"), + file_name: Some("c"), + file_stem: Some("c"), + extension: None + ); + + t!("\\a", + iter: ["\\", "a"], + has_root: true, + is_absolute: false, + parent: Some("\\"), + file_name: Some("a"), + file_stem: Some("a"), + extension: None + ); + + t!("c:\\foo.txt", + iter: ["c:", "\\", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("c:\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt") + ); + + t!("\\\\server\\share\\foo.txt", + iter: ["\\\\server\\share", "\\", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\server\\share\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt") + ); + + t!("\\\\server\\share", + iter: ["\\\\server\\share", "\\", "."], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\server", + iter: ["\\", "server"], + has_root: true, + is_absolute: false, + parent: Some("\\"), + file_name: Some("server"), + file_stem: Some("server"), + extension: None + ); + + t!("\\\\?\\bar\\foo.txt", + iter: ["\\\\?\\bar", "\\", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\bar\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt") + ); + + t!("\\\\?\\bar", + iter: ["\\\\?\\bar"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\?\\", + iter: ["\\\\?\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\?\\UNC\\server\\share\\foo.txt", + iter: ["\\\\?\\UNC\\server\\share", "\\", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\UNC\\server\\share\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt") + ); + + t!("\\\\?\\UNC\\server", + iter: ["\\\\?\\UNC\\server"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\?\\UNC\\", + iter: ["\\\\?\\UNC\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\?\\C:\\foo.txt", + iter: ["\\\\?\\C:", "\\", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt") + ); + + + t!("\\\\?\\C:\\", + iter: ["\\\\?\\C:", "\\", ""], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + + t!("\\\\?\\C:", + iter: ["\\\\?\\C:"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + + t!("\\\\?\\foo/bar", + iter: ["\\\\?\\foo/bar"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + + t!("\\\\?\\C:/foo", + iter: ["\\\\?\\C:/foo"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + + t!("\\\\.\\foo\\bar", + iter: ["\\\\.\\foo", "\\", "bar"], + has_root: true, + is_absolute: true, + parent: Some("\\\\.\\foo\\"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + + t!("\\\\.\\foo", + iter: ["\\\\.\\foo", "\\", "."], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + + t!("\\\\.\\foo/bar", + iter: ["\\\\.\\foo/bar", "\\", "."], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + + t!("\\\\.\\foo\\bar/baz", + iter: ["\\\\.\\foo", "\\", "bar", "baz"], + has_root: true, + is_absolute: true, + parent: Some("\\\\.\\foo\\bar"), + file_name: Some("baz"), + file_stem: Some("baz"), + extension: None + ); + + + t!("\\\\.\\", + iter: ["\\\\.\\", "\\", "."], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\?\\a\\b\\", + iter: ["\\\\?\\a", "\\", "b", ""], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\a\\b"), + file_name: None, + file_stem: None, + extension: None + ); + } + + #[test] + pub fn test_stem_ext() { + t!("foo", + file_stem: Some("foo"), + extension: None + ); + + t!("foo.", + file_stem: Some("foo"), + extension: Some("") + ); + + t!(".foo", + file_stem: Some(".foo"), + extension: None + ); + + t!("foo.txt", + file_stem: Some("foo"), + extension: Some("txt") + ); + + t!("foo.bar.txt", + file_stem: Some("foo.bar"), + extension: Some("txt") + ); + + t!("foo.bar.", + file_stem: Some("foo.bar"), + extension: Some("") + ); + + t!(".", + file_stem: None, + extension: None + ); + + t!("..", + file_stem: None, + extension: None + ); + + t!("", + file_stem: None, + extension: None + ); + } + + #[test] + pub fn test_push() { + macro_rules! tp( + ($path:expr, $push:expr, $expected:expr) => ( { + let mut actual = PathBuf::new($path); + actual.push($push); + assert!(actual.to_str() == Some($expected), + "pushing {:?} onto {:?}: Expected {:?}, got {:?}", + $push, $path, $expected, actual.to_str().unwrap()); + }); + ); + + if cfg!(unix) { + tp!("", "foo", "foo"); + tp!("foo", "bar", "foo/bar"); + tp!("foo/", "bar", "foo/bar"); + tp!("foo//", "bar", "foo//bar"); + tp!("foo/.", "bar", "foo/./bar"); + tp!("foo./.", "bar", "foo././bar"); + tp!("foo", "", "foo/"); + tp!("foo", ".", "foo/."); + tp!("foo", "..", "foo/.."); + tp!("foo", "/", "/"); + tp!("/foo/bar", "/", "/"); + tp!("/foo/bar", "/baz", "/baz"); + tp!("/foo/bar", "./baz", "/foo/bar/./baz"); + } else { + tp!("", "foo", "foo"); + tp!("foo", "bar", r"foo\bar"); + tp!("foo/", "bar", r"foo/bar"); + tp!(r"foo\", "bar", r"foo\bar"); + tp!("foo//", "bar", r"foo//bar"); + tp!(r"foo\\", "bar", r"foo\\bar"); + tp!("foo/.", "bar", r"foo/.\bar"); + tp!("foo./.", "bar", r"foo./.\bar"); + tp!(r"foo\.", "bar", r"foo\.\bar"); + tp!(r"foo.\.", "bar", r"foo.\.\bar"); + tp!("foo", "", "foo\\"); + tp!("foo", ".", r"foo\."); + tp!("foo", "..", r"foo\.."); + tp!("foo", "/", "/"); + tp!("foo", r"\", r"\"); + tp!("/foo/bar", "/", "/"); + tp!(r"\foo\bar", r"\", r"\"); + tp!("/foo/bar", "/baz", "/baz"); + tp!("/foo/bar", r"\baz", r"\baz"); + tp!("/foo/bar", "./baz", r"/foo/bar\./baz"); + tp!("/foo/bar", r".\baz", r"/foo/bar\.\baz"); + + tp!("c:\\", "windows", "c:\\windows"); + tp!("c:", "windows", "c:windows"); + + tp!("a\\b\\c", "d", "a\\b\\c\\d"); + tp!("\\a\\b\\c", "d", "\\a\\b\\c\\d"); + tp!("a\\b", "c\\d", "a\\b\\c\\d"); + tp!("a\\b", "\\c\\d", "\\c\\d"); + tp!("a\\b", ".", "a\\b\\."); + tp!("a\\b", "..\\c", "a\\b\\..\\c"); + tp!("a\\b", "C:a.txt", "C:a.txt"); + tp!("a\\b", "C:\\a.txt", "C:\\a.txt"); + tp!("C:\\a", "C:\\b.txt", "C:\\b.txt"); + tp!("C:\\a\\b\\c", "C:d", "C:d"); + tp!("C:a\\b\\c", "C:d", "C:d"); + tp!("C:", r"a\b\c", r"C:a\b\c"); + tp!("C:", r"..\a", r"C:..\a"); + tp!("\\\\server\\share\\foo", "bar", "\\\\server\\share\\foo\\bar"); + tp!("\\\\server\\share\\foo", "C:baz", "C:baz"); + tp!("\\\\?\\C:\\a\\b", "C:c\\d", "C:c\\d"); + tp!("\\\\?\\C:a\\b", "C:c\\d", "C:c\\d"); + tp!("\\\\?\\C:\\a\\b", "C:\\c\\d", "C:\\c\\d"); + tp!("\\\\?\\foo\\bar", "baz", "\\\\?\\foo\\bar\\baz"); + tp!("\\\\?\\UNC\\server\\share\\foo", "bar", "\\\\?\\UNC\\server\\share\\foo\\bar"); + tp!("\\\\?\\UNC\\server\\share", "C:\\a", "C:\\a"); + tp!("\\\\?\\UNC\\server\\share", "C:a", "C:a"); + + // Note: modified from old path API + tp!("\\\\?\\UNC\\server", "foo", "\\\\?\\UNC\\server\\foo"); + + tp!("C:\\a", "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share"); + tp!("\\\\.\\foo\\bar", "baz", "\\\\.\\foo\\bar\\baz"); + tp!("\\\\.\\foo\\bar", "C:a", "C:a"); + // again, not sure about the following, but I'm assuming \\.\ should be verbatim + tp!("\\\\.\\foo", "..\\bar", "\\\\.\\foo\\..\\bar"); + + tp!("\\\\?\\C:", "foo", "\\\\?\\C:\\foo"); // this is a weird one + } + } + + #[test] + pub fn test_pop() { + macro_rules! tp( + ($path:expr, $expected:expr, $output:expr) => ( { + let mut actual = PathBuf::new($path); + let output = actual.pop(); + assert!(actual.to_str() == Some($expected) && output == $output, + "popping from {:?}: Expected {:?}/{:?}, got {:?}/{:?}", + $path, $expected, $output, + actual.to_str().unwrap(), output); + }); + ); + + tp!("", "", false); + tp!("/", "/", false); + tp!("foo", "foo", false); + tp!(".", ".", false); + tp!("/foo", "/", true); + tp!("/foo/bar", "/foo", true); + tp!("foo/bar", "foo", true); + tp!("foo/.", "foo", true); + tp!("foo//bar", "foo", true); + + if cfg!(windows) { + tp!("a\\b\\c", "a\\b", true); + tp!("\\a", "\\", true); + tp!("\\", "\\", false); + + tp!("C:\\a\\b", "C:\\a", true); + tp!("C:\\a", "C:\\", true); + tp!("C:\\", "C:\\", false); + tp!("C:a\\b", "C:a", true); + tp!("C:a", "C:", true); + tp!("C:", "C:", false); + tp!("\\\\server\\share\\a\\b", "\\\\server\\share\\a", true); + tp!("\\\\server\\share\\a", "\\\\server\\share\\", true); + tp!("\\\\server\\share", "\\\\server\\share", false); + tp!("\\\\?\\a\\b\\c", "\\\\?\\a\\b", true); + tp!("\\\\?\\a\\b", "\\\\?\\a\\", true); + tp!("\\\\?\\a", "\\\\?\\a", false); + tp!("\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", true); + tp!("\\\\?\\C:\\a", "\\\\?\\C:\\", true); + tp!("\\\\?\\C:\\", "\\\\?\\C:\\", false); + tp!("\\\\?\\UNC\\server\\share\\a\\b", "\\\\?\\UNC\\server\\share\\a", true); + tp!("\\\\?\\UNC\\server\\share\\a", "\\\\?\\UNC\\server\\share\\", true); + tp!("\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share", false); + tp!("\\\\.\\a\\b\\c", "\\\\.\\a\\b", true); + tp!("\\\\.\\a\\b", "\\\\.\\a\\", true); + tp!("\\\\.\\a", "\\\\.\\a", false); + + tp!("\\\\?\\a\\b\\", "\\\\?\\a\\b", true); + } + } + + #[test] + pub fn test_set_file_name() { + macro_rules! tfn( + ($path:expr, $file:expr, $expected:expr) => ( { + let mut p = PathBuf::new($path); + p.set_file_name($file); + assert!(p.to_str() == Some($expected), + "setting file name of {:?} to {:?}: Expected {:?}, got {:?}", + $path, $file, $expected, + p.to_str().unwrap()); + }); + ); + + tfn!("foo", "foo", "foo"); + tfn!("foo", "bar", "bar"); + tfn!("foo", "", ""); + tfn!("", "foo", "foo"); + if cfg!(unix) { + tfn!(".", "foo", "./foo"); + tfn!("foo/", "bar", "foo/bar"); + tfn!("foo/.", "bar", "foo/./bar"); + tfn!("..", "foo", "../foo"); + tfn!("foo/..", "bar", "foo/../bar"); + tfn!("/", "foo", "/foo"); + } else { + tfn!(".", "foo", r".\foo"); + tfn!(r"foo\", "bar", r"foo\bar"); + tfn!(r"foo\.", "bar", r"foo\.\bar"); + tfn!("..", "foo", r"..\foo"); + tfn!(r"foo\..", "bar", r"foo\..\bar"); + tfn!(r"\", "foo", r"\foo"); + } + } + + #[test] + pub fn test_set_extension() { + macro_rules! tfe( + ($path:expr, $ext:expr, $expected:expr, $output:expr) => ( { + let mut p = PathBuf::new($path); + let output = p.set_extension($ext); + assert!(p.to_str() == Some($expected) && output == $output, + "setting extension of {:?} to {:?}: Expected {:?}/{:?}, got {:?}/{:?}", + $path, $ext, $expected, $output, + p.to_str().unwrap(), output); + }); + ); + + tfe!("foo", "txt", "foo.txt", true); + tfe!("foo.bar", "txt", "foo.txt", true); + tfe!("foo.bar.baz", "txt", "foo.bar.txt", true); + tfe!(".test", "txt", ".test.txt", true); + tfe!("foo.txt", "", "foo", true); + tfe!("foo", "", "foo", true); + tfe!("", "foo", "", false); + tfe!(".", "foo", ".", false); + tfe!("foo/", "bar", "foo/", false); + tfe!("foo/.", "bar", "foo/.", false); + tfe!("..", "foo", "..", false); + tfe!("foo/..", "bar", "foo/..", false); + tfe!("/", "foo", "/", false); + } + + #[test] + pub fn test_compare() { + macro_rules! tc( + ($path1:expr, $path2:expr, eq: $eq:expr, + starts_with: $starts_with:expr, ends_with: $ends_with:expr, + relative_from: $relative_from:expr) => ({ + let path1 = Path::new($path1); + let path2 = Path::new($path2); + + let eq = path1 == path2; + assert!(eq == $eq, "{:?} == {:?}, expected {:?}, got {:?}", + $path1, $path2, $eq, eq); + + let starts_with = path1.starts_with(path2); + assert!(starts_with == $starts_with, + "{:?}.starts_with({:?}), expected {:?}, got {:?}", $path1, $path2, + $starts_with, starts_with); + + let ends_with = path1.ends_with(path2); + assert!(ends_with == $ends_with, + "{:?}.ends_with({:?}), expected {:?}, got {:?}", $path1, $path2, + $ends_with, ends_with); + + let relative_from = path1.relative_from(path2).map(|p| p.to_str().unwrap()); + let exp: Option<&str> = $relative_from; + assert!(relative_from == exp, + "{:?}.relative_from({:?}), expected {:?}, got {:?}", $path1, $path2, + exp, relative_from); + }); + ); + + tc!("", "", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo", "", + eq: false, + starts_with: true, + ends_with: true, + relative_from: Some("foo") + ); + + tc!("", "foo", + eq: false, + starts_with: false, + ends_with: false, + relative_from: None + ); + + tc!("foo", "foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo/", "foo", + eq: false, + starts_with: true, + ends_with: false, + relative_from: Some(".") + ); + + tc!("foo/bar", "foo", + eq: false, + starts_with: true, + ends_with: false, + relative_from: Some("bar") + ); + + tc!("foo/bar/baz", "foo/bar", + eq: false, + starts_with: true, + ends_with: false, + relative_from: Some("baz") + ); + + tc!("foo/bar", "foo/bar/baz", + eq: false, + starts_with: false, + ends_with: false, + relative_from: None + ); + + tc!("./foo/bar/", ".", + eq: false, + starts_with: true, + ends_with: true, + relative_from: Some("foo/bar/") + ); + } +} diff --git a/src/libstd/prelude/v1.rs b/src/libstd/prelude/v1.rs index 2398485afefb..d2dc33451200 100644 --- a/src/libstd/prelude/v1.rs +++ b/src/libstd/prelude/v1.rs @@ -56,7 +56,7 @@ #[doc(no_inline)] pub use vec::Vec; // NB: remove when path reform lands -#[doc(no_inline)] pub use path::{Path, GenericPath}; +#[doc(no_inline)] pub use old_path::{Path, GenericPath}; // NB: remove when I/O reform lands #[doc(no_inline)] pub use old_io::{Buffer, Writer, Reader, Seek, BufferPrelude}; // NB: remove when range syntax lands diff --git a/src/libstd/rand/os.rs b/src/libstd/rand/os.rs index 4b45d5501c23..797b9332f17d 100644 --- a/src/libstd/rand/os.rs +++ b/src/libstd/rand/os.rs @@ -20,7 +20,7 @@ mod imp { use self::OsRngInner::*; use old_io::{IoResult, File}; - use path::Path; + use old_path::Path; use rand::Rng; use rand::reader::ReaderRng; use result::Result::Ok; diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index ae01586c7039..6f6b4c587174 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -16,7 +16,7 @@ use prelude::v1::*; use sys::{last_error, retry}; use ffi::CString; use num::Int; -use path::BytesContainer; +use old_path::BytesContainer; use collections; pub mod backtrace; diff --git a/src/libstd/sys/unix/c.rs b/src/libstd/sys/unix/c.rs index 89bd9a234065..50a8e6b73e38 100644 --- a/src/libstd/sys/unix/c.rs +++ b/src/libstd/sys/unix/c.rs @@ -143,6 +143,7 @@ extern { 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(not(target_os = "ios"))] pub fn getpwuid_r(uid: libc::uid_t, pwd: *mut passwd, buf: *mut libc::c_char, diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index b03b9046966a..427cf21ac70a 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -18,10 +18,11 @@ use prelude::v1::*; use ffi; -use old_io::{self, IoResult, IoError}; +use io::ErrorKind; use libc; use num::{Int, SignedInt}; use num; +use old_io::{self, IoResult, IoError}; use str; use sys_common::mkerr_libc; @@ -133,6 +134,35 @@ pub fn decode_error_detailed(errno: i32) -> IoError { err } +pub fn decode_error_kind(errno: i32) -> ErrorKind { + match errno as libc::c_int { + libc::ECONNREFUSED => ErrorKind::ConnectionRefused, + libc::ECONNRESET => ErrorKind::ConnectionReset, + libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied, + libc::EPIPE => ErrorKind::BrokenPipe, + libc::ENOTCONN => ErrorKind::NotConnected, + libc::ECONNABORTED => ErrorKind::ConnectionAborted, + libc::EADDRNOTAVAIL => ErrorKind::ConnectionRefused, + libc::EADDRINUSE => ErrorKind::ConnectionRefused, + libc::ENOENT => ErrorKind::FileNotFound, + libc::EISDIR => ErrorKind::InvalidInput, + libc::EINTR => ErrorKind::Interrupted, + libc::EINVAL => ErrorKind::InvalidInput, + libc::ENOTTY => ErrorKind::MismatchedFileTypeForOperation, + libc::ETIMEDOUT => ErrorKind::TimedOut, + libc::ECANCELED => ErrorKind::TimedOut, + libc::consts::os::posix88::EEXIST => ErrorKind::PathAlreadyExists, + + // These two constants can have the same value on some systems, + // but different values on others, so we can't use a match + // clause + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => + ErrorKind::ResourceUnavailable, + + _ => ErrorKind::Other, + } +} + #[inline] pub fn retry (mut f: F) -> T where T: SignedInt, diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index b3f379629458..5004ff713c45 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -307,23 +307,23 @@ pub fn args() -> Args { let mut res = Vec::new(); unsafe { - let processInfoSel = sel_registerName("processInfo\0".as_ptr()); - let argumentsSel = sel_registerName("arguments\0".as_ptr()); - let utf8Sel = sel_registerName("UTF8String\0".as_ptr()); - let countSel = sel_registerName("count\0".as_ptr()); - let objectAtSel = sel_registerName("objectAtIndex:\0".as_ptr()); + let process_info_sel = sel_registerName("processInfo\0".as_ptr()); + let arguments_sel = sel_registerName("arguments\0".as_ptr()); + let utf8_sel = sel_registerName("UTF8String\0".as_ptr()); + let count_sel = sel_registerName("count\0".as_ptr()); + let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr()); let klass = objc_getClass("NSProcessInfo\0".as_ptr()); - let info = objc_msgSend(klass, processInfoSel); - let args = objc_msgSend(info, argumentsSel); + let info = objc_msgSend(klass, process_info_sel); + let args = objc_msgSend(info, arguments_sel); - let cnt: int = mem::transmute(objc_msgSend(args, countSel)); + let cnt: int = mem::transmute(objc_msgSend(args, count_sel)); for i in range(0, cnt) { - let tmp = objc_msgSend(args, objectAtSel, i); + let tmp = objc_msgSend(args, object_at_sel, i); let utf_c_str: *const libc::c_char = - mem::transmute(objc_msgSend(tmp, utf8Sel)); - let bytes = ffi::c_str_to_bytes(&utf_c_str).to_vec(); - res.push(OsString::from_vec(bytes)) + mem::transmute(objc_msgSend(tmp, utf8_sel)); + let bytes = ffi::c_str_to_bytes(&utf_c_str); + res.push(OsString::from_str(str::from_utf8(bytes).unwrap())) } } @@ -455,9 +455,11 @@ pub fn home_dir() -> Option { Path::new(os.into_vec()) }); - #[cfg(target_os = "android")] + #[cfg(any(target_os = "android", + target_os = "ios"))] unsafe fn fallback() -> Option { None } - #[cfg(not(target_os = "android"))] + #[cfg(not(any(target_os = "android", + target_os = "ios")))] unsafe fn fallback() -> Option { let mut amt = match libc::sysconf(c::_SC_GETPW_R_SIZE_MAX) { n if n < 0 => 512 as usize, diff --git a/src/libstd/sys/unix/os_str.rs b/src/libstd/sys/unix/os_str.rs index 3dd89ad07593..a2c93dea6a4f 100644 --- a/src/libstd/sys/unix/os_str.rs +++ b/src/libstd/sys/unix/os_str.rs @@ -20,7 +20,7 @@ use str; use string::{String, CowString}; use mem; -#[derive(Clone)] +#[derive(Clone, Hash)] pub struct Buf { pub inner: Vec } diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index 7e117b10a347..20f86227e8ea 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -20,7 +20,7 @@ use old_io::{self, IoResult, IoError, EndOfFile}; use libc::{self, pid_t, c_void, c_int}; use mem; use os; -use path::BytesContainer; +use old_path::BytesContainer; use ptr; use sync::mpsc::{channel, Sender, Receiver}; use sys::fs::FileDesc; diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs index 66712b9e3a1e..92e309da34be 100644 --- a/src/libstd/sys/windows/backtrace.rs +++ b/src/libstd/sys/windows/backtrace.rs @@ -32,7 +32,7 @@ use libc; use mem; use ops::Drop; use option::Option::{Some}; -use path::Path; +use old_path::Path; use ptr; use result::Result::{Ok, Err}; use slice::SliceExt; diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 8dd467eba9e2..f1af70e2cf7d 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -15,6 +15,7 @@ use prelude::v1::*; use ffi::OsStr; +use io::ErrorKind; use libc; use mem; use old_io::{self, IoResult, IoError}; @@ -143,6 +144,34 @@ pub fn decode_error_detailed(errno: i32) -> IoError { err } +pub fn decode_error_kind(errno: i32) -> ErrorKind { + match errno as libc::c_int { + libc::ERROR_ACCESS_DENIED => ErrorKind::PermissionDenied, + libc::ERROR_ALREADY_EXISTS => ErrorKind::PathAlreadyExists, + libc::ERROR_BROKEN_PIPE => ErrorKind::BrokenPipe, + libc::ERROR_FILE_NOT_FOUND => ErrorKind::FileNotFound, + libc::ERROR_INVALID_FUNCTION => ErrorKind::InvalidInput, + libc::ERROR_INVALID_HANDLE => ErrorKind::MismatchedFileTypeForOperation, + libc::ERROR_INVALID_NAME => ErrorKind::InvalidInput, + libc::ERROR_NOTHING_TO_TERMINATE => ErrorKind::InvalidInput, + libc::ERROR_NO_DATA => ErrorKind::BrokenPipe, + libc::ERROR_OPERATION_ABORTED => ErrorKind::TimedOut, + + libc::WSAEACCES => ErrorKind::PermissionDenied, + libc::WSAEADDRINUSE => ErrorKind::ConnectionRefused, + libc::WSAEADDRNOTAVAIL => ErrorKind::ConnectionRefused, + libc::WSAECONNABORTED => ErrorKind::ConnectionAborted, + libc::WSAECONNREFUSED => ErrorKind::ConnectionRefused, + libc::WSAECONNRESET => ErrorKind::ConnectionReset, + libc::WSAEINVAL => ErrorKind::InvalidInput, + libc::WSAENOTCONN => ErrorKind::NotConnected, + libc::WSAEWOULDBLOCK => ErrorKind::ResourceUnavailable, + + _ => ErrorKind::Other, + } +} + + #[inline] pub fn retry(f: F) -> I where F: FnOnce() -> I { f() } // PR rust-lang/rust/#17020 diff --git a/src/libstd/sys/windows/os_str.rs b/src/libstd/sys/windows/os_str.rs index aab2406cef92..af94b56bf1f7 100644 --- a/src/libstd/sys/windows/os_str.rs +++ b/src/libstd/sys/windows/os_str.rs @@ -18,7 +18,7 @@ use result::Result; use option::Option; use mem; -#[derive(Clone)] +#[derive(Clone, Hash)] pub struct Buf { pub inner: Wtf8Buf } diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index 3ca735f7fdfd..315c41e779a3 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -23,7 +23,7 @@ use old_io::process::{ProcessExit, ExitStatus}; use old_io::{IoResult, IoError}; use old_io; use os; -use path::BytesContainer; +use old_path::BytesContainer; use ptr; use str; use sync::{StaticMutex, MUTEX_INIT}; diff --git a/src/libstd/thread_local/mod.rs b/src/libstd/thread_local/mod.rs index d4d777789dd5..9de5fd1c770e 100644 --- a/src/libstd/thread_local/mod.rs +++ b/src/libstd/thread_local/mod.rs @@ -45,6 +45,7 @@ pub mod scoped; // Sure wish we had macro hygiene, no? #[doc(hidden)] +#[stable(feature = "rust1", since = "1.0.0")] pub mod __impl { pub use super::imp::Key as KeyInner; pub use super::imp::destroy_value; diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index d7283db25a5f..34eeedeaa765 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -48,7 +48,6 @@ pub use self::TraitItem::*; pub use self::Ty_::*; pub use self::TyParamBound::*; pub use self::UintTy::*; -pub use self::ClosureKind::*; pub use self::UnOp::*; pub use self::UnsafeSource::*; pub use self::VariantKind::*; @@ -736,7 +735,7 @@ pub enum Expr_ { // FIXME #6993: change to Option ... or not, if these are hygienic. ExprLoop(P, Option), ExprMatch(P, Vec, MatchSource), - ExprClosure(CaptureClause, Option, P, P), + ExprClosure(CaptureClause, P, P), ExprBlock(P), ExprAssign(P, P), @@ -1687,13 +1686,6 @@ impl ForeignItem_ { } } -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] -pub enum ClosureKind { - FnClosureKind, - FnMutClosureKind, - FnOnceClosureKind, -} - /// The data we save and restore about an inlined item or method. This is not /// part of the AST that we parse from a file, but it becomes part of the tree /// that we trans. diff --git a/src/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs index 53787d71eef8..a85b87f47d6e 100644 --- a/src/libsyntax/ast_map/blocks.rs +++ b/src/libsyntax/ast_map/blocks.rs @@ -218,7 +218,7 @@ impl<'a> FnLikeNode<'a> { } } ast_map::NodeExpr(e) => match e.node { - ast::ExprClosure(_, _, ref decl, ref block) => + ast::ExprClosure(_, ref decl, ref block) => closure(ClosureParts::new(&**decl, &**block, e.id, e.span)), _ => panic!("expr FnLikeNode that is not fn-like"), }, diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 2b3a72126831..53c35ef34cd0 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -876,14 +876,14 @@ impl<'a> AstBuilder for ExtCtxt<'a> { fn lambda_fn_decl(&self, span: Span, fn_decl: P, blk: P) -> P { - self.expr(span, ast::ExprClosure(ast::CaptureByRef, None, fn_decl, blk)) + self.expr(span, ast::ExprClosure(ast::CaptureByRef, fn_decl, blk)) } fn lambda(&self, span: Span, ids: Vec, blk: P) -> P { let fn_decl = self.fn_decl( ids.iter().map(|id| self.arg(span, *id, self.ty_infer(span))).collect(), self.ty_infer(span)); - self.expr(span, ast::ExprClosure(ast::CaptureByRef, None, fn_decl, blk)) + self.expr(span, ast::ExprClosure(ast::CaptureByRef, fn_decl, blk)) } fn lambda0(&self, span: Span, blk: P) -> P { self.lambda(span, Vec::new(), blk) diff --git a/src/libsyntax/ext/deriving/rand.rs b/src/libsyntax/ext/deriving/rand.rs index 9fd5091e194d..739c73a70b02 100644 --- a/src/libsyntax/ext/deriving/rand.rs +++ b/src/libsyntax/ext/deriving/rand.rs @@ -66,7 +66,7 @@ fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) cx.ident_of("Rand"), cx.ident_of("rand") ); - let mut rand_call = |&mut: cx: &mut ExtCtxt, span| { + let rand_call = |&: cx: &mut ExtCtxt, span| { cx.expr_call_global(span, rand_ident.clone(), vec!(rng.clone())) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 6eacb3440189..77440914342f 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -322,11 +322,10 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { fld.cx.expr_match(span, into_iter_expr, vec![iter_arm]) } - ast::ExprClosure(capture_clause, opt_kind, fn_decl, block) => { + ast::ExprClosure(capture_clause, fn_decl, block) => { let (rewritten_fn_decl, rewritten_block) = expand_and_rename_fn_decl_and_block(fn_decl, block, fld); let new_node = ast::ExprClosure(capture_clause, - opt_kind, rewritten_fn_decl, rewritten_block); P(ast::Expr{id:id, node: new_node, span: fld.new_span(span)}) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 4e76359e9304..d7a51e1149f3 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -119,6 +119,9 @@ static KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[ // Allows use of #[staged_api] ("staged_api", "1.0.0", Active), + + // Allows using items which are missing stability attributes + ("unmarked_api", "1.0.0", Active) ]; enum Status { @@ -145,6 +148,7 @@ pub struct Features { pub quote: bool, pub old_orphan_check: bool, pub simd_ffi: bool, + pub unmarked_api: bool, pub lib_features: Vec<(InternedString, Span)> } @@ -157,6 +161,7 @@ impl Features { quote: false, old_orphan_check: false, simd_ffi: false, + unmarked_api: false, lib_features: Vec::new() } } @@ -566,6 +571,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C quote: cx.has_feature("quote"), old_orphan_check: cx.has_feature("old_orphan_check"), simd_ffi: cx.has_feature("simd_ffi"), + unmarked_api: cx.has_feature("unmarked_api"), lib_features: unknown_features } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 9012ec2114d0..07b6af651f61 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1325,9 +1325,8 @@ pub fn noop_fold_expr(Expr {id, node, span}: Expr, folder: &mut T) -> arms.move_map(|x| folder.fold_arm(x)), source) } - ExprClosure(capture_clause, opt_kind, decl, body) => { + ExprClosure(capture_clause, decl, body) => { ExprClosure(capture_clause, - opt_kind, folder.fold_fn_decl(decl), folder.fold_block(body)) } diff --git a/src/libsyntax/parse/obsolete.rs b/src/libsyntax/parse/obsolete.rs index a3600506057a..60de6c909b78 100644 --- a/src/libsyntax/parse/obsolete.rs +++ b/src/libsyntax/parse/obsolete.rs @@ -27,6 +27,7 @@ pub enum ObsoleteSyntax { ProcType, ProcExpr, ClosureType, + ClosureKind, } pub trait ParserObsoleteMethods { @@ -65,6 +66,10 @@ impl<'a> ParserObsoleteMethods for parser::Parser<'a> { "`|usize| -> bool` closure type syntax", "use unboxed closures instead, no type annotation needed" ), + ObsoleteSyntax::ClosureKind => ( + "`:`, `&mut:`, or `&:` syntax", + "rely on inference instead" + ), ObsoleteSyntax::Sized => ( "`Sized? T` syntax for removing the `Sized` bound", "write `T: ?Sized` instead" diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c3182602a4b8..2cb265033c39 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -28,8 +28,6 @@ use ast::{ExprLit, ExprLoop, ExprMac, ExprRange}; use ast::{ExprMethodCall, ExprParen, ExprPath, ExprQPath}; use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary}; use ast::{ExprVec, ExprWhile, ExprWhileLet, ExprForLoop, Field, FnDecl}; -use ast::{FnClosureKind, FnMutClosureKind}; -use ast::{FnOnceClosureKind}; use ast::{ForeignItem, ForeignItemStatic, ForeignItemFn, ForeignMod, FunctionRetTy}; use ast::{Ident, Inherited, ImplItem, Item, Item_, ItemStatic}; use ast::{ItemEnum, ItemFn, ItemForeignMod, ItemImpl, ItemConst}; @@ -57,7 +55,7 @@ use ast::{TyFixedLengthVec, TyBareFn}; use ast::{TyTypeof, TyInfer, TypeMethod}; use ast::{TyParam, TyParamBound, TyParen, TyPath, TyPolyTraitRef, TyPtr, TyQPath}; use ast::{TyRptr, TyTup, TyU32, TyVec, UnUniq}; -use ast::{TypeImplItem, TypeTraitItem, Typedef, ClosureKind}; +use ast::{TypeImplItem, TypeTraitItem, Typedef,}; use ast::{UnnamedField, UnsafeBlock}; use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple}; use ast::{Visibility, WhereClause}; @@ -1139,29 +1137,36 @@ impl<'a> Parser<'a> { TyInfer } - /// Parses an optional closure kind (`&:`, `&mut:`, or `:`). - pub fn parse_optional_closure_kind(&mut self) -> Option { - if self.check(&token::BinOp(token::And)) && - self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) && - self.look_ahead(2, |t| *t == token::Colon) { + /// Parses an obsolete closure kind (`&:`, `&mut:`, or `:`). + pub fn parse_obsolete_closure_kind(&mut self) { + // let lo = self.span.lo; + if + self.check(&token::BinOp(token::And)) && + self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) && + self.look_ahead(2, |t| *t == token::Colon) + { self.bump(); self.bump(); self.bump(); - return Some(FnMutClosureKind) + } else if + self.token == token::BinOp(token::And) && + self.look_ahead(1, |t| *t == token::Colon) + { + self.bump(); + self.bump(); + return; + } else if + self.eat(&token::Colon) + { + /* nothing */ + } else { + return; } - if self.token == token::BinOp(token::And) && - self.look_ahead(1, |t| *t == token::Colon) { - self.bump(); - self.bump(); - return Some(FnClosureKind) - } - - if self.eat(&token::Colon) { - return Some(FnOnceClosureKind) - } - - return None + // SNAP 474b324 + // Enable these obsolete errors after snapshot: + // let span = mk_sp(lo, self.span.hi); + // self.obsolete(span, ObsoleteSyntax::ClosureKind); } pub fn parse_ty_bare_fn_or_ty_closure(&mut self, lifetime_defs: Vec) -> Ty_ { @@ -3047,7 +3052,7 @@ impl<'a> Parser<'a> { -> P { let lo = self.span.lo; - let (decl, optional_closure_kind) = self.parse_fn_block_decl(); + let decl = self.parse_fn_block_decl(); let body = self.parse_expr(); let fakeblock = P(ast::Block { id: ast::DUMMY_NODE_ID, @@ -3060,7 +3065,7 @@ impl<'a> Parser<'a> { self.mk_expr( lo, fakeblock.span.hi, - ExprClosure(capture_clause, optional_closure_kind, decl, fakeblock)) + ExprClosure(capture_clause, decl, fakeblock)) } pub fn parse_else_expr(&mut self) -> P { @@ -4529,30 +4534,29 @@ impl<'a> Parser<'a> { } // parse the |arg, arg| header on a lambda - fn parse_fn_block_decl(&mut self) -> (P, Option) { - let (optional_closure_kind, inputs_captures) = { + fn parse_fn_block_decl(&mut self) -> P { + let inputs_captures = { if self.eat(&token::OrOr) { - (None, Vec::new()) + Vec::new() } else { self.expect(&token::BinOp(token::Or)); - let optional_closure_kind = - self.parse_optional_closure_kind(); + self.parse_obsolete_closure_kind(); let args = self.parse_seq_to_before_end( &token::BinOp(token::Or), seq_sep_trailing_allowed(token::Comma), |p| p.parse_fn_block_arg() ); self.bump(); - (optional_closure_kind, args) + args } }; let output = self.parse_ret_ty(); - (P(FnDecl { + P(FnDecl { inputs: inputs_captures, output: output, variadic: false - }), optional_closure_kind) + }) } /// Parses the `(arg, arg) -> return_type` header on a procedure. diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 5c3892e49c05..129c1d20bc04 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -25,7 +25,7 @@ use serialize::{Decodable, Decoder, Encodable, Encoder}; use std::fmt; use std::mem; use std::ops::Deref; -use std::path::BytesContainer; +use std::old_path::BytesContainer; use std::rc::Rc; #[allow(non_camel_case_types)] diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index e6d895a49fcd..ee8e207fa6c0 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -11,11 +11,9 @@ pub use self::AnnNode::*; use abi; -use ast::{self, FnClosureKind, FnMutClosureKind}; -use ast::{FnOnceClosureKind}; +use ast; use ast::{MethodImplItem, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier}; use ast::{RequiredMethod, ProvidedMethod, TypeImplItem, TypeTraitItem}; -use ast::{ClosureKind}; use ast_util; use owned_slice::OwnedSlice; use attr::{AttrMetaMethods, AttributeMethods}; @@ -350,7 +348,7 @@ pub fn method_to_string(p: &ast::Method) -> String { } pub fn fn_block_to_string(p: &ast::FnDecl) -> String { - $to_string(|s| s.print_fn_block_args(p, None)) + $to_string(|s| s.print_fn_block_args(p)) } pub fn path_to_string(p: &ast::Path) -> String { @@ -1747,10 +1745,10 @@ impl<'a> State<'a> { } try!(self.bclose_(expr.span, indent_unit)); } - ast::ExprClosure(capture_clause, opt_kind, ref decl, ref body) => { + ast::ExprClosure(capture_clause, ref decl, ref body) => { try!(self.print_capture_clause(capture_clause)); - try!(self.print_fn_block_args(&**decl, opt_kind)); + try!(self.print_fn_block_args(&**decl)); try!(space(&mut self.s)); if !body.stmts.is_empty() || !body.expr.is_some() { @@ -2350,16 +2348,9 @@ impl<'a> State<'a> { pub fn print_fn_block_args( &mut self, - decl: &ast::FnDecl, - closure_kind: Option) + decl: &ast::FnDecl) -> IoResult<()> { try!(word(&mut self.s, "|")); - match closure_kind { - None => {} - Some(FnClosureKind) => try!(self.word_space("&:")), - Some(FnMutClosureKind) => try!(self.word_space("&mut:")), - Some(FnOnceClosureKind) => try!(self.word_space(":")), - } try!(self.print_fn_args(decl, None)); try!(word(&mut self.s, "|")); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index bd84306fe17e..fbcfcaadf12b 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -836,7 +836,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_arm(arm) } } - ExprClosure(_, _, ref function_declaration, ref body) => { + ExprClosure(_, ref function_declaration, ref body) => { visitor.visit_fn(FkFnBlock, &**function_declaration, &**body, diff --git a/src/test/auxiliary/lint_stability.rs b/src/test/auxiliary/lint_stability.rs index 3679557d06bb..7ac3925fb247 100644 --- a/src/test/auxiliary/lint_stability.rs +++ b/src/test/auxiliary/lint_stability.rs @@ -11,6 +11,7 @@ #![crate_type = "lib"] #![feature(staged_api)] #![staged_api] +#![stable(feature = "lint_stability", since = "1.0.0")] #[stable(feature = "test_feature", since = "1.0.0")] #[deprecated(since = "1.0.0")] @@ -31,8 +32,6 @@ pub fn unstable() {} #[unstable(feature = "test_feature", reason = "text")] pub fn unstable_text() {} -pub fn unmarked() {} - #[stable(feature = "rust1", since = "1.0.0")] pub fn stable() {} #[stable(feature = "rust1", since = "1.0.0", reason = "text")] @@ -61,8 +60,6 @@ impl MethodTester { #[unstable(feature = "test_feature", reason = "text")] pub fn method_unstable_text(&self) {} - pub fn method_unmarked(&self) {} - #[stable(feature = "rust1", since = "1.0.0")] pub fn method_stable(&self) {} #[stable(feature = "rust1", since = "1.0.0", reason = "text")] @@ -79,6 +76,7 @@ impl MethodTester { pub fn method_frozen_text(&self) {} } +#[stable(feature = "test_feature", since = "1.0.0")] pub trait Trait { #[stable(feature = "test_feature", since = "1.0.0")] #[deprecated(since = "1.0.0")] @@ -99,8 +97,6 @@ pub trait Trait { #[unstable(feature = "test_feature", reason = "text")] fn trait_unstable_text(&self) {} - fn trait_unmarked(&self) {} - #[stable(feature = "rust1", since = "1.0.0")] fn trait_stable(&self) {} #[stable(feature = "rust1", since = "1.0.0", reason = "text")] @@ -130,7 +126,6 @@ pub struct DeprecatedStruct { pub i: int } pub struct DeprecatedUnstableStruct { pub i: int } #[unstable(feature = "test_feature")] pub struct UnstableStruct { pub i: int } -pub struct UnmarkedStruct { pub i: int } #[stable(feature = "rust1", since = "1.0.0")] pub struct StableStruct { pub i: int } @@ -142,10 +137,10 @@ pub struct DeprecatedUnitStruct; pub struct DeprecatedUnstableUnitStruct; #[unstable(feature = "test_feature")] pub struct UnstableUnitStruct; -pub struct UnmarkedUnitStruct; #[stable(feature = "rust1", since = "1.0.0")] pub struct StableUnitStruct; +#[stable(feature = "test_feature", since = "1.0.0")] pub enum Enum { #[stable(feature = "test_feature", since = "1.0.0")] #[deprecated(since = "1.0.0")] @@ -156,7 +151,6 @@ pub enum Enum { #[unstable(feature = "test_feature")] UnstableVariant, - UnmarkedVariant, #[stable(feature = "rust1", since = "1.0.0")] StableVariant, } @@ -169,7 +163,6 @@ pub struct DeprecatedTupleStruct(pub int); pub struct DeprecatedUnstableTupleStruct(pub int); #[unstable(feature = "test_feature")] pub struct UnstableTupleStruct(pub int); -pub struct UnmarkedTupleStruct(pub int); #[stable(feature = "rust1", since = "1.0.0")] pub struct StableTupleStruct(pub int); diff --git a/src/test/compile-fail/borrow-immutable-upvar-mutation.rs b/src/test/compile-fail/borrow-immutable-upvar-mutation.rs index 12555c550729..7033f5caef6c 100644 --- a/src/test/compile-fail/borrow-immutable-upvar-mutation.rs +++ b/src/test/compile-fail/borrow-immutable-upvar-mutation.rs @@ -8,34 +8,38 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(unboxed_closures, overloaded_calls)] +#![feature(unboxed_closures)] // Tests that we can't assign to or mutably borrow upvars from `Fn` // closures (issue #17780) fn set(x: &mut usize) { *x = 5; } +fn to_fn>(f: F) -> F { f } +fn to_fn_mut>(f: F) -> F { f } + fn main() { // By-ref captures { let mut x = 0us; - let _f = |&:| x = 42; //~ ERROR cannot assign + let _f = to_fn(|| x = 42); //~ ERROR cannot assign let mut y = 0us; - let _g = |&:| set(&mut y); //~ ERROR cannot borrow + let _g = to_fn(|| set(&mut y)); //~ ERROR cannot borrow let mut z = 0us; - let _h = |&mut:| { set(&mut z); |&:| z = 42; }; //~ ERROR cannot assign + let _h = to_fn_mut(|| { set(&mut z); to_fn(|| z = 42); }); //~ ERROR cannot assign } + // By-value captures { let mut x = 0us; - let _f = move |&:| x = 42; //~ ERROR cannot assign + let _f = to_fn(move || x = 42); //~ ERROR cannot assign let mut y = 0us; - let _g = move |&:| set(&mut y); //~ ERROR cannot borrow + let _g = to_fn(move || set(&mut y)); //~ ERROR cannot borrow let mut z = 0us; - let _h = move |&mut:| { set(&mut z); move |&:| z = 42; }; //~ ERROR cannot assign + let _h = to_fn_mut(move || { set(&mut z); to_fn(move || z = 42); }); //~ ERROR cannot assign } } diff --git a/src/test/compile-fail/borrowck-move-by-capture.rs b/src/test/compile-fail/borrowck-move-by-capture.rs index b0d546cd5c80..a1708e7f4972 100644 --- a/src/test/compile-fail/borrowck-move-by-capture.rs +++ b/src/test/compile-fail/borrowck-move-by-capture.rs @@ -8,11 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(box_syntax)] +#![feature(box_syntax,unboxed_closures)] + +fn to_fn_mut>(f: F) -> F { f } +fn to_fn_once>(f: F) -> F { f } pub fn main() { let bar = box 3; - let _g = |&mut:| { - let _h = move |:| -> isize { *bar }; //~ ERROR cannot move out of captured outer variable - }; + let _g = to_fn_mut(|| { + let _h = to_fn_once(move || -> isize { *bar }); //~ ERROR cannot move out of + }); } diff --git a/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs b/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs index 2951c63828d5..738755855c07 100644 --- a/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs +++ b/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs @@ -8,12 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(unboxed_closures)] + +fn to_fn_once>(f: F) -> F { f } + fn main() { let x = 1; - move|:| { x = 2; }; + to_fn_once(move|:| { x = 2; }); //~^ ERROR: cannot assign to immutable captured outer variable let s = std::old_io::stdin(); - move|:| { s.read_to_end(); }; + to_fn_once(move|:| { s.read_to_end(); }); //~^ ERROR: cannot borrow immutable captured outer variable } diff --git a/src/test/compile-fail/issue-11925.rs b/src/test/compile-fail/issue-11925.rs index 69f7b46009c3..df4dab2552e7 100644 --- a/src/test/compile-fail/issue-11925.rs +++ b/src/test/compile-fail/issue-11925.rs @@ -8,12 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(box_syntax)] +#![feature(box_syntax, unboxed_closures)] + +fn to_fn_once>(f: F) -> F { f } fn main() { let r = { let x = box 42; - let f = move|:| &x; //~ ERROR: `x` does not live long enough + let f = to_fn_once(move|| &x); //~ ERROR: `x` does not live long enough f() }; diff --git a/src/test/compile-fail/issue-12127.rs b/src/test/compile-fail/issue-12127.rs index c06082de3cd0..40d446b91a5a 100644 --- a/src/test/compile-fail/issue-12127.rs +++ b/src/test/compile-fail/issue-12127.rs @@ -8,14 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(box_syntax)] +#![feature(box_syntax, unboxed_closures)] +fn to_fn_once>(f: F) -> F { f } fn do_it(x: &isize) { } fn main() { let x = box 22; - let f = move|:| do_it(&*x); - (move|:| { + let f = to_fn_once(move|| do_it(&*x)); + to_fn_once(move|| { f(); f(); //~^ ERROR: use of moved value: `f` diff --git a/src/test/compile-fail/lint-stability.rs b/src/test/compile-fail/lint-stability.rs index 5b093a8556cd..4cf75bf15de2 100644 --- a/src/test/compile-fail/lint-stability.rs +++ b/src/test/compile-fail/lint-stability.rs @@ -20,7 +20,7 @@ #![staged_api] #[macro_use] -extern crate lint_stability; //~ ERROR: use of unmarked library feature +extern crate lint_stability; mod cross_crate { extern crate stability_cfg1; @@ -61,10 +61,6 @@ mod cross_crate { foo.method_unstable_text(); //~ WARNING use of unstable library feature 'test_feature': text foo.trait_unstable_text(); //~ WARNING use of unstable library feature 'test_feature': text - unmarked(); //~ ERROR use of unmarked library feature - foo.method_unmarked(); //~ ERROR use of unmarked library feature - foo.trait_unmarked(); //~ ERROR use of unmarked library feature - stable(); foo.method_stable(); foo.trait_stable(); @@ -77,28 +73,24 @@ mod cross_crate { let _ = DeprecatedUnstableStruct { i: 0 }; //~ ERROR use of deprecated item //~^ WARNING use of unstable library feature let _ = UnstableStruct { i: 0 }; //~ WARNING use of unstable library feature - let _ = UnmarkedStruct { i: 0 }; //~ ERROR use of unmarked library feature let _ = StableStruct { i: 0 }; let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item let _ = DeprecatedUnstableUnitStruct; //~ ERROR use of deprecated item //~^ WARNING use of unstable library feature let _ = UnstableUnitStruct; //~ WARNING use of unstable library feature - let _ = UnmarkedUnitStruct; //~ ERROR use of unmarked library feature let _ = StableUnitStruct; let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item let _ = Enum::DeprecatedUnstableVariant; //~ ERROR use of deprecated item //~^ WARNING use of unstable library feature let _ = Enum::UnstableVariant; //~ WARNING use of unstable library feature - let _ = Enum::UnmarkedVariant; //~ ERROR use of unmarked library feature let _ = Enum::StableVariant; let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item let _ = DeprecatedUnstableTupleStruct (1); //~ ERROR use of deprecated item //~^ WARNING use of unstable library feature let _ = UnstableTupleStruct (1); //~ WARNING use of unstable library feature - let _ = UnmarkedTupleStruct (1); //~ ERROR use of unmarked library feature let _ = StableTupleStruct (1); // At the moment, the lint checker only checks stability in @@ -123,7 +115,6 @@ mod cross_crate { //~^ WARNING use of unstable library feature foo.trait_unstable(); //~ WARNING use of unstable library feature foo.trait_unstable_text(); //~ WARNING use of unstable library feature 'test_feature': text - foo.trait_unmarked(); //~ ERROR use of unmarked library feature foo.trait_stable(); } @@ -136,7 +127,6 @@ mod cross_crate { //~^ WARNING use of unstable library feature foo.trait_unstable(); //~ WARNING use of unstable library feature foo.trait_unstable_text(); //~ WARNING use of unstable library feature 'test_feature': text - foo.trait_unmarked(); //~ ERROR use of unmarked library feature foo.trait_stable(); } @@ -183,8 +173,6 @@ mod this_crate { #[unstable(feature = "test_feature", reason = "text")] pub fn unstable_text() {} - pub fn unmarked() {} - #[stable(feature = "rust1", since = "1.0.0")] pub fn stable() {} #[stable(feature = "rust1", since = "1.0.0", reason = "text")] @@ -206,8 +194,6 @@ mod this_crate { #[unstable(feature = "test_feature", reason = "text")] pub fn method_unstable_text(&self) {} - pub fn method_unmarked(&self) {} - #[stable(feature = "rust1", since = "1.0.0")] pub fn method_stable(&self) {} #[stable(feature = "rust1", since = "1.0.0", reason = "text")] @@ -227,8 +213,6 @@ mod this_crate { #[unstable(feature = "test_feature", reason = "text")] fn trait_unstable_text(&self) {} - fn trait_unmarked(&self) {} - #[stable(feature = "rust1", since = "1.0.0")] fn trait_stable(&self) {} #[stable(feature = "rust1", since = "1.0.0", reason = "text")] @@ -242,7 +226,6 @@ mod this_crate { pub struct DeprecatedStruct { i: isize } #[unstable(feature = "test_feature")] pub struct UnstableStruct { i: isize } - pub struct UnmarkedStruct { i: isize } #[stable(feature = "rust1", since = "1.0.0")] pub struct StableStruct { i: isize } @@ -251,7 +234,6 @@ mod this_crate { pub struct DeprecatedUnitStruct; #[unstable(feature = "test_feature")] pub struct UnstableUnitStruct; - pub struct UnmarkedUnitStruct; #[stable(feature = "rust1", since = "1.0.0")] pub struct StableUnitStruct; @@ -262,7 +244,6 @@ mod this_crate { #[unstable(feature = "test_feature")] UnstableVariant, - UnmarkedVariant, #[stable(feature = "rust1", since = "1.0.0")] StableVariant, } @@ -272,7 +253,6 @@ mod this_crate { pub struct DeprecatedTupleStruct(isize); #[unstable(feature = "test_feature")] pub struct UnstableTupleStruct(isize); - pub struct UnmarkedTupleStruct(isize); #[stable(feature = "rust1", since = "1.0.0")] pub struct StableTupleStruct(isize); @@ -299,10 +279,6 @@ mod this_crate { foo.method_unstable_text(); foo.trait_unstable_text(); - unmarked(); - foo.method_unmarked(); - foo.trait_unmarked(); - stable(); foo.method_stable(); foo.trait_stable(); @@ -313,22 +289,18 @@ mod this_crate { let _ = DeprecatedStruct { i: 0 }; //~ ERROR use of deprecated item let _ = UnstableStruct { i: 0 }; - let _ = UnmarkedStruct { i: 0 }; let _ = StableStruct { i: 0 }; let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item let _ = UnstableUnitStruct; - let _ = UnmarkedUnitStruct; let _ = StableUnitStruct; let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item let _ = Enum::UnstableVariant; - let _ = Enum::UnmarkedVariant; let _ = Enum::StableVariant; let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item let _ = UnstableTupleStruct (1); - let _ = UnmarkedTupleStruct (1); let _ = StableTupleStruct (1); } @@ -337,7 +309,6 @@ mod this_crate { foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text foo.trait_unstable(); foo.trait_unstable_text(); - foo.trait_unmarked(); foo.trait_stable(); } @@ -346,7 +317,6 @@ mod this_crate { foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text foo.trait_unstable(); foo.trait_unstable_text(); - foo.trait_unmarked(); foo.trait_stable(); } diff --git a/src/test/compile-fail/missing-stability.rs b/src/test/compile-fail/missing-stability.rs new file mode 100644 index 000000000000..14dd983161b4 --- /dev/null +++ b/src/test/compile-fail/missing-stability.rs @@ -0,0 +1,33 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Checks that exported items without stability attributes cause an error + +#![crate_type="lib"] +#![feature(staged_api)] +#![staged_api] + +pub fn unmarked() { + //~^ ERROR This node does not have a stability attribute + () +} + +#[unstable(feature = "foo")] +pub mod foo { + // #[unstable] is inherited + pub fn unmarked() {} +} + +#[stable(feature = "bar", since="1.0.0")] +pub mod bar { + // #[stable] is not inherited + pub fn unmarked() {} + //~^ ERROR This node does not have a stability attribute +} \ No newline at end of file diff --git a/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs b/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs index 5dfe7f0c71f1..4251be36ab43 100644 --- a/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs +++ b/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs @@ -8,13 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(box_syntax)] +#![feature(box_syntax, unboxed_closures)] use std::usize; +fn to_fn>(f: F) -> F { f } + fn test(_x: Box) {} fn main() { let i = box 3; - let _f = |&:| test(i); //~ ERROR cannot move out + let _f = to_fn(|| test(i)); //~ ERROR cannot move out } diff --git a/src/test/compile-fail/range-3.rs b/src/test/compile-fail/range-3.rs index fe79165236f8..78c575d33bad 100644 --- a/src/test/compile-fail/range-3.rs +++ b/src/test/compile-fail/range-3.rs @@ -13,4 +13,4 @@ pub fn main() { let r = 1..2..3; //~^ ERROR expected one of `.`, `;`, or an operator, found `..` -} \ No newline at end of file +} diff --git a/src/test/compile-fail/range-4.rs b/src/test/compile-fail/range-4.rs index bbd6ae289cce..a3e27fbbe9aa 100644 --- a/src/test/compile-fail/range-4.rs +++ b/src/test/compile-fail/range-4.rs @@ -13,4 +13,4 @@ pub fn main() { let r = ..1..2; //~^ ERROR expected one of `.`, `;`, or an operator, found `..` -} \ No newline at end of file +} diff --git a/src/test/compile-fail/unboxed-closer-non-implicit-copyable.rs b/src/test/compile-fail/unboxed-closer-non-implicit-copyable.rs index 182c632d0626..2d5597949198 100644 --- a/src/test/compile-fail/unboxed-closer-non-implicit-copyable.rs +++ b/src/test/compile-fail/unboxed-closer-non-implicit-copyable.rs @@ -10,8 +10,10 @@ #![feature(unboxed_closures)] +fn to_fn_once>(f: F) -> F { f } + fn main() { - let f = move|:| (); + let f = to_fn_once(move|| ()); f(); f(); //~ ERROR use of moved value } diff --git a/src/test/compile-fail/unboxed-closure-illegal-move.rs b/src/test/compile-fail/unboxed-closure-illegal-move.rs index 1312b42fb82d..224cbc2bef32 100644 --- a/src/test/compile-fail/unboxed-closure-illegal-move.rs +++ b/src/test/compile-fail/unboxed-closure-illegal-move.rs @@ -15,31 +15,35 @@ // if the upvar is captured by ref or the closure takes self by // reference. +fn to_fn>(f: F) -> F { f } +fn to_fn_mut>(f: F) -> F { f } +fn to_fn_once>(f: F) -> F { f } + fn main() { // By-ref cases { let x = box 0us; - let f = |&:| drop(x); //~ ERROR cannot move + let f = to_fn(|| drop(x)); //~ ERROR cannot move } { let x = box 0us; - let f = |&mut:| drop(x); //~ ERROR cannot move + let f = to_fn_mut(|| drop(x)); //~ ERROR cannot move } { let x = box 0us; - let f = |:| drop(x); // OK -- FnOnce + let f = to_fn_once(|| drop(x)); // OK -- FnOnce } // By-value cases { let x = box 0us; - let f = move |&:| drop(x); //~ ERROR cannot move + let f = to_fn(move || drop(x)); //~ ERROR cannot move } { let x = box 0us; - let f = move |&mut:| drop(x); //~ ERROR cannot move + let f = to_fn_mut(move || drop(x)); //~ ERROR cannot move } { let x = box 0us; - let f = move |:| drop(x); // this one is ok + let f = to_fn_once(move || drop(x)); // this one is ok } } diff --git a/src/test/compile-fail/unboxed-closures-mutate-upvar.rs b/src/test/compile-fail/unboxed-closures-mutate-upvar.rs index 96c7948dcb04..650bb17bb775 100644 --- a/src/test/compile-fail/unboxed-closures-mutate-upvar.rs +++ b/src/test/compile-fail/unboxed-closures-mutate-upvar.rs @@ -12,51 +12,56 @@ // as `mut` through a closure. Also test that we CAN mutate a moved copy, // unless this is a `Fn` closure. Issue #16749. +#![feature(unboxed_closures)] + use std::mem; +fn to_fn>(f: F) -> F { f } +fn to_fn_mut>(f: F) -> F { f } + fn a() { let n = 0u8; - let mut f = |&mut:| { //~ ERROR closure cannot assign + let mut f = to_fn_mut(|| { //~ ERROR closure cannot assign n += 1; - }; + }); } fn b() { let mut n = 0u8; - let mut f = |&mut:| { + let mut f = to_fn_mut(|| { n += 1; // OK - }; + }); } fn c() { let n = 0u8; - let mut f = move |&mut:| { + let mut f = to_fn_mut(move || { // If we just did a straight-forward desugaring, this would // compile, but we do something a bit more subtle, and hence // we get an error. n += 1; //~ ERROR cannot assign - }; + }); } fn d() { let mut n = 0u8; - let mut f = move |&mut:| { + let mut f = to_fn_mut(move || { n += 1; // OK - }; + }); } fn e() { let n = 0u8; - let mut f = move |&:| { + let mut f = to_fn(move || { n += 1; //~ ERROR cannot assign - }; + }); } fn f() { let mut n = 0u8; - let mut f = move |&:| { + let mut f = to_fn(move || { n += 1; //~ ERROR cannot assign - }; + }); } fn main() { } diff --git a/src/test/compile-fail/unboxed-closures-static-call-wrong-trait.rs b/src/test/compile-fail/unboxed-closures-static-call-wrong-trait.rs index 8d3721f28db5..f430e9fc7590 100644 --- a/src/test/compile-fail/unboxed-closures-static-call-wrong-trait.rs +++ b/src/test/compile-fail/unboxed-closures-static-call-wrong-trait.rs @@ -10,8 +10,10 @@ #![feature(unboxed_closures)] +fn to_fn_mut>(f: F) -> F { f } + fn main() { - let mut_ = |&mut: x| x; + let mut_ = to_fn_mut(|x| x); mut_.call((0, )); //~ ERROR does not implement any method in scope named `call` } diff --git a/src/test/compile-fail/unboxed-closures-vtable-mismatch.rs b/src/test/compile-fail/unboxed-closures-vtable-mismatch.rs index 305dd33e5a05..c2a2e5162ace 100644 --- a/src/test/compile-fail/unboxed-closures-vtable-mismatch.rs +++ b/src/test/compile-fail/unboxed-closures-vtable-mismatch.rs @@ -12,12 +12,14 @@ use std::ops::FnMut; +fn to_fn_mut>(f: F) -> F { f } + fn call_itisize>(y: isize, mut f: F) -> isize { f(2, y) } pub fn main() { - let f = |&mut: x: usize, y: isize| -> isize { (x as isize) + y }; + let f = to_fn_mut(|x: usize, y: isize| -> isize { (x as isize) + y }); let z = call_it(3, f); //~^ ERROR type mismatch //~| ERROR type mismatch diff --git a/src/test/compile-fail/unboxed-closures-wrong-trait.rs b/src/test/compile-fail/unboxed-closures-wrong-trait.rs deleted file mode 100644 index 2ada0dd22e75..000000000000 --- a/src/test/compile-fail/unboxed-closures-wrong-trait.rs +++ /dev/null @@ -1,23 +0,0 @@ -// 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(lang_items, overloaded_calls, unboxed_closures)] - -fn c isize>(f: F) -> isize { - f(5, 6) -} - -fn main() { - let z: isize = 7; - assert_eq!(c(|&mut: x: isize, y| x + y + z), 10); - //~^ ERROR not implemented - //~| ERROR not implemented -} - diff --git a/src/test/debuginfo/associated-types.rs b/src/test/debuginfo/associated-types.rs index 6a624e39e326..0f6f0ac6ae75 100644 --- a/src/test/debuginfo/associated-types.rs +++ b/src/test/debuginfo/associated-types.rs @@ -149,4 +149,4 @@ fn main() { assoc_enum(Enum::Variant2(8i64, 9i32)); } -fn zzz() { () } \ No newline at end of file +fn zzz() { () } diff --git a/src/test/run-pass/closure-inference.rs b/src/test/run-pass/closure-inference.rs index 893003dd9972..3bd0273216de 100644 --- a/src/test/run-pass/closure-inference.rs +++ b/src/test/run-pass/closure-inference.rs @@ -9,11 +9,11 @@ // except according to those terms. -fn foo(i: int) -> int { i + 1 } +fn foo(i: isize) -> isize { i + 1 } fn apply(f: F, v: A) -> A where F: FnOnce(A) -> A { f(v) } pub fn main() { - let f = {|: i| foo(i)}; + let f = {|i| foo(i)}; assert_eq!(apply(f, 2), 3); } diff --git a/src/test/run-pass/issue-15149.rs b/src/test/run-pass/issue-15149.rs index 1d18f33fd18e..b37c71bc3262 100644 --- a/src/test/run-pass/issue-15149.rs +++ b/src/test/run-pass/issue-15149.rs @@ -11,7 +11,7 @@ use std::slice::SliceExt; use std::old_io::{Command, fs, USER_RWX}; use std::os; -use std::path::BytesContainer; +use std::old_path::BytesContainer; use std::rand::random; fn main() { diff --git a/src/test/run-pass/issue-3424.rs b/src/test/run-pass/issue-3424.rs index 6647fbe2238e..0d85f61e5135 100644 --- a/src/test/run-pass/issue-3424.rs +++ b/src/test/run-pass/issue-3424.rs @@ -1,4 +1,3 @@ - // 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. @@ -15,8 +14,8 @@ #![feature(box_syntax)] #![feature(unboxed_closures)] -use std::path::{Path}; -use std::path; +use std::old_path::{Path}; +use std::old_path; use std::result; use std::thunk::Thunk; @@ -28,7 +27,7 @@ fn tester() result::Result::Ok("more blah".to_string()) }; - let path = path::Path::new("blah"); + let path = old_path::Path::new("blah"); assert!(loader(&path).is_ok()); } diff --git a/src/test/run-pass/last-use-in-cap-clause.rs b/src/test/run-pass/last-use-in-cap-clause.rs index 0cdd4d3889c0..0cd8c13a4e10 100644 --- a/src/test/run-pass/last-use-in-cap-clause.rs +++ b/src/test/run-pass/last-use-in-cap-clause.rs @@ -14,9 +14,9 @@ #![feature(box_syntax)] #![feature(unboxed_closures)] -struct A { a: Box } +struct A { a: Box } -fn foo() -> Box int + 'static> { +fn foo() -> Box isize + 'static> { let k = box 22; let _u = A {a: k.clone()}; let result = |&mut:| 22; 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 5dcaa885e380..c6fd55272610 100644 --- a/src/test/run-pass/process-spawn-with-unicode-params.rs +++ b/src/test/run-pass/process-spawn-with-unicode-params.rs @@ -20,7 +20,7 @@ use std::old_io; use std::old_io::fs; use std::old_io::Command; use std::os; -use std::path::Path; +use std::old_path::Path; fn main() { let my_args = os::args(); diff --git a/src/test/run-pass/unboxed-closures-zero-args.rs b/src/test/run-pass/unboxed-closures-zero-args.rs index f2eddd84af83..8e3d44df798a 100644 --- a/src/test/run-pass/unboxed-closures-zero-args.rs +++ b/src/test/run-pass/unboxed-closures-zero-args.rs @@ -11,7 +11,7 @@ #![feature(unboxed_closures)] fn main() { - let mut zero = |&mut:| {}; - let () = zero.call_mut(()); + let mut zero = || {}; + let () = zero(); }