From b2297fd710b9e0f860f8991b1f641d827f32e5da Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 1 Feb 2015 18:33:13 -0800 Subject: [PATCH 01/22] std: Add some missing stability attributes * Display::fmt is stable * Debug::fmt is stable * FromIterator::from_iter is stable * Peekable::peek is stable --- src/libcore/fmt/mod.rs | 2 ++ src/libcore/iter.rs | 2 ++ 2 files changed, 4 insertions(+) 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..2264bd74968cc 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -119,6 +119,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; } @@ -1821,6 +1822,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(); From 3449751ff7fa21aedd1352cdbe9f7d77e2bc0d91 Mon Sep 17 00:00:00 2001 From: Valerii Hiora Date: Tue, 3 Feb 2015 15:30:10 +0200 Subject: [PATCH 02/22] iOS: fixed build --- src/libstd/sys/unix/c.rs | 1 + src/libstd/sys/unix/os.rs | 30 ++++++++++++++++-------------- 2 files changed, 17 insertions(+), 14 deletions(-) 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/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, From 88449a8f79d83c68c8e3a695653df6a4c63a84f4 Mon Sep 17 00:00:00 2001 From: Alexis Date: Tue, 3 Feb 2015 00:47:38 -0500 Subject: [PATCH 03/22] add naivest entry API to VecMap --- src/libcollections/vec_map.rs | 194 +++++++++++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 1 deletion(-) diff --git a/src/libcollections/vec_map.rs b/src/libcollections/vec_map.rs index f2a9bb4392ccc..70af10fa567cd 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")] @@ -787,7 +928,7 @@ mod test_map { use prelude::*; use core::hash::{hash, SipHasher}; - use super::VecMap; + use super::{VecMap, Occupied, Vacant}; #[test] fn test_get_mut() { @@ -1139,6 +1280,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)] From 9a17f62947d0c079f1c877de4fe1dab5b2c500d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Tue, 3 Feb 2015 16:54:06 +0100 Subject: [PATCH 04/22] Optimize rposition The extra check caused by the expect() call can, in general, not be optimized away, because the length of the iterator is unknown at compile time, causing a noticable slow-down. Since the check only triggers if the element isn't actually found in the iterator, i.e. it isn't guaranteed to trigger for ill-behaved ExactSizeIterators, it seems reasonable to switch to an implementation that doesn't need the check and just always returns None if the value isn't found. Benchmark: ````rust let v: Vec = (0..1024*65).map(|_| 0).collect(); b.iter(|| { v.as_slice().iter().rposition(|&c| c == 1) }); ```` Before: ```` test rposition ... bench: 49939 ns/iter (+/- 23) ```` After: ```` test rposition ... bench: 33306 ns/iter (+/- 68) ```` --- src/libcore/iter.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index d0734f9c0395f..417bcab5140af 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -723,11 +723,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 } From c9e1c445dbcbfc9c938488a79ef12595e0f99c8d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 2 Feb 2015 11:52:08 -0500 Subject: [PATCH 05/22] Allow closure arguments types to unify even if we can't fully resolve a trait obligation. Partial fix for #16440 -- closure return types are not handled yet. --- src/librustc/middle/traits/select.rs | 83 +++++++++++++++++++------- src/test/run-pass/closure-inference.rs | 2 +- 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 000572cdd40a3..7a59909e13166 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,69 @@ 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 +1060,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 +2360,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/test/run-pass/closure-inference.rs b/src/test/run-pass/closure-inference.rs index 893003dd99722..51996ddfbe800 100644 --- a/src/test/run-pass/closure-inference.rs +++ b/src/test/run-pass/closure-inference.rs @@ -14,6 +14,6 @@ fn foo(i: int) -> int { 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); } From 498595a3dc90b7df08e90b04278b4de33c5ab3cf Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 Feb 2015 06:12:43 -0500 Subject: [PATCH 06/22] Teach project to unify the return type even if a precise match is not possible. There is some amount of duplication as a result (similar to select) -- I am not happy about this but not sure how to fix it without deeper rewrites. --- src/librustc/middle/traits/project.rs | 75 +++++++++++++------ src/librustc/middle/traits/select.rs | 3 +- src/test/run-pass/closure-inference.rs | 2 +- src/test/run-pass/last-use-in-cap-clause.rs | 4 +- .../run-pass/unboxed-closures-zero-args.rs | 4 +- 5 files changed, 60 insertions(+), 28 deletions(-) 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 7a59909e13166..b8af91add9efb 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -277,8 +277,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// 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>) - { + 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(_) => { } diff --git a/src/test/run-pass/closure-inference.rs b/src/test/run-pass/closure-inference.rs index 51996ddfbe800..3bd0273216de3 100644 --- a/src/test/run-pass/closure-inference.rs +++ b/src/test/run-pass/closure-inference.rs @@ -9,7 +9,7 @@ // 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) } 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(); } From 47f18659ff629792f49b5ed87e870687703831fe Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 Feb 2015 11:32:26 -0500 Subject: [PATCH 07/22] Update compile-fail tests to use the expected type to force the closure kind, thereby detecting what happens if there are mismatches. Simply removing the `:` annotations caused most of these tests to pass or produce other errors, because the inference would convert the closure into a more appropriate kind. (The ability to override the inference by using the expected type is an important backdoor partly for this reason.) --- .../borrow-immutable-upvar-mutation.rs | 18 +++++++----- .../compile-fail/borrowck-move-by-capture.rs | 11 ++++--- .../cannot-mutate-captured-non-mut-var.rs | 8 +++-- src/test/compile-fail/issue-11925.rs | 6 ++-- src/test/compile-fail/issue-12127.rs | 7 +++-- ...type-move-out-of-closure-env-issue-1965.rs | 6 ++-- .../unboxed-closer-non-implicit-copyable.rs | 4 ++- .../unboxed-closure-illegal-move.rs | 16 ++++++---- .../unboxed-closures-mutate-upvar.rs | 29 +++++++++++-------- ...nboxed-closures-static-call-wrong-trait.rs | 4 ++- .../unboxed-closures-vtable-mismatch.rs | 4 ++- .../unboxed-closures-wrong-trait.rs | 23 --------------- 12 files changed, 72 insertions(+), 64 deletions(-) delete mode 100644 src/test/compile-fail/unboxed-closures-wrong-trait.rs 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/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 -} - From 04311341197ddabc420a8cffc0d26c12228d445b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 Feb 2015 11:34:05 -0500 Subject: [PATCH 08/22] Remove the explicit closure kind syntax from the parser and AST; upgrade the inference based on expected type so that it is able to infer the fn kind in isolation even if the full signature is not available (and we could perhaps do better still in some cases, such as extracting just the types of the arguments but not the return value). --- src/librustc/middle/check_loop.rs | 2 +- src/librustc/middle/liveness.rs | 2 +- src/librustc/middle/mem_categorization.rs | 2 +- src/librustc_borrowck/borrowck/mod.rs | 2 +- src/librustc_resolve/lib.rs | 2 +- src/librustc_trans/save/mod.rs | 2 +- src/librustc_trans/trans/base.rs | 2 +- src/librustc_trans/trans/debuginfo.rs | 4 +- src/librustc_trans/trans/expr.rs | 2 +- src/librustc_typeck/check/closure.rs | 151 +++++++++++++--------- src/librustc_typeck/check/mod.rs | 4 +- src/librustc_typeck/check/regionck.rs | 2 +- src/librustc_typeck/check/upvar.rs | 2 +- src/librustc_typeck/check/writeback.rs | 2 +- src/libsyntax/ast.rs | 10 +- src/libsyntax/ast_map/blocks.rs | 2 +- src/libsyntax/ext/build.rs | 4 +- src/libsyntax/ext/expand.rs | 3 +- src/libsyntax/fold.rs | 3 +- src/libsyntax/parse/obsolete.rs | 5 + src/libsyntax/parse/parser.rs | 62 ++++----- src/libsyntax/print/pprust.rs | 19 +-- src/libsyntax/visit.rs | 2 +- 23 files changed, 155 insertions(+), 136 deletions(-) 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_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_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 66bb299273d9f..172e105896c30 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 bed43a5c83882..df9e722569701 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -1094,7 +1094,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/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/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/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..385c0a48f870f 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 a45e117 + // 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, From 68ad6949d4c3e2160098c94007b9c48abc94aaad Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 Feb 2015 13:14:29 -0500 Subject: [PATCH 09/22] Correct one case where the inference was detecting a looser result than the explicit annotation, leading to "extra `mut` declaration" lint errors. --- src/libsyntax/ext/deriving/rand.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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())) From 8ddcb06b1d021560bfe641c0dbc452a04e80388e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 Feb 2015 13:14:36 -0500 Subject: [PATCH 10/22] Update for new snapshot after rebasing. --- src/libsyntax/parse/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 385c0a48f870f..2cb265033c399 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1163,7 +1163,7 @@ impl<'a> Parser<'a> { return; } - // SNAP a45e117 + // SNAP 474b324 // Enable these obsolete errors after snapshot: // let span = mk_sp(lo, self.span.hi); // self.obsolete(span, ObsoleteSyntax::ClosureKind); From 0b9e227a1697e22b21d947f36bf1bd7695971d8f Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 3 Feb 2015 21:16:08 +0530 Subject: [PATCH 11/22] Move stability pass after privacy pass --- src/librustc/middle/stability.rs | 30 ++++++++++++++++-------------- src/librustc_driver/driver.rs | 9 +++++---- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 5028a1322cac1..88719a9bbdd29 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -44,7 +44,7 @@ pub struct Index { // A private tree-walker for producing an Index. struct Annotator<'a> { sess: &'a Session, - index: Index, + index: &'a mut Index, parent: Option } @@ -146,7 +146,20 @@ impl<'a, 'v> Visitor<'v> for Annotator<'a> { 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) { + if !self.staged_api { + return; + } + let mut annotator = Annotator { + sess: sess, + index: self, + parent: None + }; + annotator.annotate(ast::CRATE_NODE_ID, true, &krate.attrs, krate.span, + |v| visit::walk_crate(v, krate)); + } + + pub fn new(krate: &Crate) -> Index { let mut staged_api = false; for attr in &krate.attrs { if attr.name().get() == "staged_api" { @@ -159,22 +172,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 } } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 9b9cc14c47615..166a1d6f809b3 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)); + time(time_passes, "intrinsic checking", (), |_| middle::intrinsicck::check_crate(&ty_cx)); From d30f225b492163b14005d5069b7924f3fecf868c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 3 Feb 2015 12:32:56 -0800 Subject: [PATCH 12/22] std: Remove `iter::ByRef` and generalize impls This removes the `ByRef` iterator adaptor to stay in line with the changes to `std::io`. The `by_ref` method instead just returns `&mut Self`. This also removes the implementation of `Iterator for &mut Iterator` and instead generalizes it to `Iterator for &mut I` where `I: Iterator + ?Sized`. The `Box` implementations were also updated. This is a breaking change due to the removal of the `std::iter::ByRef` type. All mentions of `ByRef<'a, T>` should be replaced with `&mut T` to migrate forward. [breaking-change] --- src/liballoc/boxed.rs | 32 +++++++++++------------ src/libcore/iter.rs | 61 ++++++++++++++----------------------------- 2 files changed, 34 insertions(+), 59 deletions(-) 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/libcore/iter.rs b/src/libcore/iter.rs index d0734f9c0395f..f676bb891b632 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` @@ -548,9 +543,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`. @@ -1017,15 +1010,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 +1065,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 +1120,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")] From 5cf9905e257ddeeadaf493a705a230081a6c7da3 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 31 Jan 2015 20:24:36 -0800 Subject: [PATCH 13/22] std: Add `io` module again This commit is an implementation of [RFC 576][rfc] which adds back the `std::io` module to the standard library. No functionality in `std::old_io` has been deprecated just yet, and the new `std::io` module is behind the same `io` feature gate. [rfc]: https://github.com/rust-lang/rfcs/pull/576 A good bit of functionality was copied over from `std::old_io`, but many tweaks were required for the new method signatures. Behavior such as precisely when buffered objects call to the underlying object may have been tweaked slightly in the transition. All implementations were audited to use composition wherever possible. For example the custom `pos` and `cap` cursors in `BufReader` were removed in favor of just using `Cursor>`. A few liberties were taken during this implementation which were not explicitly spelled out in the RFC: * The old `LineBufferedWriter` is now named `LineWriter` * The internal representation of `Error` now favors OS error codes (a 0-allocation path) and contains a `Box` for extra semantic data. * The io prelude currently reexports `Seek` as `NewSeek` to prevent conflicts with the real prelude reexport of `old_io::Seek` * The `chars` method was moved from `BufReadExt` to `ReadExt`. * The `chars` iterator returns a custom error with a variant that explains that the data was not valid UTF-8. --- src/libstd/io/buffered.rs | 676 ++++++++++++++++++++++++ src/libstd/io/cursor.rs | 408 +++++++++++++++ src/libstd/io/error.rs | 183 +++++++ src/libstd/io/impls.rs | 88 ++++ src/libstd/io/mod.rs | 948 ++++++++++++++++++++++++++++++++++ src/libstd/io/prelude.rs | 27 + src/libstd/io/util.rs | 153 ++++++ src/libstd/lib.rs | 6 +- src/libstd/sys/unix/mod.rs | 32 +- src/libstd/sys/windows/mod.rs | 29 ++ 10 files changed, 2546 insertions(+), 4 deletions(-) create mode 100644 src/libstd/io/buffered.rs create mode 100644 src/libstd/io/cursor.rs create mode 100644 src/libstd/io/error.rs create mode 100644 src/libstd/io/impls.rs create mode 100644 src/libstd/io/mod.rs create mode 100644 src/libstd/io/prelude.rs create mode 100644 src/libstd/io/util.rs 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 839983d336d76..9efb6fa4247aa 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/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/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 From 6ec5a0f62b3b153ef5846429ffe2f0bf0f9fc5d0 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 3 Feb 2015 22:04:13 +0530 Subject: [PATCH 14/22] Error when #![staged_api] crates are missing stability markers --- src/librustc/middle/stability.rs | 43 ++++++++++++++++++++++---------- src/librustc_driver/driver.rs | 2 +- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 88719a9bbdd29..f446c4daefb4f 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}; @@ -45,14 +46,15 @@ pub struct Index { struct Annotator<'a> { sess: &'a Session, index: &'a mut Index, - parent: Option + 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,13 @@ 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.span_err(item_sp, + "This node does not have a stability attribute"); + } } f(self); } @@ -93,11 +101,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 +122,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,37 +142,38 @@ 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(&mut self, sess: &Session, krate: &Crate) { + 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 + parent: None, + export_map: export_map, }; annotator.annotate(ast::CRATE_NODE_ID, true, &krate.attrs, krate.span, - |v| visit::walk_crate(v, krate)); + |v| visit::walk_crate(v, krate), true); } pub fn new(krate: &Crate) -> Index { diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 166a1d6f809b3..8ede037594a00 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -627,7 +627,7 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, // Do not move this check past lint time(time_passes, "stability index", (), |_| - ty_cx.stability.borrow_mut().build(&ty_cx.sess, krate)); + ty_cx.stability.borrow_mut().build(&ty_cx.sess, krate, &public_items)); time(time_passes, "intrinsic checking", (), |_| middle::intrinsicck::check_crate(&ty_cx)); From c6aaea67250d32bde3ef26b7c864c3d6c1e05726 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 4 Feb 2015 00:15:28 +0530 Subject: [PATCH 15/22] Fix some missing stability attrs --- src/libstd/thread_local/mod.rs | 1 + 1 file changed, 1 insertion(+) 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; From 4aa661ab3608551edab18584b958d281f8e4c094 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 4 Feb 2015 00:30:54 +0530 Subject: [PATCH 16/22] Add test for missing stability checker --- src/test/compile-fail/missing-stability.rs | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/test/compile-fail/missing-stability.rs 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 From b64572cefe9f37c58c07af745afc91bfd0303c30 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 4 Feb 2015 01:21:26 +0530 Subject: [PATCH 17/22] Add unmarked_api feature (fixes #21884) --- src/librustc/middle/stability.rs | 17 +++++++++++++---- src/libsyntax/feature_gate.rs | 6 ++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index f446c4daefb4f..3a69489df2a4b 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -253,10 +253,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/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 } } From d02d4c3c9bf0500d951efb20f273207e075259df Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 4 Feb 2015 01:26:15 +0530 Subject: [PATCH 18/22] Add staged_api and unmarked_api features to reference.md --- src/doc/reference.md | 7 +++++++ 1 file changed, 7 insertions(+) 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 From f5e5bdb1973faddd9cab12201f5f8678a2f5b985 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 4 Feb 2015 03:20:12 +0530 Subject: [PATCH 19/22] Fix test --- src/test/auxiliary/lint_stability.rs | 13 +++------- src/test/compile-fail/lint-stability.rs | 32 +------------------------ 2 files changed, 4 insertions(+), 41 deletions(-) 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/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(); } From 2258f906abcd518ebd88dde2c7a7605373059687 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 4 Feb 2015 03:21:10 +0530 Subject: [PATCH 20/22] Don't check stability for tests --- src/librustc/middle/stability.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 3a69489df2a4b..3304bd4ae2952 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -75,7 +75,8 @@ impl<'a> Annotator<'a> { 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.export_map.contains(&id) + && !self.sess.opts.test { self.sess.span_err(item_sp, "This node does not have a stability attribute"); } From a5ddacf001e4207efd732ecb04250783a64f36c8 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 4 Feb 2015 03:46:36 +0530 Subject: [PATCH 21/22] More test fixes --- src/librustc_driver/test.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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); From 70ecd8ed38d5bedbeb281d78c3da44477764236a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 3 Feb 2015 16:39:27 -0800 Subject: [PATCH 22/22] Test fixes and rebase conflicts --- src/libstd/path.rs | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) 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]