diff --git a/src/doc/reference.md b/src/doc/reference.md index 64ddb3ffdd39f..326946837bf66 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 504b58d8ad105..340a8d59612f2 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 c74c870eef5d5..abcf358a1926c 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/fmt/mod.rs b/src/libcore/fmt/mod.rs index 551277bae5c6b..60262857765e2 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 d0734f9c0395f..23157072d536b 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/librustc/middle/check_loop.rs b/src/librustc/middle/check_loop.rs index 41ef55933cda2..ea584407944ab 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 e40e04bdee86a..c0fabb2a3481d 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 1ae483be2696d..156ff43e2bab3 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 5028a1322cac1..3304bd4ae2952 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 3ede6bbb965ef..c2a451b405bb5 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 000572cdd40a3..b8af91add9efb 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_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index e5271cfde5a44..b9d2b9ec263ab 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 9b9cc14c47615..8ede037594a00 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 20bf77190be72..7dc0d9be53924 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 d87039cbaefc1..dd739059ed0dd 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 7758039e40a41..b0ce9641cf440 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 9e561fc883bb0..6901eb25b31fe 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 0ced4066f625b..c6b70e1a1abc3 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, @@ -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 689d0c231b5b7..44eb5b190e1a6 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 808dbd4b31910..b2a676e878e63 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 07b67d543a87e..adf15fbf28a8f 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 d5baff3a0c4a3..9df0403794d7c 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 b52e01f9a7a76..f452c8488ce1c 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 52b1eb490cc25..f047a36c56095 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/libstd/io/buffered.rs b/src/libstd/io/buffered.rs new file mode 100644 index 0000000000000..2fd6631ecc437 --- /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 0000000000000..9f3655de20fc2 --- /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 0000000000000..9f3cd8c8b15de --- /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 0000000000000..7f3ce7924c1ca --- /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 0000000000000..0832206a48b60 --- /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 0000000000000..475ada2ff84b8 --- /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 0000000000000..3d342137c62dd --- /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 bd4763d7bd475..a46cea7a44325 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,6 +247,7 @@ 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; diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 3fd45ea6a7bc0..3f4f1ec4c0db5 100755 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -156,7 +156,7 @@ mod platform { None } - #[derive(Copy, Clone, Show, Hash, PartialEq, Eq)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct Prefix<'a>; impl<'a> Prefix<'a> { @@ -177,9 +177,10 @@ mod platform { mod platform { use core::prelude::*; - use super::{Path, os_str_as_u8_slice, u8_slice_as_os_str}; - use ffi::OsStr; + 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 { @@ -299,7 +300,7 @@ mod platform { pub fn len(&self) -> usize { use self::Prefix::*; fn os_str_len(s: &OsStr) -> usize { - unsafe { os_str_as_u8_slice(s).len() } + os_str_as_u8_slice(s).len() } match *self { Verbatim(x) => 4 + os_str_len(x), @@ -339,12 +340,12 @@ mod platform { } } - impl<'a> ops::PartialEq for Prefix<'a> { + 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), Verbatim(y1, y2)) => x1 == y1 && x2 == y2, + (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, @@ -457,7 +458,7 @@ fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { /// 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, Show)] +#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] enum State { Prefix = 0, // c: Root = 1, // / @@ -470,7 +471,7 @@ enum State { /// /// 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, Show)] +#[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), @@ -2434,12 +2435,21 @@ mod tests { tfn!("foo", "bar", "bar"); tfn!("foo", "", ""); tfn!("", "foo", "foo"); - tfn!(".", "foo", "./foo"); - tfn!("foo/", "bar", "foo/bar"); - tfn!("foo/.", "bar", "foo/./bar"); - tfn!("..", "foo", "../foo"); - tfn!("foo/..", "bar", "foo/../bar"); - 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] diff --git a/src/libstd/sys/unix/c.rs b/src/libstd/sys/unix/c.rs index 89bd9a2340652..50a8e6b73e386 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 b03b9046966a0..427cf21ac70a9 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 b3f3796294580..5004ff713c45f 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/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 8dd467eba9e2a..f1af70e2cf7d1 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/thread_local/mod.rs b/src/libstd/thread_local/mod.rs index d4d777789dd5d..9de5fd1c770ec 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 d7283db25a5f2..34eeedeaa7650 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 53787d71eef80..a85b87f47d6ee 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 2b3a72126831e..53c35ef34cd0d 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 9fd5091e194dc..739c73a70b02b 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 6eacb34401894..77440914342fb 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 4e76359e93040..d7a51e1149f30 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 9012ec2114d07..07b6af651f610 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 a3600506057af..60de6c909b78b 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 c3182602a4b89..2cb265033c399 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) - } - - if self.token == token::BinOp(token::And) && - self.look_ahead(1, |t| *t == token::Colon) { + } else 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; + } else if + self.eat(&token::Colon) + { + /* nothing */ + } else { + return; } - 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/print/pprust.rs b/src/libsyntax/print/pprust.rs index e6d895a49fcd7..ee8e207fa6c05 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 bd84306fe17e0..fbcfcaadf12b7 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 3679557d06bb7..7ac3925fb2476 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 12555c550729c..7033f5caef6c6 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 b0d546cd5c803..a1708e7f49728 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 2951c63828d5c..738755855c070 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 69f7b46009c38..df4dab2552e7d 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 c06082de3cd08..40d446b91a5a8 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 5b093a8556cdd..4cf75bf15de27 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 0000000000000..14dd983161b4a --- /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 5dfe7f0c71f14..4251be36ab438 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/unboxed-closer-non-implicit-copyable.rs b/src/test/compile-fail/unboxed-closer-non-implicit-copyable.rs index 182c632d06261..2d55979491981 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 1312b42fb82d4..224cbc2bef324 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 96c7948dcb046..650bb17bb7758 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 8d3721f28db50..f430e9fc75902 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 305dd33e5a05a..c2a2e5162ace0 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 2ada0dd22e75f..0000000000000 --- 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/run-pass/closure-inference.rs b/src/test/run-pass/closure-inference.rs index 893003dd99722..3bd0273216de3 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/last-use-in-cap-clause.rs b/src/test/run-pass/last-use-in-cap-clause.rs index 0cdd4d3889c06..0cd8c13a4e10a 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/unboxed-closures-zero-args.rs b/src/test/run-pass/unboxed-closures-zero-args.rs index f2eddd84af832..8e3d44df798a7 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(); }