From 7d296c4843785f64a4246213375b21c1ab1e7462 Mon Sep 17 00:00:00 2001 From: Vitali Lovich <vlovich@google.com> Date: Fri, 2 Feb 2018 11:17:48 -0800 Subject: [PATCH 01/46] Add Condvar APIs not susceptible to spurious wake Provide wait_until and wait_timeout_until helper wrappers that aren't susceptible to spurious wake. --- src/libstd/sync/condvar.rs | 207 ++++++++++++++++++++++++++++++++++++- 1 file changed, 205 insertions(+), 2 deletions(-) diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 564021758176b..1e5beaaa34275 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -14,7 +14,7 @@ use sync::{mutex, MutexGuard, PoisonError}; use sys_common::condvar as sys; use sys_common::mutex as sys_mutex; use sys_common::poison::{self, LockResult}; -use time::Duration; +use time::{Duration, Instant}; /// A type indicating whether a timed wait on a condition variable returned /// due to a time out or not. @@ -219,6 +219,61 @@ impl Condvar { } } + /// Blocks the current thread until this condition variable receives a + /// notification and the required condition is met. There are no spurious + /// wakeups when calling this. + /// + /// This function will atomically unlock the mutex specified (represented by + /// `guard`) and block the current thread. This means that any calls + /// to [`notify_one`] or [`notify_all`] which happen logically after the + /// mutex is unlocked are candidates to wake this thread up. When this + /// function call returns, the lock specified will have been re-acquired. + /// + /// # Errors + /// + /// This function will return an error if the mutex being waited on is + /// poisoned when this thread re-acquires the lock. For more information, + /// see information about [poisoning] on the [`Mutex`] type. + /// + /// [`notify_one`]: #method.notify_one + /// [`notify_all`]: #method.notify_all + /// [poisoning]: ../sync/struct.Mutex.html#poisoning + /// [`Mutex`]: ../sync/struct.Mutex.html + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// thread::spawn(move|| { + /// let &(ref lock, ref cvar) = &*pair2; + /// let mut started = lock.lock().unwrap(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let &(ref lock, ref cvar) = &*pair; + /// // As long as the value inside the `Mutex` is false, we wait. + /// cvar.wait_until(lock.lock().unwrap(), |ref started| { started }); + /// ``` + #[stable(feature = "wait_until", since = "1.24")] + pub fn wait_until<'a, T, F>(&self, mut guard: MutexGuard<'a, T>, + mut condition: F) + -> LockResult<MutexGuard<'a, T>> + where F: FnMut(&T) -> bool { + while !condition(&*guard) { + guard = self.wait(guard)?; + } + Ok(guard) + } + + /// Waits on this condition variable for a notification, timing out after a /// specified duration. /// @@ -293,7 +348,15 @@ impl Condvar { /// /// Note that the best effort is made to ensure that the time waited is /// measured with a monotonic clock, and not affected by the changes made to - /// the system time. + /// the system time. This function is susceptible to spurious wakeups. + /// Condition variables normally have a boolean predicate associated with + /// them, and the predicate must always be checked each time this function + /// returns to protect against spurious wakeups. Additionally, it is + /// typically desirable for the time-out to not exceed some duration in + /// spite of spurious wakes, thus the sleep-duration is decremented by the + /// amount slept. Alternatively, use the `wait_timeout_until` method + /// to wait until a condition is met with a total time-out regardless + /// of spurious wakes. /// /// The returned [`WaitTimeoutResult`] value indicates if the timeout is /// known to have elapsed. @@ -302,6 +365,7 @@ impl Condvar { /// returns, regardless of whether the timeout elapsed or not. /// /// [`wait`]: #method.wait + /// [`wait_timeout_until`]: #method.wait_timeout_until /// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html /// /// # Examples @@ -353,6 +417,76 @@ impl Condvar { } } + /// Waits on this condition variable for a notification, timing out after a + /// specified duration. + /// + /// The semantics of this function are equivalent to [`wait_until`] except + /// that the thread will be blocked for roughly no longer than `dur`. This + /// method should not be used for precise timing due to anomalies such as + /// preemption or platform differences that may not cause the maximum + /// amount of time waited to be precisely `dur`. + /// + /// Note that the best effort is made to ensure that the time waited is + /// measured with a monotonic clock, and not affected by the changes made to + /// the system time. + /// + /// The returned [`WaitTimeoutResult`] value indicates if the timeout is + /// known to have elapsed without the condition being met. + /// + /// Like [`wait_until`], the lock specified will be re-acquired when this + /// function returns, regardless of whether the timeout elapsed or not. + /// + /// [`wait_until`]: #method.wait_until + /// [`wait_timeout`]: #method.wait_timeout + /// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// use std::time::Duration; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// thread::spawn(move|| { + /// let &(ref lock, ref cvar) = &*pair2; + /// let mut started = lock.lock().unwrap(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // wait for the thread to start up + /// let &(ref lock, ref cvar) = &*pair; + /// let result = cvar.wait_timeout_until(lock, Duration::from_millis(100), |started| { + /// started + /// }).unwrap(); + /// if result.1.timed_out() { + /// // timed-out without the condition ever evaluating to true. + /// } + /// // access the locked mutex via result.0 + /// ``` + #[stable(feature = "wait_timeout_until", since = "1.24")] + pub fn wait_timeout_until<'a, T, F>(&self, mut guard: MutexGuard<'a, T>, + mut dur: Duration, mut condition: F) + -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> + where F: FnMut(&T) -> bool { + let timed_out = Duration::new(0, 0); + loop { + if !condition(&*guard) { + return Ok((guard, WaitTimeoutResult(false))); + } else if dur == timed_out { + return Ok((guard, WaitTimeoutResult(false))); + } + let wait_timer = Instant::now(); + let wait_result = self.wait_timeout(guard, dur)?; + dur = dur.checked_sub(wait_timer.elapsed()).unwrap_or(timed_out); + guard = wait_result.0; + } + } + /// Wakes up one blocked thread on this condvar. /// /// If there is a blocked thread on this condition variable, then it will @@ -546,6 +680,29 @@ mod tests { } } + #[test] + #[cfg_attr(target_os = "emscripten", ignore)] + fn wait_until() { + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair2 = pair.clone(); + + // Inside of our lock, spawn a new thread, and then wait for it to start. + thread::spawn(move|| { + let &(ref lock, ref cvar) = &*pair2; + let mut started = lock.lock().unwrap(); + *started = true; + // We notify the condvar that the value has changed. + cvar.notify_one(); + }); + + // Wait for the thread to start up. + let &(ref lock, ref cvar) = &*pair; + let guard = cvar.wait_until(lock.lock().unwrap(), |started| { + started + }); + assert!(*guard); + } + #[test] #[cfg_attr(target_os = "emscripten", ignore)] fn wait_timeout_wait() { @@ -565,6 +722,52 @@ mod tests { } } + #[test] + #[cfg_attr(target_os = "emscripten", ignore)] + fn wait_timeout_until_wait() { + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + let g = m.lock().unwrap(); + let (_g, wait) = c.wait_timeout_until(g, Duration::from_millis(1), || { false }).unwrap(); + // no spurious wakeups. ensure it timed-out + assert!(wait.timed_out()); + } + + #[test] + #[cfg_attr(target_os = "emscripten", ignore)] + fn wait_timeout_until_instant_satisfy() { + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + let g = m.lock().unwrap(); + let (_g, wait) = c.wait_timeout_until(g, Duration::from_millis(0), || { true }).unwrap(); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); + } + + #[test] + #[cfg_attr(target_os = "emscripten", ignore)] + fn wait_timeout_until_wake() { + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair_copy = pair.clone(); + + let g = m.lock().unwrap(); + let t = thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair2; + let mut started = lock.lock().unwrap(); + thread::sleep(Duration::from_millis(1)); + started = true; + cvar.notify_one(); + }); + let (g2, wait) = c.wait_timeout_until(g, Duration::from_millis(u64::MAX), |¬ified| { + notified + }).unwrap(); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); + assert!(*g2); + } + #[test] #[cfg_attr(target_os = "emscripten", ignore)] fn wait_timeout_wake() { From 404e1a67007a254ab35e4fe8a8650e9335590a76 Mon Sep 17 00:00:00 2001 From: Vitali Lovich <vlovich@google.com> Date: Fri, 2 Feb 2018 11:52:16 -0800 Subject: [PATCH 02/46] Fix typo --- src/libstd/sync/condvar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 1e5beaaa34275..c58f8fd299010 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -478,7 +478,7 @@ impl Condvar { if !condition(&*guard) { return Ok((guard, WaitTimeoutResult(false))); } else if dur == timed_out { - return Ok((guard, WaitTimeoutResult(false))); + return Ok((guard, WaitTimeoutResult(true))); } let wait_timer = Instant::now(); let wait_result = self.wait_timeout(guard, dur)?; From e72bd6df5398dd7ee02c6057b861537c49649b4e Mon Sep 17 00:00:00 2001 From: Vitali Lovich <vlovich@google.com> Date: Fri, 2 Feb 2018 12:07:16 -0800 Subject: [PATCH 03/46] Review response Make condition closure accept mut T&. Clarify spurious wakeup documentation. Cleanup doc example code. --- src/libstd/sync/condvar.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index c58f8fd299010..76b68fc4f4fe9 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -220,8 +220,9 @@ impl Condvar { } /// Blocks the current thread until this condition variable receives a - /// notification and the required condition is met. There are no spurious - /// wakeups when calling this. + /// notification and the required condition is met. Spurious wakeups are + /// ignored and this function will only return once the condition has been + /// met. /// /// This function will atomically unlock the mutex specified (represented by /// `guard`) and block the current thread. This means that any calls @@ -260,14 +261,14 @@ impl Condvar { /// // Wait for the thread to start up. /// let &(ref lock, ref cvar) = &*pair; /// // As long as the value inside the `Mutex` is false, we wait. - /// cvar.wait_until(lock.lock().unwrap(), |ref started| { started }); + /// cvar.wait_until(lock.lock().unwrap(), |started| { started }); /// ``` #[stable(feature = "wait_until", since = "1.24")] pub fn wait_until<'a, T, F>(&self, mut guard: MutexGuard<'a, T>, mut condition: F) -> LockResult<MutexGuard<'a, T>> - where F: FnMut(&T) -> bool { - while !condition(&*guard) { + where F: FnMut(&mut T) -> bool { + while !condition(&mut *guard) { guard = self.wait(guard)?; } Ok(guard) @@ -418,7 +419,8 @@ impl Condvar { } /// Waits on this condition variable for a notification, timing out after a - /// specified duration. + /// specified duration. Spurious wakes will not cause this function to + /// return. /// /// The semantics of this function are equivalent to [`wait_until`] except /// that the thread will be blocked for roughly no longer than `dur`. This @@ -472,10 +474,10 @@ impl Condvar { pub fn wait_timeout_until<'a, T, F>(&self, mut guard: MutexGuard<'a, T>, mut dur: Duration, mut condition: F) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> - where F: FnMut(&T) -> bool { + where F: FnMut(&mut T) -> bool { let timed_out = Duration::new(0, 0); loop { - if !condition(&*guard) { + if !condition(&mut *guard) { return Ok((guard, WaitTimeoutResult(false))); } else if dur == timed_out { return Ok((guard, WaitTimeoutResult(true))); From 95e4dc2ad143c91d0930ea28634e6f5c54ac0812 Mon Sep 17 00:00:00 2001 From: Vitali Lovich <vlovich@google.com> Date: Mon, 5 Feb 2018 15:11:00 -0800 Subject: [PATCH 04/46] Simplify wait_timeout_until & fix condition typo --- src/libstd/sync/condvar.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 76b68fc4f4fe9..e6a3388aa2587 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -475,17 +475,16 @@ impl Condvar { mut dur: Duration, mut condition: F) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> where F: FnMut(&mut T) -> bool { - let timed_out = Duration::new(0, 0); + let start = Instant::now(); loop { - if !condition(&mut *guard) { + if condition(&mut *guard) { return Ok((guard, WaitTimeoutResult(false))); - } else if dur == timed_out { - return Ok((guard, WaitTimeoutResult(true))); } - let wait_timer = Instant::now(); - let wait_result = self.wait_timeout(guard, dur)?; - dur = dur.checked_sub(wait_timer.elapsed()).unwrap_or(timed_out); - guard = wait_result.0; + let timeout = match dur.checked_sub(start.elapsed()) { + Some(timeout) => timeout, + None => return Ok((guard, WaitTimeoutResult(true))), + } + guard = self.wait_timeout(guard, dur)?.0; } } From 770fdeddb4ca7cedca41a391d4b283069e1d9a8c Mon Sep 17 00:00:00 2001 From: hedgehog1024 <hedgehog1024@scryptmail.com> Date: Mon, 12 Feb 2018 22:19:37 +0300 Subject: [PATCH 05/46] Stabilize 'entry_and_modify' feature for BTreeMap --- src/liballoc/btree/map.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index b320bed54320a..b98489c516a05 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -2114,7 +2114,6 @@ impl<'a, K: Ord, V> Entry<'a, K, V> { /// # Examples /// /// ``` - /// #![feature(entry_and_modify)] /// use std::collections::BTreeMap; /// /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); @@ -2129,7 +2128,7 @@ impl<'a, K: Ord, V> Entry<'a, K, V> { /// .or_insert(42); /// assert_eq!(map["poneyland"], 43); /// ``` - #[unstable(feature = "entry_and_modify", issue = "44733")] + #[stable(feature = "entry_and_modify", since = "1.25.0")] pub fn and_modify<F>(self, mut f: F) -> Self where F: FnMut(&mut V) { From 862132be72d4de87330e31d53489b8c718a6663e Mon Sep 17 00:00:00 2001 From: hedgehog1024 <hedgehog1024@scryptmail.com> Date: Mon, 12 Feb 2018 22:25:03 +0300 Subject: [PATCH 06/46] Stabilize 'entry_and_modify' feature for HashMap --- src/libstd/collections/hash/map.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 82a687ae5e493..80e52123a0177 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -2066,7 +2066,6 @@ impl<'a, K, V> Entry<'a, K, V> { /// # Examples /// /// ``` - /// #![feature(entry_and_modify)] /// use std::collections::HashMap; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); @@ -2081,7 +2080,7 @@ impl<'a, K, V> Entry<'a, K, V> { /// .or_insert(42); /// assert_eq!(map["poneyland"], 43); /// ``` - #[unstable(feature = "entry_and_modify", issue = "44733")] + #[stable(feature = "entry_and_modify", since = "1.25.0")] pub fn and_modify<F>(self, mut f: F) -> Self where F: FnMut(&mut V) { From 4360dfa126ba5d26a520e8a0d2dd9680081dad0d Mon Sep 17 00:00:00 2001 From: hedgehog1024 <hedgehog1024@scryptmail.com> Date: Mon, 12 Feb 2018 22:27:33 +0300 Subject: [PATCH 07/46] Delete information about 'entry_and_modify' from Unstable book --- .../src/library-features/entry-and-modify.md | 77 ------------------- 1 file changed, 77 deletions(-) delete mode 100644 src/doc/unstable-book/src/library-features/entry-and-modify.md diff --git a/src/doc/unstable-book/src/library-features/entry-and-modify.md b/src/doc/unstable-book/src/library-features/entry-and-modify.md deleted file mode 100644 index 1280c71e83c92..0000000000000 --- a/src/doc/unstable-book/src/library-features/entry-and-modify.md +++ /dev/null @@ -1,77 +0,0 @@ -# `entry_and_modify` - -The tracking issue for this feature is: [#44733] - -[#44733]: https://github.com/rust-lang/rust/issues/44733 - ------------------------- - -This introduces a new method for the Entry API of maps -(`std::collections::HashMap` and `std::collections::BTreeMap`), so that -occupied entries can be modified before any potential inserts into the -map. - -For example: - -```rust -#![feature(entry_and_modify)] -# fn main() { -use std::collections::HashMap; - -struct Foo { - new: bool, -} - -let mut map: HashMap<&str, Foo> = HashMap::new(); - -map.entry("quux") - .and_modify(|e| e.new = false) - .or_insert(Foo { new: true }); -# } -``` - -This is not possible with the stable API alone since inserting a default -_before_ modifying the `new` field would mean we would lose the default state: - -```rust -# fn main() { -use std::collections::HashMap; - -struct Foo { - new: bool, -} - -let mut map: HashMap<&str, Foo> = HashMap::new(); - -map.entry("quux").or_insert(Foo { new: true }).new = false; -# } -``` - -In the above code the `new` field will never be `true`, even though we only -intended to update that field to `false` for previously extant entries. - -To achieve the same effect as `and_modify` we would have to manually match -against the `Occupied` and `Vacant` variants of the `Entry` enum, which is -a little less user-friendly, and much more verbose: - -```rust -# fn main() { -use std::collections::HashMap; -use std::collections::hash_map::Entry; - -struct Foo { - new: bool, -} - -let mut map: HashMap<&str, Foo> = HashMap::new(); - -match map.entry("quux") { - Entry::Occupied(entry) => { - entry.into_mut().new = false; - }, - Entry::Vacant(entry) => { - entry.insert(Foo { new: true }); - }, -}; -# } -``` From 97df227d19791b0e9d199be28851fb924c3c1e21 Mon Sep 17 00:00:00 2001 From: Vitali Lovich <vlovich@google.com> Date: Mon, 12 Feb 2018 21:08:14 -0800 Subject: [PATCH 08/46] Fix wait_timeout value --- src/libstd/sync/condvar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index e6a3388aa2587..65235aa62a19b 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -484,7 +484,7 @@ impl Condvar { Some(timeout) => timeout, None => return Ok((guard, WaitTimeoutResult(true))), } - guard = self.wait_timeout(guard, dur)?.0; + guard = self.wait_timeout(guard, timeout)?.0; } } From f45a474bd6c7ccfe35e7be5f341e3d04aa5d178e Mon Sep 17 00:00:00 2001 From: James Cowgill <jcowgill@debian.org> Date: Tue, 6 Feb 2018 17:11:27 +0000 Subject: [PATCH 09/46] rustc_trans: add abi::CastTarget::ChunkedPrefix --- src/librustc_trans/abi.rs | 40 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index 12698964d2e65..60f3105170b7d 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -407,7 +407,8 @@ impl<'tcx> LayoutExt<'tcx> for TyLayout<'tcx> { #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum CastTarget { Uniform(Uniform), - Pair(Reg, Reg) + Pair(Reg, Reg), + ChunkedPrefix { prefix: [RegKind; 8], chunk: Size, total: Size } } impl From<Reg> for CastTarget { @@ -429,7 +430,8 @@ impl CastTarget { CastTarget::Pair(a, b) => { (a.size.abi_align(a.align(cx)) + b.size) .abi_align(self.align(cx)) - } + }, + CastTarget::ChunkedPrefix { total, .. } => total } } @@ -440,6 +442,12 @@ impl CastTarget { cx.data_layout().aggregate_align .max(a.align(cx)) .max(b.align(cx)) + }, + CastTarget::ChunkedPrefix { chunk, .. } => { + cx.data_layout().aggregate_align + .max(Reg { kind: RegKind::Integer, size: chunk }.align(cx)) + .max(Reg { kind: RegKind::Float, size: chunk }.align(cx)) + .max(Reg { kind: RegKind::Vector, size: chunk }.align(cx)) } } } @@ -452,6 +460,34 @@ impl CastTarget { a.llvm_type(cx), b.llvm_type(cx) ], false) + }, + CastTarget::ChunkedPrefix { prefix, chunk, total } => { + let total_chunks = total.bytes() / chunk.bytes(); + let rem_bytes = total.bytes() % chunk.bytes(); + let prefix_chunks = total_chunks.min(prefix.len() as u64); + + let int_ll_type = Reg { kind: RegKind::Integer, size: chunk }.llvm_type(cx); + + // Simple cases simplify to an array + if rem_bytes == 0 && prefix.into_iter().all(|&kind| kind == RegKind::Integer) { + return Type::array(&int_ll_type, total_chunks); + } + + // The final structure is made up of: + // Up to 8 chunks of the type specified in the prefix + // Any other complete chunks as integers + // One final integer needed to make up the total structure size + let mut args: Vec<_> = + prefix.into_iter().take(prefix_chunks as usize) + .map(|&kind| Reg { kind: kind, size: chunk }.llvm_type(cx)) + .chain((0..total_chunks - prefix_chunks).map(|_| int_ll_type)) + .collect(); + + if rem_bytes > 0 { + args.push(Type::ix(cx, rem_bytes * 8)); + } + + Type::struct_(cx, &args, false) } } } From 6fe2d1d765810c05ce2aa2184baa9f4aabf1a151 Mon Sep 17 00:00:00 2001 From: Vitali Lovich <vlovich@google.com> Date: Tue, 13 Feb 2018 11:32:04 -0800 Subject: [PATCH 10/46] Misc fixes Switch feature guards to unstable Add missing semicolon Remove mut that's no longer necessary --- src/libstd/sync/condvar.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 65235aa62a19b..98fadbd35439a 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -263,7 +263,7 @@ impl Condvar { /// // As long as the value inside the `Mutex` is false, we wait. /// cvar.wait_until(lock.lock().unwrap(), |started| { started }); /// ``` - #[stable(feature = "wait_until", since = "1.24")] + #[unstable(feature = "wait_until", issue = "47960")] pub fn wait_until<'a, T, F>(&self, mut guard: MutexGuard<'a, T>, mut condition: F) -> LockResult<MutexGuard<'a, T>> @@ -470,9 +470,9 @@ impl Condvar { /// } /// // access the locked mutex via result.0 /// ``` - #[stable(feature = "wait_timeout_until", since = "1.24")] + #[unstable(feature = "wait_timeout_until", issue = "47960")] pub fn wait_timeout_until<'a, T, F>(&self, mut guard: MutexGuard<'a, T>, - mut dur: Duration, mut condition: F) + dur: Duration, mut condition: F) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> where F: FnMut(&mut T) -> bool { let start = Instant::now(); @@ -483,7 +483,7 @@ impl Condvar { let timeout = match dur.checked_sub(start.elapsed()) { Some(timeout) => timeout, None => return Ok((guard, WaitTimeoutResult(true))), - } + }; guard = self.wait_timeout(guard, timeout)?.0; } } From 16350526d8d8b514327c62b904d26f74937a1b23 Mon Sep 17 00:00:00 2001 From: Jimmy Brush <code@jimmah.com> Date: Thu, 8 Feb 2018 07:48:16 -0500 Subject: [PATCH 11/46] pass correct pie args to gcc linker When linking with gcc, run gcc -v to see if --enable-default-pie is compiled in. If it is, pass -no-pie when necessary to disable pie. Otherwise, pass -pie when necessary to enable it. Fixes #48032 and fixes #35061 --- src/librustc_trans/back/link.rs | 66 ++++++++++++++++++++++++++----- src/librustc_trans/back/linker.rs | 10 +++++ 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index f050edcd513b9..a84ac5cb8bc2f 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -897,16 +897,33 @@ fn link_args(cmd: &mut Linker, let used_link_args = &trans.crate_info.link_args; - if crate_type == config::CrateTypeExecutable && - t.options.position_independent_executables { - let empty_vec = Vec::new(); - let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); - let more_args = &sess.opts.cg.link_arg; - let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter()); - - if get_reloc_model(sess) == llvm::RelocMode::PIC - && !sess.crt_static() && !args.any(|x| *x == "-static") { - cmd.position_independent_executable(); + if crate_type == config::CrateTypeExecutable { + let mut position_independent_executable = false; + + if t.options.position_independent_executables { + let empty_vec = Vec::new(); + let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); + let more_args = &sess.opts.cg.link_arg; + let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter()); + + if get_reloc_model(sess) == llvm::RelocMode::PIC + && !sess.crt_static() && !args.any(|x| *x == "-static") { + position_independent_executable = true; + } + } + + // Check to see if gcc defaults to generating a position independent + // executable. If so, tell it when to disable pie. Otherwise, tell it + // when to enable it. We can't do both because older versions of gcc + // don't understand -no-pie and will blow up. + if is_pie_default(sess) { + if !position_independent_executable { + cmd.no_position_independent_executable(); + } + } else { + if position_independent_executable { + cmd.position_independent_executable(); + } } } @@ -1421,3 +1438,32 @@ fn is_full_lto_enabled(sess: &Session) -> bool { Lto::ThinLocal => false, } } + +fn is_pie_default(sess: &Session) -> bool { + match sess.linker_flavor() { + LinkerFlavor::Gcc => { + let (_, mut cmd, envs) = get_linker(sess); + // This will set PATH on windows + cmd.envs(envs); + cmd.arg("-v"); + + info!("{:?}", &cmd); + + let output = cmd.command() + .stdout(Stdio::piped()).stderr(Stdio::piped()) + .spawn() + .unwrap() + .wait_with_output() + .unwrap(); + + let ret = String::from_utf8_lossy(&output.stderr) + .contains("--enable-default-pie"); + + info!("gcc {} compiled with --enable-default-pie", + if ret { "IS" } else { "is NOT" }); + + ret + }, + _ => false, + } +} diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index aa29c3cc12058..7e7811c56c74e 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -105,6 +105,7 @@ pub trait Linker { fn add_object(&mut self, path: &Path); fn gc_sections(&mut self, keep_metadata: bool); fn position_independent_executable(&mut self); + fn no_position_independent_executable(&mut self); fn partial_relro(&mut self); fn full_relro(&mut self); fn optimize(&mut self); @@ -179,6 +180,7 @@ impl<'a> Linker for GccLinker<'a> { fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); } fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } + fn no_position_independent_executable(&mut self) { self.cmd.arg("-no-pie"); } fn partial_relro(&mut self) { self.linker_arg("-z,relro"); } fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); } fn build_static_executable(&mut self) { self.cmd.arg("-static"); } @@ -439,6 +441,10 @@ impl<'a> Linker for MsvcLinker<'a> { // noop } + fn no_position_independent_executable(&mut self) { + // noop + } + fn partial_relro(&mut self) { // noop } @@ -647,6 +653,10 @@ impl<'a> Linker for EmLinker<'a> { // noop } + fn no_position_independent_executable(&mut self) { + // noop + } + fn partial_relro(&mut self) { // noop } From 8a72f589e5f8d628a5dcfcedbdd8eec117fbe2a2 Mon Sep 17 00:00:00 2001 From: Jimmy Brush <code@jimmah.com> Date: Sat, 10 Feb 2018 13:16:59 -0500 Subject: [PATCH 12/46] pass correct pie args to gcc linker 2 Recent versions of gcc default to creating a position independent executable and must be explicitly told not to with the -no-pie argument. Old versions of gcc don't understand -no-pie and will throw an error. Check for that case and retry without -no-pie. This is safe because these old versions of gcc should never default to creating a position independent executable. --- src/librustc_trans/back/link.rs | 66 ++++++++++++--------------------- 1 file changed, 23 insertions(+), 43 deletions(-) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index a84ac5cb8bc2f..d15450212ae37 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -652,9 +652,6 @@ fn link_natively(sess: &Session, prog = time(sess.time_passes(), "running linker", || { exec_linker(sess, &mut cmd, tmpdir) }); - if !retry_on_segfault || i > 3 { - break - } let output = match prog { Ok(ref output) => output, Err(_) => break, @@ -665,6 +662,26 @@ fn link_natively(sess: &Session, let mut out = output.stderr.clone(); out.extend(&output.stdout); let out = String::from_utf8_lossy(&out); + + // Check to see if the link failed with "unrecognized command line option: + // '-no-pie'". If so, reperform the link step without the -no-pie option. This + // is safe because if the linker doesn't support -no-pie then it should not + // default to linking executables as pie. Different versions of gcc seem to + // use different quotes in the error message so don't check for them. + if out.contains("unrecognized command line option") && out.contains("-no-pie") { + info!("linker output: {:?}", out); + warn!("Linker does not support -no-pie command line option. Retrying without."); + for arg in cmd.take_args() { + if arg.to_string_lossy() != "-no-pie" { + cmd.arg(arg); + } + } + info!("{:?}", &cmd); + continue; + } + if !retry_on_segfault || i > 3 { + break + } let msg_segv = "clang: error: unable to execute command: Segmentation fault: 11"; let msg_bus = "clang: error: unable to execute command: Bus error: 10"; if !(out.contains(msg_segv) || out.contains(msg_bus)) { @@ -912,18 +929,10 @@ fn link_args(cmd: &mut Linker, } } - // Check to see if gcc defaults to generating a position independent - // executable. If so, tell it when to disable pie. Otherwise, tell it - // when to enable it. We can't do both because older versions of gcc - // don't understand -no-pie and will blow up. - if is_pie_default(sess) { - if !position_independent_executable { - cmd.no_position_independent_executable(); - } + if position_independent_executable { + cmd.position_independent_executable(); } else { - if position_independent_executable { - cmd.position_independent_executable(); - } + cmd.no_position_independent_executable(); } } @@ -1438,32 +1447,3 @@ fn is_full_lto_enabled(sess: &Session) -> bool { Lto::ThinLocal => false, } } - -fn is_pie_default(sess: &Session) -> bool { - match sess.linker_flavor() { - LinkerFlavor::Gcc => { - let (_, mut cmd, envs) = get_linker(sess); - // This will set PATH on windows - cmd.envs(envs); - cmd.arg("-v"); - - info!("{:?}", &cmd); - - let output = cmd.command() - .stdout(Stdio::piped()).stderr(Stdio::piped()) - .spawn() - .unwrap() - .wait_with_output() - .unwrap(); - - let ret = String::from_utf8_lossy(&output.stderr) - .contains("--enable-default-pie"); - - info!("gcc {} compiled with --enable-default-pie", - if ret { "IS" } else { "is NOT" }); - - ret - }, - _ => false, - } -} From f0e9af1c5509ecd57c2398085b639abfb15f4ba1 Mon Sep 17 00:00:00 2001 From: Jimmy Brush <code@jimmah.com> Date: Sun, 11 Feb 2018 10:50:18 -0500 Subject: [PATCH 13/46] verify passed -no-pie arg before retrying failed link --- src/librustc_trans/back/command.rs | 4 ++++ src/librustc_trans/back/link.rs | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/librustc_trans/back/command.rs b/src/librustc_trans/back/command.rs index 3b765a493e0e7..0bccef1e62a8e 100644 --- a/src/librustc_trans/back/command.rs +++ b/src/librustc_trans/back/command.rs @@ -109,6 +109,10 @@ impl Command { // extensions + pub fn get_args(&self) -> &[OsString] { + &self.args + } + pub fn take_args(&mut self) -> Vec<OsString> { mem::replace(&mut self.args, Vec::new()) } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index d15450212ae37..6f8b425ad56dc 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -668,7 +668,9 @@ fn link_natively(sess: &Session, // is safe because if the linker doesn't support -no-pie then it should not // default to linking executables as pie. Different versions of gcc seem to // use different quotes in the error message so don't check for them. - if out.contains("unrecognized command line option") && out.contains("-no-pie") { + if out.contains("unrecognized command line option") && + out.contains("-no-pie") && + cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") { info!("linker output: {:?}", out); warn!("Linker does not support -no-pie command line option. Retrying without."); for arg in cmd.take_args() { From c8def9222be5079585e15b2902039d371b7527ce Mon Sep 17 00:00:00 2001 From: Jimmy Brush <code@jimmah.com> Date: Mon, 12 Feb 2018 13:32:55 -0500 Subject: [PATCH 14/46] handle -no-pie error from clang --- src/librustc_trans/back/link.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 6f8b425ad56dc..0a3e6265c1b7e 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -664,11 +664,13 @@ fn link_natively(sess: &Session, let out = String::from_utf8_lossy(&out); // Check to see if the link failed with "unrecognized command line option: - // '-no-pie'". If so, reperform the link step without the -no-pie option. This - // is safe because if the linker doesn't support -no-pie then it should not - // default to linking executables as pie. Different versions of gcc seem to - // use different quotes in the error message so don't check for them. - if out.contains("unrecognized command line option") && + // '-no-pie'" for gcc or "unknown argument: '-no-pie'" for clang. If so, + // reperform the link step without the -no-pie option. This is safe because + // if the linker doesn't support -no-pie then it should not default to + // linking executables as pie. Different versions of gcc seem to use + // different quotes in the error message so don't check for them. + if (out.contains("unrecognized command line option") || + out.contains("unknown argument")) && out.contains("-no-pie") && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") { info!("linker output: {:?}", out); From ab9cae1ba192afcc35e8bd6a5fddcd1445d05da7 Mon Sep 17 00:00:00 2001 From: Jimmy Brush <code@jimmah.com> Date: Tue, 13 Feb 2018 22:09:02 -0500 Subject: [PATCH 15/46] only pass -no-pie if linker_is_gnu --- src/librustc_trans/back/link.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 0a3e6265c1b7e..8be4dc48d5122 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -669,7 +669,8 @@ fn link_natively(sess: &Session, // if the linker doesn't support -no-pie then it should not default to // linking executables as pie. Different versions of gcc seem to use // different quotes in the error message so don't check for them. - if (out.contains("unrecognized command line option") || + if sess.target.target.options.linker_is_gnu && + (out.contains("unrecognized command line option") || out.contains("unknown argument")) && out.contains("-no-pie") && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") { @@ -936,7 +937,12 @@ fn link_args(cmd: &mut Linker, if position_independent_executable { cmd.position_independent_executable(); } else { - cmd.no_position_independent_executable(); + // recent versions of gcc can be configured to generate position + // independent executables by default. We have to pass -no-pie to + // explicitly turn that off. + if sess.target.target.options.linker_is_gnu { + cmd.no_position_independent_executable(); + } } } From 68042ba0d35f16d66dadf62334ca6bbf20d97268 Mon Sep 17 00:00:00 2001 From: James Cowgill <jcowgill@debian.org> Date: Thu, 8 Feb 2018 11:01:34 +0000 Subject: [PATCH 16/46] rustc_trans: rewrite mips64 abi --- src/librustc_trans/cabi_mips64.rs | 150 +++++++++++++++++++++++++----- 1 file changed, 127 insertions(+), 23 deletions(-) diff --git a/src/librustc_trans/cabi_mips64.rs b/src/librustc_trans/cabi_mips64.rs index e44063faab810..ad35dbeadfc8d 100644 --- a/src/librustc_trans/cabi_mips64.rs +++ b/src/librustc_trans/cabi_mips64.rs @@ -8,50 +8,154 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use abi::{ArgType, FnType, LayoutExt, Reg, Uniform}; +use abi::{ArgAttribute, ArgType, CastTarget, FnType, LayoutExt, PassMode, Reg, RegKind, Uniform}; use context::CodegenCx; +use rustc::ty::layout::{self, Size}; -use rustc::ty::layout::Size; +fn extend_integer_width_mips(arg: &mut ArgType, bits: u64) { + // Always sign extend u32 values on 64-bit mips + if let layout::Abi::Scalar(ref scalar) = arg.layout.abi { + if let layout::Int(i, signed) = scalar.value { + if !signed && i.size().bits() == 32 { + if let PassMode::Direct(ref mut attrs) = arg.mode { + attrs.set(ArgAttribute::SExt); + return; + } + } + } + } + + arg.extend_integer_width_to(bits); +} + +fn bits_to_int_reg(bits: u64) -> Reg { + if bits <= 8 { + Reg::i8() + } else if bits <= 16 { + Reg::i16() + } else if bits <= 32 { + Reg::i32() + } else { + Reg::i64() + } +} -fn classify_ret_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - ret: &mut ArgType<'tcx>, - offset: &mut Size) { +fn float_reg<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ret: &ArgType<'tcx>, i: usize) -> Option<Reg> { + match ret.layout.field(cx, i).abi { + layout::Abi::Scalar(ref scalar) => match scalar.value { + layout::F32 => Some(Reg::f32()), + layout::F64 => Some(Reg::f64()), + _ => None + }, + _ => None + } +} + +fn classify_ret_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ret: &mut ArgType<'tcx>) { if !ret.layout.is_aggregate() { - ret.extend_integer_width_to(64); + extend_integer_width_mips(ret, 64); + return; + } + + let size = ret.layout.size; + let bits = size.bits(); + if bits <= 128 { + // Unlike other architectures which return aggregates in registers, MIPS n64 limits the + // use of float registers to structures (not unions) containing exactly one or two + // float fields. + + if let layout::FieldPlacement::Arbitrary { .. } = ret.layout.fields { + if ret.layout.fields.count() == 1 { + if let Some(reg) = float_reg(cx, ret, 0) { + ret.cast_to(reg); + return; + } + } else if ret.layout.fields.count() == 2 { + if let Some(reg0) = float_reg(cx, ret, 0) { + if let Some(reg1) = float_reg(cx, ret, 1) { + ret.cast_to(CastTarget::Pair(reg0, reg1)); + return; + } + } + } + } + + // Cast to a uniform int structure + ret.cast_to(Uniform { + unit: bits_to_int_reg(bits), + total: size + }); } else { ret.make_indirect(); - *offset += cx.tcx.data_layout.pointer_size; } } -fn classify_arg_ty(cx: &CodegenCx, arg: &mut ArgType, offset: &mut Size) { +fn classify_arg_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, arg: &mut ArgType<'tcx>) { + if !arg.layout.is_aggregate() { + extend_integer_width_mips(arg, 64); + return; + } + let dl = &cx.tcx.data_layout; let size = arg.layout.size; - let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align); + let mut prefix = [RegKind::Integer; 8]; + let mut prefix_index = 0; - if arg.layout.is_aggregate() { - arg.cast_to(Uniform { - unit: Reg::i64(), - total: size - }); - if !offset.is_abi_aligned(align) { - arg.pad_with(Reg::i64()); + match arg.layout.fields { + layout::FieldPlacement::Array { .. } => { + // Arrays are passed indirectly + arg.make_indirect(); + return; } - } else { - arg.extend_integer_width_to(64); - } + layout::FieldPlacement::Union(_) => { + // Unions and are always treated as a series of 64-bit integer chunks + }, + layout::FieldPlacement::Arbitrary { .. } => { + // Structures are split up into a series of 64-bit integer chunks, but any aligned + // doubles not part of another aggregate are passed as floats. + let mut last_offset = Size::from_bytes(0); + + for i in 0..arg.layout.fields.count() { + let field = arg.layout.field(cx, i); + let offset = arg.layout.fields.offset(i); + + // We only care about aligned doubles + if let layout::Abi::Scalar(ref scalar) = field.abi { + if let layout::F64 = scalar.value { + if offset.is_abi_aligned(dl.f64_align) { + // Skip over enough integers to cover [last_offset, offset) + assert!(last_offset.is_abi_aligned(dl.f64_align)); + prefix_index += ((offset - last_offset).bits() / 64) as usize; + + if prefix_index >= prefix.len() { + break; + } + + prefix[prefix_index] = RegKind::Float; + prefix_index += 1; + last_offset = offset + Reg::f64().size; + } + } + } + } + } + }; - *offset = offset.abi_align(align) + size.abi_align(align); + // Extract first 8 chunks as the prefix + arg.cast_to(CastTarget::ChunkedPrefix { + prefix: prefix, + chunk: Size::from_bytes(8), + total: size + }); } pub fn compute_abi_info<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, fty: &mut FnType<'tcx>) { - let mut offset = Size::from_bytes(0); if !fty.ret.is_ignore() { - classify_ret_ty(cx, &mut fty.ret, &mut offset); + classify_ret_ty(cx, &mut fty.ret); } for arg in &mut fty.args { if arg.is_ignore() { continue; } - classify_arg_ty(cx, arg, &mut offset); + classify_arg_ty(cx, arg); } } From 05d66dc7a4ff053b5cbfa5ddafa890af291f4fc2 Mon Sep 17 00:00:00 2001 From: James Cowgill <jcowgill@debian.org> Date: Wed, 14 Feb 2018 12:47:38 +0000 Subject: [PATCH 17/46] rustc_trans: add chunked prefix fields to CastTarget --- src/librustc_trans/abi.rs | 138 +++++++++++------------------- src/librustc_trans/cabi_x86_64.rs | 2 +- 2 files changed, 53 insertions(+), 87 deletions(-) diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index 60f3105170b7d..ee0f2415bd808 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -40,7 +40,7 @@ use rustc::ty::layout::{self, Align, Size, TyLayout}; use rustc::ty::layout::{HasDataLayout, LayoutOf}; use libc::c_uint; -use std::{cmp, iter}; +use std::cmp; pub use syntax::abi::Abi; pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; @@ -279,30 +279,6 @@ impl Uniform { pub fn align(&self, cx: &CodegenCx) -> Align { self.unit.align(cx) } - - pub fn llvm_type(&self, cx: &CodegenCx) -> Type { - let llunit = self.unit.llvm_type(cx); - - if self.total <= self.unit.size { - return llunit; - } - - let count = self.total.bytes() / self.unit.size.bytes(); - let rem_bytes = self.total.bytes() % self.unit.size.bytes(); - - if rem_bytes == 0 { - return Type::array(&llunit, count); - } - - // Only integers can be really split further. - assert_eq!(self.unit.kind, RegKind::Integer); - - let args: Vec<_> = (0..count).map(|_| llunit) - .chain(iter::once(Type::ix(cx, rem_bytes * 8))) - .collect(); - - Type::struct_(cx, &args, false) - } } pub trait LayoutExt<'tcx> { @@ -405,91 +381,81 @@ impl<'tcx> LayoutExt<'tcx> for TyLayout<'tcx> { } #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum CastTarget { - Uniform(Uniform), - Pair(Reg, Reg), - ChunkedPrefix { prefix: [RegKind; 8], chunk: Size, total: Size } +pub struct CastTarget { + pub prefix: [Option<RegKind>; 8], + pub prefix_chunk: Size, + pub rest: Uniform, } impl From<Reg> for CastTarget { fn from(unit: Reg) -> CastTarget { - CastTarget::Uniform(Uniform::from(unit)) + CastTarget::from(Uniform::from(unit)) } } impl From<Uniform> for CastTarget { fn from(uniform: Uniform) -> CastTarget { - CastTarget::Uniform(uniform) + CastTarget { + prefix: [None; 8], + prefix_chunk: Size::from_bytes(0), + rest: uniform + } } } impl CastTarget { - pub fn size(&self, cx: &CodegenCx) -> Size { - match *self { - CastTarget::Uniform(u) => u.total, - CastTarget::Pair(a, b) => { - (a.size.abi_align(a.align(cx)) + b.size) - .abi_align(self.align(cx)) - }, - CastTarget::ChunkedPrefix { total, .. } => total + pub fn pair(a: Reg, b: Reg) -> CastTarget { + CastTarget { + prefix: [Some(a.kind), None, None, None, None, None, None, None], + prefix_chunk: a.size, + rest: Uniform::from(b) } } + pub fn size(&self, cx: &CodegenCx) -> Size { + (self.prefix_chunk * self.prefix.iter().filter(|x| x.is_some()).count() as u64) + .abi_align(self.rest.align(cx)) + self.rest.total + } + pub fn align(&self, cx: &CodegenCx) -> Align { - match *self { - CastTarget::Uniform(u) => u.align(cx), - CastTarget::Pair(a, b) => { - cx.data_layout().aggregate_align - .max(a.align(cx)) - .max(b.align(cx)) - }, - CastTarget::ChunkedPrefix { chunk, .. } => { - cx.data_layout().aggregate_align - .max(Reg { kind: RegKind::Integer, size: chunk }.align(cx)) - .max(Reg { kind: RegKind::Float, size: chunk }.align(cx)) - .max(Reg { kind: RegKind::Vector, size: chunk }.align(cx)) - } - } + self.prefix.iter() + .filter_map(|x| x.map(|kind| Reg { kind: kind, size: self.prefix_chunk }.align(cx))) + .fold(cx.data_layout().aggregate_align.max(self.rest.align(cx)), + |acc, align| acc.max(align)) } pub fn llvm_type(&self, cx: &CodegenCx) -> Type { - match *self { - CastTarget::Uniform(u) => u.llvm_type(cx), - CastTarget::Pair(a, b) => { - Type::struct_(cx, &[ - a.llvm_type(cx), - b.llvm_type(cx) - ], false) - }, - CastTarget::ChunkedPrefix { prefix, chunk, total } => { - let total_chunks = total.bytes() / chunk.bytes(); - let rem_bytes = total.bytes() % chunk.bytes(); - let prefix_chunks = total_chunks.min(prefix.len() as u64); - - let int_ll_type = Reg { kind: RegKind::Integer, size: chunk }.llvm_type(cx); + let rest_ll_unit = self.rest.unit.llvm_type(cx); + let rest_count = self.rest.total.bytes() / self.rest.unit.size.bytes(); + let rem_bytes = self.rest.total.bytes() % self.rest.unit.size.bytes(); + + if self.prefix.iter().all(|x| x.is_none()) { + // Simplify to a single unit when there is no prefix and size <= unit size + if self.rest.total <= self.rest.unit.size { + return rest_ll_unit; + } - // Simple cases simplify to an array - if rem_bytes == 0 && prefix.into_iter().all(|&kind| kind == RegKind::Integer) { - return Type::array(&int_ll_type, total_chunks); - } + // Simplify to array when all chunks are the same size and type + if rem_bytes == 0 { + return Type::array(&rest_ll_unit, rest_count); + } + } - // The final structure is made up of: - // Up to 8 chunks of the type specified in the prefix - // Any other complete chunks as integers - // One final integer needed to make up the total structure size - let mut args: Vec<_> = - prefix.into_iter().take(prefix_chunks as usize) - .map(|&kind| Reg { kind: kind, size: chunk }.llvm_type(cx)) - .chain((0..total_chunks - prefix_chunks).map(|_| int_ll_type)) - .collect(); - - if rem_bytes > 0 { - args.push(Type::ix(cx, rem_bytes * 8)); - } + // Create list of fields in the main structure + let mut args: Vec<_> = + self.prefix.iter().flat_map(|option_kind| option_kind.map( + |kind| Reg { kind: kind, size: self.prefix_chunk }.llvm_type(cx))) + .chain((0..rest_count).map(|_| rest_ll_unit)) + .collect(); - Type::struct_(cx, &args, false) - } + // Append final integer + if rem_bytes != 0 { + // Only integers can be really split further. + assert_eq!(self.rest.unit.kind, RegKind::Integer); + args.push(Type::ix(cx, rem_bytes * 8)); } + + Type::struct_(cx, &args, false) } } diff --git a/src/librustc_trans/cabi_x86_64.rs b/src/librustc_trans/cabi_x86_64.rs index b8144a3ca7a3e..7eadaa7f493a3 100644 --- a/src/librustc_trans/cabi_x86_64.rs +++ b/src/librustc_trans/cabi_x86_64.rs @@ -171,7 +171,7 @@ fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget { let mut target = CastTarget::from(lo); if size > offset { if let Some(hi) = reg_component(cls, &mut i, size - offset) { - target = CastTarget::Pair(lo, hi); + target = CastTarget::pair(lo, hi); } } assert_eq!(reg_component(cls, &mut i, Size::from_bytes(0)), None); From 47c33f7bd0535fe6e47e38700ac1c8bf33e3f0d5 Mon Sep 17 00:00:00 2001 From: James Cowgill <jcowgill@debian.org> Date: Wed, 14 Feb 2018 12:48:04 +0000 Subject: [PATCH 18/46] rustc_trans: adjust mips64 abi to use new CastTarget --- src/librustc_trans/cabi_mips64.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/librustc_trans/cabi_mips64.rs b/src/librustc_trans/cabi_mips64.rs index ad35dbeadfc8d..94bf53cee1edb 100644 --- a/src/librustc_trans/cabi_mips64.rs +++ b/src/librustc_trans/cabi_mips64.rs @@ -73,7 +73,7 @@ fn classify_ret_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ret: &mut ArgType<'tcx>) } else if ret.layout.fields.count() == 2 { if let Some(reg0) = float_reg(cx, ret, 0) { if let Some(reg1) = float_reg(cx, ret, 1) { - ret.cast_to(CastTarget::Pair(reg0, reg1)); + ret.cast_to(CastTarget::pair(reg0, reg1)); return; } } @@ -98,7 +98,7 @@ fn classify_arg_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, arg: &mut ArgType<'tcx>) let dl = &cx.tcx.data_layout; let size = arg.layout.size; - let mut prefix = [RegKind::Integer; 8]; + let mut prefix = [None; 8]; let mut prefix_index = 0; match arg.layout.fields { @@ -123,15 +123,20 @@ fn classify_arg_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, arg: &mut ArgType<'tcx>) if let layout::Abi::Scalar(ref scalar) = field.abi { if let layout::F64 = scalar.value { if offset.is_abi_aligned(dl.f64_align) { - // Skip over enough integers to cover [last_offset, offset) + // Insert enough integers to cover [last_offset, offset) assert!(last_offset.is_abi_aligned(dl.f64_align)); - prefix_index += ((offset - last_offset).bits() / 64) as usize; + for _ in 0..((offset - last_offset).bits() / 64) + .min((prefix.len() - prefix_index) as u64) { - if prefix_index >= prefix.len() { + prefix[prefix_index] = Some(RegKind::Integer); + prefix_index += 1; + } + + if prefix_index == prefix.len() { break; } - prefix[prefix_index] = RegKind::Float; + prefix[prefix_index] = Some(RegKind::Float); prefix_index += 1; last_offset = offset + Reg::f64().size; } @@ -142,10 +147,11 @@ fn classify_arg_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, arg: &mut ArgType<'tcx>) }; // Extract first 8 chunks as the prefix - arg.cast_to(CastTarget::ChunkedPrefix { + let rest_size = size - Size::from_bytes(8) * prefix_index as u64; + arg.cast_to(CastTarget { prefix: prefix, - chunk: Size::from_bytes(8), - total: size + prefix_chunk: Size::from_bytes(8), + rest: Uniform { unit: Reg::i64(), total: rest_size } }); } From ebec5e39067fdbc8abcad69e7933f6387a3b9fb7 Mon Sep 17 00:00:00 2001 From: "Jonathan A. Kollasch" <jakllsch@kollasch.net> Date: Fri, 16 Feb 2018 14:29:24 -0600 Subject: [PATCH 19/46] Add powerpc-unknown-netbsd target --- src/bootstrap/native.rs | 1 + src/librustc_back/target/mod.rs | 1 + .../target/powerpc_unknown_netbsd.rs | 35 +++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 src/librustc_back/target/powerpc_unknown_netbsd.rs diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 29cd23bdbb197..b44be44234805 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -480,6 +480,7 @@ impl Step for Openssl { "mips64el-unknown-linux-gnuabi64" => "linux64-mips64", "mipsel-unknown-linux-gnu" => "linux-mips32", "powerpc-unknown-linux-gnu" => "linux-ppc", + "powerpc-unknown-netbsd" => "BSD-generic32", "powerpc64-unknown-linux-gnu" => "linux-ppc64", "powerpc64le-unknown-linux-gnu" => "linux-ppc64le", "s390x-unknown-linux-gnu" => "linux64-s390x", diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 2872c59157d6b..42ae2fc13ae05 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -186,6 +186,7 @@ supported_targets! { ("x86_64-unknown-openbsd", x86_64_unknown_openbsd), ("i686-unknown-netbsd", i686_unknown_netbsd), + ("powerpc-unknown-netbsd", powerpc_unknown_netbsd), ("sparc64-unknown-netbsd", sparc64_unknown_netbsd), ("x86_64-unknown-netbsd", x86_64_unknown_netbsd), ("x86_64-rumprun-netbsd", x86_64_rumprun_netbsd), diff --git a/src/librustc_back/target/powerpc_unknown_netbsd.rs b/src/librustc_back/target/powerpc_unknown_netbsd.rs new file mode 100644 index 0000000000000..2c78dbd206171 --- /dev/null +++ b/src/librustc_back/target/powerpc_unknown_netbsd.rs @@ -0,0 +1,35 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use LinkerFlavor; +use target::{Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::netbsd_base::opts(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.max_atomic_width = Some(32); + + // see #36994 + base.exe_allocation_crate = None; + + Ok(Target { + llvm_target: "powerpc-unknown-netbsd".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), + arch: "powerpc".to_string(), + target_os: "netbsd".to_string(), + target_env: "".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} From b1f04a3a2e1a01ea1b77153dd8d22e7837542aa0 Mon Sep 17 00:00:00 2001 From: Vitali Lovich <vlovich@google.com> Date: Thu, 15 Feb 2018 09:28:25 -0800 Subject: [PATCH 20/46] Fix unit test compilation Also fix some code snippets in documentation. --- src/libstd/sync/condvar.rs | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 98fadbd35439a..546e105deb7e6 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -244,6 +244,8 @@ impl Condvar { /// # Examples /// /// ``` + /// #![feature(wait_until)] + /// /// use std::sync::{Arc, Mutex, Condvar}; /// use std::thread; /// @@ -261,7 +263,7 @@ impl Condvar { /// // Wait for the thread to start up. /// let &(ref lock, ref cvar) = &*pair; /// // As long as the value inside the `Mutex` is false, we wait. - /// cvar.wait_until(lock.lock().unwrap(), |started| { started }); + /// let _guard = cvar.wait_until(lock.lock().unwrap(), |started| { *started }).unwrap(); /// ``` #[unstable(feature = "wait_until", issue = "47960")] pub fn wait_until<'a, T, F>(&self, mut guard: MutexGuard<'a, T>, @@ -445,6 +447,8 @@ impl Condvar { /// # Examples /// /// ``` + /// #![feature(wait_timeout_until)] + /// /// use std::sync::{Arc, Mutex, Condvar}; /// use std::thread; /// use std::time::Duration; @@ -462,8 +466,8 @@ impl Condvar { /// /// // wait for the thread to start up /// let &(ref lock, ref cvar) = &*pair; - /// let result = cvar.wait_timeout_until(lock, Duration::from_millis(100), |started| { - /// started + /// let result = cvar.wait_timeout_until(lock.lock().unwrap(), Duration::from_millis(100), |started| { + /// *started /// }).unwrap(); /// if result.1.timed_out() { /// // timed-out without the condition ever evaluating to true. @@ -613,6 +617,7 @@ impl Drop for Condvar { #[cfg(test)] mod tests { + /// #![feature(wait_until)] use sync::mpsc::channel; use sync::{Condvar, Mutex, Arc}; use sync::atomic::{AtomicBool, Ordering}; @@ -699,9 +704,9 @@ mod tests { // Wait for the thread to start up. let &(ref lock, ref cvar) = &*pair; let guard = cvar.wait_until(lock.lock().unwrap(), |started| { - started + *started }); - assert!(*guard); + assert!(*guard.unwrap()); } #[test] @@ -730,7 +735,7 @@ mod tests { let c = Arc::new(Condvar::new()); let g = m.lock().unwrap(); - let (_g, wait) = c.wait_timeout_until(g, Duration::from_millis(1), || { false }).unwrap(); + let (_g, wait) = c.wait_timeout_until(g, Duration::from_millis(1), |_| { false }).unwrap(); // no spurious wakeups. ensure it timed-out assert!(wait.timed_out()); } @@ -742,7 +747,7 @@ mod tests { let c = Arc::new(Condvar::new()); let g = m.lock().unwrap(); - let (_g, wait) = c.wait_timeout_until(g, Duration::from_millis(0), || { true }).unwrap(); + let (_g, wait) = c.wait_timeout_until(g, Duration::from_millis(0), |_| { true }).unwrap(); // ensure it didn't time-out even if we were not given any time. assert!(!wait.timed_out()); } @@ -753,15 +758,16 @@ mod tests { let pair = Arc::new((Mutex::new(false), Condvar::new())); let pair_copy = pair.clone(); + let &(ref m, ref c) = &*pair; let g = m.lock().unwrap(); - let t = thread::spawn(move || { - let &(ref lock, ref cvar) = &*pair2; + let _t = thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair_copy; let mut started = lock.lock().unwrap(); thread::sleep(Duration::from_millis(1)); - started = true; + *started = true; cvar.notify_one(); }); - let (g2, wait) = c.wait_timeout_until(g, Duration::from_millis(u64::MAX), |¬ified| { + let (g2, wait) = c.wait_timeout_until(g, Duration::from_millis(u64::MAX), |&mut notified| { notified }).unwrap(); // ensure it didn't time-out even if we were not given any time. From d549db8031a07b79009f9efe8b3233cd8900c82b Mon Sep 17 00:00:00 2001 From: Vitali Lovich <vlovich@google.com> Date: Sat, 17 Feb 2018 09:17:58 -0800 Subject: [PATCH 21/46] Fix tidy violation --- src/libstd/sync/condvar.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 546e105deb7e6..7f9b566762884 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -466,9 +466,11 @@ impl Condvar { /// /// // wait for the thread to start up /// let &(ref lock, ref cvar) = &*pair; - /// let result = cvar.wait_timeout_until(lock.lock().unwrap(), Duration::from_millis(100), |started| { - /// *started - /// }).unwrap(); + /// let result = cvar.wait_timeout_until( + /// lock.lock().unwrap(), + /// Duration::from_millis(100), + /// |started| started, + /// ).unwrap(); /// if result.1.timed_out() { /// // timed-out without the condition ever evaluating to true. /// } From 6ad328ca772edc2474844a35133a6f902a80bccd Mon Sep 17 00:00:00 2001 From: Mark Mansi <markm@cs.wisc.edu> Date: Sat, 17 Feb 2018 11:42:11 -0600 Subject: [PATCH 22/46] Move macro-at-most-once-rep-ambig test to ui test --- .../macros}/macro-at-most-once-rep-ambig.rs | 0 .../macro-at-most-once-rep-ambig.stderr | 80 +++++++++++++++++++ 2 files changed, 80 insertions(+) rename src/test/{compile-fail => ui/macros}/macro-at-most-once-rep-ambig.rs (100%) create mode 100644 src/test/ui/macros/macro-at-most-once-rep-ambig.stderr diff --git a/src/test/compile-fail/macro-at-most-once-rep-ambig.rs b/src/test/ui/macros/macro-at-most-once-rep-ambig.rs similarity index 100% rename from src/test/compile-fail/macro-at-most-once-rep-ambig.rs rename to src/test/ui/macros/macro-at-most-once-rep-ambig.rs diff --git a/src/test/ui/macros/macro-at-most-once-rep-ambig.stderr b/src/test/ui/macros/macro-at-most-once-rep-ambig.stderr new file mode 100644 index 0000000000000..67a77e0a481d8 --- /dev/null +++ b/src/test/ui/macros/macro-at-most-once-rep-ambig.stderr @@ -0,0 +1,80 @@ +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:40:11 + | +40 | foo!(a?a?a); //~ ERROR no rules expected the token `?` + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:41:11 + | +41 | foo!(a?a); //~ ERROR no rules expected the token `?` + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:42:11 + | +42 | foo!(a?); //~ ERROR no rules expected the token `?` + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:43:11 + | +43 | baz!(a?a?a); //~ ERROR no rules expected the token `?` + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:44:11 + | +44 | baz!(a?a); //~ ERROR no rules expected the token `?` + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:45:11 + | +45 | baz!(a?); //~ ERROR no rules expected the token `?` + | ^ + +error: unexpected end of macro invocation + --> $DIR/macro-at-most-once-rep-ambig.rs:46:11 + | +46 | baz!(a,); //~ ERROR unexpected end of macro invocation + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:47:11 + | +47 | baz!(a?a?a,); //~ ERROR no rules expected the token `?` + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:48:11 + | +48 | baz!(a?a,); //~ ERROR no rules expected the token `?` + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:49:11 + | +49 | baz!(a?,); //~ ERROR no rules expected the token `?` + | ^ + +error: unexpected end of macro invocation + --> $DIR/macro-at-most-once-rep-ambig.rs:50:5 + | +50 | barplus!(); //~ ERROR unexpected end of macro invocation + | ^^^^^^^^^^^ + +error: unexpected end of macro invocation + --> $DIR/macro-at-most-once-rep-ambig.rs:51:15 + | +51 | barplus!(a?); //~ ERROR unexpected end of macro invocation + | ^ + +error: unexpected end of macro invocation + --> $DIR/macro-at-most-once-rep-ambig.rs:52:15 + | +52 | barstar!(a?); //~ ERROR unexpected end of macro invocation + | ^ + +error: aborting due to 13 previous errors + From d17d645ad75c797a293ccf1fa3881853617f292c Mon Sep 17 00:00:00 2001 From: Corey Farwell <coreyf@rwell.org> Date: Sun, 18 Feb 2018 16:08:48 -0500 Subject: [PATCH 23/46] Add tests ensuring zero-Duration timeouts result in errors. Part of https://github.com/rust-lang/rust/issues/48311 --- src/libstd/net/tcp.rs | 20 +++++++++++++++++ src/libstd/net/udp.rs | 17 ++++++++++++++ src/libstd/sys/unix/ext/net.rs | 41 +++++++++++++++++++++++++++++++++- 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index 78235ea1b4b5f..ee63e185ddb23 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -1545,6 +1545,26 @@ mod tests { drop(listener); } + // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors + // when passed zero Durations + #[test] + fn test_timeout_zero_duration() { + let addr = next_test_ip4(); + + let listener = t!(TcpListener::bind(&addr)); + let stream = t!(TcpStream::connect(&addr)); + + let result = stream.set_write_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + let result = stream.set_read_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + drop(listener); + } + #[test] fn nodelay() { let addr = next_test_ip4(); diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index fc7f9205d06ff..4163bec000bb5 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -1024,6 +1024,23 @@ mod tests { assert!(start.elapsed() > Duration::from_millis(400)); } + // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors + // when passed zero Durations + #[test] + fn test_timeout_zero_duration() { + let addr = next_test_ip4(); + + let socket = t!(UdpSocket::bind(&addr)); + + let result = socket.set_write_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + let result = socket.set_read_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + } + #[test] fn connect_send_recv() { let addr = next_test_ip4(); diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs index 86b0f35be924d..f1bf8f240d3fb 100644 --- a/src/libstd/sys/unix/ext/net.rs +++ b/src/libstd/sys/unix/ext/net.rs @@ -1410,7 +1410,7 @@ impl IntoRawFd for UnixDatagram { #[cfg(all(test, not(target_os = "emscripten")))] mod test { use thread; - use io; + use io::{self, ErrorKind}; use io::prelude::*; use time::Duration; use sys_common::io::test::tmpdir; @@ -1613,6 +1613,27 @@ mod test { assert!(kind == io::ErrorKind::WouldBlock || kind == io::ErrorKind::TimedOut); } + // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors + // when passed zero Durations + #[test] + fn test_unix_stream_timeout_zero_duration() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + + let listener = or_panic!(UnixListener::bind(&socket_path)); + let stream = or_panic!(UnixStream::connect(&socket_path)); + + let result = stream.set_write_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + let result = stream.set_read_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + drop(listener); + } + #[test] fn test_unix_datagram() { let dir = tmpdir(); @@ -1712,6 +1733,24 @@ mod test { thread.join().unwrap(); } + // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors + // when passed zero Durations + #[test] + fn test_unix_datagram_timeout_zero_duration() { + let dir = tmpdir(); + let path = dir.path().join("sock"); + + let datagram = or_panic!(UnixDatagram::bind(&path)); + + let result = datagram.set_write_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + let result = datagram.set_read_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + } + #[test] fn abstract_namespace_not_allowed() { assert!(UnixStream::connect("\0asdf").is_err()); From c0e87f13a4d2f6fcef92c8c01bdc8dda9e0549a5 Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Thu, 15 Feb 2018 15:54:40 +0000 Subject: [PATCH 24/46] Make ".e0" not parse as 0.0 This forces floats to have either a digit before the separating point, or after. Thus ".e0" is invalid like ".", when using `parse()`. --- src/libcore/num/dec2flt/parse.rs | 3 ++- src/libcore/tests/num/dec2flt/mod.rs | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libcore/num/dec2flt/parse.rs b/src/libcore/num/dec2flt/parse.rs index d20986faa0fc2..69418434ebead 100644 --- a/src/libcore/num/dec2flt/parse.rs +++ b/src/libcore/num/dec2flt/parse.rs @@ -73,7 +73,8 @@ pub fn parse_decimal(s: &str) -> ParseResult { } Some(&b'.') => { let (fractional, s) = eat_digits(&s[1..]); - if integral.is_empty() && fractional.is_empty() && s.is_empty() { + if integral.is_empty() && fractional.is_empty() { + // We require at least a single digit before or after the point. return Invalid; } diff --git a/src/libcore/tests/num/dec2flt/mod.rs b/src/libcore/tests/num/dec2flt/mod.rs index 9934e1dab9662..17b2f59cd4df2 100644 --- a/src/libcore/tests/num/dec2flt/mod.rs +++ b/src/libcore/tests/num/dec2flt/mod.rs @@ -101,6 +101,12 @@ fn lonely_dot() { assert!(".".parse::<f64>().is_err()); } +#[test] +fn exponentiated_dot() { + assert!(".e0".parse::<f32>().is_err()); + assert!(".e0".parse::<f64>().is_err()); +} + #[test] fn lonely_sign() { assert!("+".parse::<f32>().is_err()); From 80970e6953faaba886215d8abd34e70f1d510103 Mon Sep 17 00:00:00 2001 From: Josh Stone <jistone@redhat.com> Date: Mon, 19 Feb 2018 14:05:21 -0800 Subject: [PATCH 25/46] rustbuild: Restore Config.libdir_relative This re-introduces a `Config.libdir_relative` field, now derived from `libdir` and made relative to `prefix` if necessary. This fixes a regression from #46592 when `--libdir` is given an absolute path. `Builder::sysroot_libdir` should always use a relative path so its callers don't clobber system locations, and `librustc` also asserts that `CFG_LIBDIR_RELATIVE` is really relative. --- src/bootstrap/builder.rs | 4 ++-- src/bootstrap/compile.rs | 2 +- src/bootstrap/config.rs | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 66a1c97246200..05aa6283ee579 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -444,8 +444,8 @@ impl<'a> Builder<'a> { fn run(self, builder: &Builder) -> Interned<PathBuf> { let compiler = self.compiler; - let lib = if compiler.stage >= 1 && builder.build.config.libdir.is_some() { - builder.build.config.libdir.clone().unwrap() + let lib = if compiler.stage >= 1 && builder.build.config.libdir_relative.is_some() { + builder.build.config.libdir_relative.clone().unwrap() } else { PathBuf::from("lib") }; diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 2dcc0e0e7cd9f..e6389f27785b5 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -517,7 +517,7 @@ fn rustc_cargo_env(build: &Build, cargo: &mut Command) { .env("CFG_PREFIX", build.config.prefix.clone().unwrap_or_default()); let libdir_relative = - build.config.libdir.clone().unwrap_or(PathBuf::from("lib")); + build.config.libdir_relative.clone().unwrap_or(PathBuf::from("lib")); cargo.env("CFG_LIBDIR_RELATIVE", libdir_relative); // If we're not building a compiler with debugging information then remove diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 812ca6d64fb6a..6d98153e233ba 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -126,6 +126,7 @@ pub struct Config { pub docdir: Option<PathBuf>, pub bindir: Option<PathBuf>, pub libdir: Option<PathBuf>, + pub libdir_relative: Option<PathBuf>, pub mandir: Option<PathBuf>, pub codegen_tests: bool, pub nodejs: Option<PathBuf>, @@ -417,6 +418,22 @@ impl Config { config.mandir = install.mandir.clone().map(PathBuf::from); } + // Try to infer `libdir_relative` from `libdir`. + if let Some(ref libdir) = config.libdir { + let mut libdir = libdir.as_path(); + if !libdir.is_relative() { + // Try to make it relative to the prefix. + if let Some(ref prefix) = config.prefix { + if let Ok(suffix) = libdir.strip_prefix(prefix) { + libdir = suffix; + } + } + } + if libdir.is_relative() { + config.libdir_relative = Some(libdir.to_path_buf()); + } + } + // Store off these values as options because if they're not provided // we'll infer default values for them later let mut thinlto = None; From 8174c0d65932dd6d7845af9b715090f57417b641 Mon Sep 17 00:00:00 2001 From: Josh Stone <jistone@redhat.com> Date: Mon, 19 Feb 2018 16:08:36 -0800 Subject: [PATCH 26/46] rustbuild: make libdir_relative a method --- src/bootstrap/builder.rs | 7 ++++--- src/bootstrap/compile.rs | 3 +-- src/bootstrap/config.rs | 30 ++++++++++++------------------ 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 05aa6283ee579..fcb78c479fa27 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -444,10 +444,11 @@ impl<'a> Builder<'a> { fn run(self, builder: &Builder) -> Interned<PathBuf> { let compiler = self.compiler; - let lib = if compiler.stage >= 1 && builder.build.config.libdir_relative.is_some() { - builder.build.config.libdir_relative.clone().unwrap() + let config = &builder.build.config; + let lib = if compiler.stage >= 1 && config.libdir_relative().is_some() { + builder.build.config.libdir_relative().unwrap() } else { - PathBuf::from("lib") + Path::new("lib") }; let sysroot = builder.sysroot(self.compiler).join(lib) .join("rustlib").join(self.target).join("lib"); diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index e6389f27785b5..c85b04ddc0245 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -516,8 +516,7 @@ fn rustc_cargo_env(build: &Build, cargo: &mut Command) { .env("CFG_VERSION", build.rust_version()) .env("CFG_PREFIX", build.config.prefix.clone().unwrap_or_default()); - let libdir_relative = - build.config.libdir_relative.clone().unwrap_or(PathBuf::from("lib")); + let libdir_relative = build.config.libdir_relative().unwrap_or(Path::new("lib")); cargo.env("CFG_LIBDIR_RELATIVE", libdir_relative); // If we're not building a compiler with debugging information then remove diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 6d98153e233ba..3cf8f36df25ee 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -17,7 +17,7 @@ use std::collections::{HashMap, HashSet}; use std::env; use std::fs::File; use std::io::prelude::*; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process; use std::cmp; @@ -126,7 +126,6 @@ pub struct Config { pub docdir: Option<PathBuf>, pub bindir: Option<PathBuf>, pub libdir: Option<PathBuf>, - pub libdir_relative: Option<PathBuf>, pub mandir: Option<PathBuf>, pub codegen_tests: bool, pub nodejs: Option<PathBuf>, @@ -418,22 +417,6 @@ impl Config { config.mandir = install.mandir.clone().map(PathBuf::from); } - // Try to infer `libdir_relative` from `libdir`. - if let Some(ref libdir) = config.libdir { - let mut libdir = libdir.as_path(); - if !libdir.is_relative() { - // Try to make it relative to the prefix. - if let Some(ref prefix) = config.prefix { - if let Ok(suffix) = libdir.strip_prefix(prefix) { - libdir = suffix; - } - } - } - if libdir.is_relative() { - config.libdir_relative = Some(libdir.to_path_buf()); - } - } - // Store off these values as options because if they're not provided // we'll infer default values for them later let mut thinlto = None; @@ -581,6 +564,17 @@ impl Config { config } + /// Try to find the relative path of `libdir`. + pub fn libdir_relative(&self) -> Option<&Path> { + let libdir = self.libdir.as_ref()?; + if libdir.is_relative() { + Some(libdir) + } else { + // Try to make it relative to the prefix. + libdir.strip_prefix(self.prefix.as_ref()?).ok() + } + } + pub fn verbose(&self) -> bool { self.verbose > 0 } From 6af23f977c44fc67d8611b2581c334e795999bcd Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad <twingoow@gmail.com> Date: Tue, 20 Feb 2018 08:26:30 +0100 Subject: [PATCH 27/46] add Iterator::flatten and redefine flat_map(f) in terms of map(f).flatten() --- src/libcore/iter/iterator.rs | 47 ++++++++++++++++++++- src/libcore/iter/mod.rs | 81 ++++++++++++++++-------------------- src/libcore/lib.rs | 1 + src/libcore/tests/iter.rs | 2 + 4 files changed, 84 insertions(+), 47 deletions(-) diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index 33adb3f49dd0d..8ed3450dc3ac4 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -12,7 +12,7 @@ use cmp::Ordering; use ops::Try; use super::{AlwaysOk, LoopState}; -use super::{Chain, Cycle, Cloned, Enumerate, Filter, FilterMap, FlatMap, Fuse}; +use super::{Chain, Cycle, Cloned, Enumerate, Filter, FilterMap, Flatten, FlatMap, Fuse}; use super::{Inspect, Map, Peekable, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, Rev}; use super::{Zip, Sum, Product}; use super::{ChainState, FromIterator, ZipImpl}; @@ -997,11 +997,15 @@ pub trait Iterator { /// an extra layer of indirection. `flat_map()` will remove this extra layer /// on its own. /// + /// You can think of [`flat_map(f)`][flat_map] as the equivalent of + /// [`map`]ping, and then [`flatten`]ing as in `map(f).flatten()`. + /// /// Another way of thinking about `flat_map()`: [`map`]'s closure returns /// one item for each element, and `flat_map()`'s closure returns an /// iterator for each element. /// /// [`map`]: #method.map + /// [`flatten`]: #method.flatten /// /// # Examples /// @@ -1021,7 +1025,46 @@ pub trait Iterator { fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F> where Self: Sized, U: IntoIterator, F: FnMut(Self::Item) -> U, { - FlatMap{iter: self, f: f, frontiter: None, backiter: None } + self.map(f).flatten() + } + + /// Creates an iterator that flattens nested structure. + /// + /// This is useful when you have an iterator of iterators or an iterator of + /// things that can be turned into iterators and you want to remove one + /// level of indirection. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iterator_flatten)] + /// + /// let data = vec![vec![1, 2, 3, 4], vec![5, 6]]; + /// let flattened = data.into_iter().flatten().collect::<Vec<u8>>(); + /// assert_eq!(flattened, &[1, 2, 3, 4, 5, 6]); + /// ``` + /// + /// Mapping and then flattening: + /// + /// ``` + /// #![feature(iterator_flatten)] + /// + /// let words = ["alpha", "beta", "gamma"]; + /// + /// // chars() returns an iterator + /// let merged: String = words.iter() + /// .map(|s| s.chars()) + /// .flatten() + /// .collect(); + /// assert_eq!(merged, "alphabetagamma"); + /// ``` + #[inline] + #[unstable(feature = "iterator_flatten", issue = "0")] + fn flatten(self) -> Flatten<Self, <Self::Item as IntoIterator>::IntoIter> + where Self: Sized, Self::Item: IntoIterator { + Flatten { iter: self, frontiter: None, backiter: None } } /// Creates an iterator which ends after the first [`None`]. diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 1a2da83429af9..bd801d2ae69c8 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -2403,37 +2403,35 @@ impl<B, I, St, F> Iterator for Scan<I, St, F> where /// An iterator that maps each element to an iterator, and yields the elements /// of the produced iterators. /// -/// This `struct` is created by the [`flat_map`] method on [`Iterator`]. See its +/// This `type` is created by the [`flat_map`] method on [`Iterator`]. See its /// documentation for more. /// /// [`flat_map`]: trait.Iterator.html#method.flat_map /// [`Iterator`]: trait.Iterator.html #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone)] -pub struct FlatMap<I, U: IntoIterator, F> { - iter: I, - f: F, - frontiter: Option<U::IntoIter>, - backiter: Option<U::IntoIter>, -} +type FlatMap<I, U, F> = Flatten<Map<I, F>, <U as IntoIterator>::IntoIter>; -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl<I: fmt::Debug, U: IntoIterator, F> fmt::Debug for FlatMap<I, U, F> - where U::IntoIter: fmt::Debug -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("FlatMap") - .field("iter", &self.iter) - .field("frontiter", &self.frontiter) - .field("backiter", &self.backiter) - .finish() - } +/// An iterator that flattens one level of nesting in an iterator of things +/// that can be turned into iterators. +/// +/// This `struct` is created by the [`flatten`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`flatten`]: trait.Iterator.html#method.flatten +/// [`Iterator`]: trait.Iterator.html +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[unstable(feature = "iterator_flatten", issue = "0")] +#[derive(Clone, Debug)] +pub struct Flatten<I, U> { + iter: I, + frontiter: Option<U>, + backiter: Option<U>, } -#[stable(feature = "rust1", since = "1.0.0")] -impl<I: Iterator, U: IntoIterator, F> Iterator for FlatMap<I, U, F> - where F: FnMut(I::Item) -> U, +#[unstable(feature = "iterator_flatten", issue = "0")] +impl<I: Iterator, U: Iterator> Iterator for Flatten<I, U> + where I::Item: IntoIterator<IntoIter = U, Item = U::Item> { type Item = U::Item; @@ -2441,13 +2439,11 @@ impl<I: Iterator, U: IntoIterator, F> Iterator for FlatMap<I, U, F> fn next(&mut self) -> Option<U::Item> { loop { if let Some(ref mut inner) = self.frontiter { - if let Some(x) = inner.by_ref().next() { - return Some(x) - } + if let elt@Some(_) = inner.next() { return elt } } - match self.iter.next().map(&mut self.f) { + match self.iter.next() { None => return self.backiter.as_mut().and_then(|it| it.next()), - next => self.frontiter = next.map(IntoIterator::into_iter), + Some(inner) => self.frontiter = Some(inner.into_iter()), } } } @@ -2473,10 +2469,9 @@ impl<I: Iterator, U: IntoIterator, F> Iterator for FlatMap<I, U, F> self.frontiter = None; { - let f = &mut self.f; let frontiter = &mut self.frontiter; init = self.iter.try_fold(init, |acc, x| { - let mut mid = f(x).into_iter(); + let mut mid = x.into_iter(); let r = mid.try_fold(acc, &mut fold); *frontiter = Some(mid); r @@ -2497,27 +2492,24 @@ impl<I: Iterator, U: IntoIterator, F> Iterator for FlatMap<I, U, F> where Fold: FnMut(Acc, Self::Item) -> Acc, { self.frontiter.into_iter() - .chain(self.iter.map(self.f).map(U::into_iter)) + .chain(self.iter.map(IntoIterator::into_iter)) .chain(self.backiter) .fold(init, |acc, iter| iter.fold(acc, &mut fold)) } } -#[stable(feature = "rust1", since = "1.0.0")] -impl<I: DoubleEndedIterator, U, F> DoubleEndedIterator for FlatMap<I, U, F> where - F: FnMut(I::Item) -> U, - U: IntoIterator, - U::IntoIter: DoubleEndedIterator +#[unstable(feature = "iterator_flatten", issue = "0")] +impl<I, U> DoubleEndedIterator for Flatten<I, U> + where I: DoubleEndedIterator, U: DoubleEndedIterator, + I::Item: IntoIterator<IntoIter = U, Item = U::Item> { #[inline] fn next_back(&mut self) -> Option<U::Item> { loop { if let Some(ref mut inner) = self.backiter { - if let Some(y) = inner.next_back() { - return Some(y) - } + if let elt@Some(_) = inner.next_back() { return elt } } - match self.iter.next_back().map(&mut self.f) { + match self.iter.next_back() { None => return self.frontiter.as_mut().and_then(|it| it.next_back()), next => self.backiter = next.map(IntoIterator::into_iter), } @@ -2534,10 +2526,9 @@ impl<I: DoubleEndedIterator, U, F> DoubleEndedIterator for FlatMap<I, U, F> wher self.backiter = None; { - let f = &mut self.f; let backiter = &mut self.backiter; init = self.iter.try_rfold(init, |acc, x| { - let mut mid = f(x).into_iter(); + let mut mid = x.into_iter(); let r = mid.try_rfold(acc, &mut fold); *backiter = Some(mid); r @@ -2558,15 +2549,15 @@ impl<I: DoubleEndedIterator, U, F> DoubleEndedIterator for FlatMap<I, U, F> wher where Fold: FnMut(Acc, Self::Item) -> Acc, { self.frontiter.into_iter() - .chain(self.iter.map(self.f).map(U::into_iter)) + .chain(self.iter.map(IntoIterator::into_iter)) .chain(self.backiter) .rfold(init, |acc, iter| iter.rfold(acc, &mut fold)) } } -#[unstable(feature = "fused", issue = "35602")] -impl<I, U, F> FusedIterator for FlatMap<I, U, F> - where I: FusedIterator, U: IntoIterator, F: FnMut(I::Item) -> U {} +#[unstable(feature = "fused", issue = "0")] +impl<I: FusedIterator, U: Iterator> FusedIterator for Flatten<I, U> + where I::Item: IntoIterator<IntoIter = U, Item = U::Item> {} /// An iterator that yields `None` forever after the underlying iterator /// yields `None` once. diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index d2162d307e038..3dd30ee1c69e2 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -93,6 +93,7 @@ #![feature(doc_spotlight)] #![feature(rustc_const_unstable)] #![feature(iterator_repeat_with)] +#![feature(iterator_flatten)] #[prelude_import] #[allow(unused)] diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index f91c919d7447d..f28f5ef181ce9 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -836,6 +836,8 @@ fn test_iterator_scan() { assert_eq!(i, ys.len()); } +// Note: We test flatten() by testing flat_map(). + #[test] fn test_iterator_flat_map() { let xs = [0, 3, 6]; From 36be763d0e5b0e6ceb7cbf55097427a269caec1a Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad <twingoow@gmail.com> Date: Sat, 10 Feb 2018 07:22:46 +0100 Subject: [PATCH 28/46] Iterator::flatten: fix tracking issue number on FusedIterator for Flatten --- src/libcore/iter/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index bd801d2ae69c8..cd823d1028ed6 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -2555,7 +2555,7 @@ impl<I, U> DoubleEndedIterator for Flatten<I, U> } } -#[unstable(feature = "fused", issue = "0")] +#[unstable(feature = "fused", issue = "35602")] impl<I: FusedIterator, U: Iterator> FusedIterator for Flatten<I, U> where I::Item: IntoIterator<IntoIter = U, Item = U::Item> {} From 0e394010e6ad3d6fa68c9b8c651d4745348881cd Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad <twingoow@gmail.com> Date: Wed, 14 Feb 2018 00:58:14 +0100 Subject: [PATCH 29/46] core::iter::Flatten: update FlatMap & Flatten according to discussion --- src/libcore/iter/iterator.rs | 26 +++-- src/libcore/iter/mod.rs | 178 ++++++++++++++++++++++++++++++++--- src/libcore/tests/iter.rs | 108 ++++++++++++++++++++- src/libcore/tests/lib.rs | 2 + 4 files changed, 294 insertions(+), 20 deletions(-) diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index 8ed3450dc3ac4..879696ba8e793 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -12,7 +12,8 @@ use cmp::Ordering; use ops::Try; use super::{AlwaysOk, LoopState}; -use super::{Chain, Cycle, Cloned, Enumerate, Filter, FilterMap, Flatten, FlatMap, Fuse}; +use super::{Chain, Cycle, Cloned, Enumerate, Filter, FilterMap, Fuse}; +use super::{Flatten, FlatMap, flatten_compat}; use super::{Inspect, Map, Peekable, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, Rev}; use super::{Zip, Sum, Product}; use super::{ChainState, FromIterator, ZipImpl}; @@ -997,8 +998,8 @@ pub trait Iterator { /// an extra layer of indirection. `flat_map()` will remove this extra layer /// on its own. /// - /// You can think of [`flat_map(f)`][flat_map] as the equivalent of - /// [`map`]ping, and then [`flatten`]ing as in `map(f).flatten()`. + /// You can think of [`flat_map(f)`][flat_map] as the semantic equivalent + /// of [`map`]ping, and then [`flatten`]ing as in `map(f).flatten()`. /// /// Another way of thinking about `flat_map()`: [`map`]'s closure returns /// one item for each element, and `flat_map()`'s closure returns an @@ -1025,7 +1026,7 @@ pub trait Iterator { fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F> where Self: Sized, U: IntoIterator, F: FnMut(Self::Item) -> U, { - self.map(f).flatten() + FlatMap { inner: flatten_compat(self.map(f)) } } /// Creates an iterator that flattens nested structure. @@ -1060,11 +1061,24 @@ pub trait Iterator { /// .collect(); /// assert_eq!(merged, "alphabetagamma"); /// ``` + /// + /// You can also rewrite this in terms of [`flat_map()`] which is preferable + /// in this case since that conveys intent clearer: + /// + /// ``` + /// let words = ["alpha", "beta", "gamma"]; + /// + /// // chars() returns an iterator + /// let merged: String = words.iter() + /// .flat_map(|s| s.chars()) + /// .collect(); + /// assert_eq!(merged, "alphabetagamma"); + /// ``` #[inline] #[unstable(feature = "iterator_flatten", issue = "0")] - fn flatten(self) -> Flatten<Self, <Self::Item as IntoIterator>::IntoIter> + fn flatten(self) -> Flatten<Self> where Self: Sized, Self::Item: IntoIterator { - Flatten { iter: self, frontiter: None, backiter: None } + Flatten { inner: flatten_compat(self) } } /// Creates an iterator which ends after the first [`None`]. diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index cd823d1028ed6..e498f7d1b93c5 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -2403,14 +2403,87 @@ impl<B, I, St, F> Iterator for Scan<I, St, F> where /// An iterator that maps each element to an iterator, and yields the elements /// of the produced iterators. /// -/// This `type` is created by the [`flat_map`] method on [`Iterator`]. See its +/// This `struct` is created by the [`flat_map`] method on [`Iterator`]. See its /// documentation for more. /// /// [`flat_map`]: trait.Iterator.html#method.flat_map /// [`Iterator`]: trait.Iterator.html #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] -type FlatMap<I, U, F> = Flatten<Map<I, F>, <U as IntoIterator>::IntoIter>; +pub struct FlatMap<I, U: IntoIterator, F> { + inner: FlattenCompat<Map<I, F>, <U as IntoIterator>::IntoIter> +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<I: Clone, U: Clone + IntoIterator, F: Clone> Clone for FlatMap<I, U, F> + where <U as IntoIterator>::IntoIter: Clone +{ + fn clone(&self) -> Self { FlatMap { inner: self.inner.clone() } } +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl<I: fmt::Debug, U: IntoIterator, F> fmt::Debug for FlatMap<I, U, F> + where U::IntoIter: fmt::Debug +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("FlatMap").field("inner", &self.inner).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<I: Iterator, U: IntoIterator, F> Iterator for FlatMap<I, U, F> + where F: FnMut(I::Item) -> U, +{ + type Item = U::Item; + + #[inline] + fn next(&mut self) -> Option<U::Item> { self.inner.next() } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() } + + #[inline] + fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try<Ok=Acc> + { + self.inner.try_fold(init, fold) + } + + #[inline] + fn fold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.inner.fold(init, fold) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<I: DoubleEndedIterator, U, F> DoubleEndedIterator for FlatMap<I, U, F> + where F: FnMut(I::Item) -> U, + U: IntoIterator, + U::IntoIter: DoubleEndedIterator +{ + #[inline] + fn next_back(&mut self) -> Option<U::Item> { self.inner.next_back() } + + #[inline] + fn try_rfold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try<Ok=Acc> + { + self.inner.try_rfold(init, fold) + } + + #[inline] + fn rfold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.inner.rfold(init, fold) + } +} + +#[unstable(feature = "fused", issue = "35602")] +impl<I, U, F> FusedIterator for FlatMap<I, U, F> + where I: FusedIterator, U: IntoIterator, F: FnMut(I::Item) -> U {} /// An iterator that flattens one level of nesting in an iterator of things /// that can be turned into iterators. @@ -2422,16 +2495,102 @@ type FlatMap<I, U, F> = Flatten<Map<I, F>, <U as IntoIterator>::IntoIter>; /// [`Iterator`]: trait.Iterator.html #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[unstable(feature = "iterator_flatten", issue = "0")] +pub struct Flatten<I: Iterator> +where I::Item: IntoIterator { + inner: FlattenCompat<I, <I::Item as IntoIterator>::IntoIter>, +} + +#[unstable(feature = "iterator_flatten", issue = "0")] +impl<I, U> fmt::Debug for Flatten<I> + where I: Iterator + fmt::Debug, U: Iterator + fmt::Debug, + I::Item: IntoIterator<IntoIter = U, Item = U::Item>, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Flatten").field("inner", &self.inner).finish() + } +} + +#[unstable(feature = "iterator_flatten", issue = "0")] +impl<I, U> Clone for Flatten<I> + where I: Iterator + Clone, U: Iterator + Clone, + I::Item: IntoIterator<IntoIter = U, Item = U::Item>, +{ + fn clone(&self) -> Self { Flatten { inner: self.inner.clone() } } +} + +#[unstable(feature = "iterator_flatten", issue = "0")] +impl<I, U> Iterator for Flatten<I> + where I: Iterator, U: Iterator, + I::Item: IntoIterator<IntoIter = U, Item = U::Item> +{ + type Item = U::Item; + + #[inline] + fn next(&mut self) -> Option<U::Item> { self.inner.next() } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() } + + #[inline] + fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try<Ok=Acc> + { + self.inner.try_fold(init, fold) + } + + #[inline] + fn fold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.inner.fold(init, fold) + } +} + +#[unstable(feature = "iterator_flatten", issue = "0")] +impl<I, U> DoubleEndedIterator for Flatten<I> + where I: DoubleEndedIterator, U: DoubleEndedIterator, + I::Item: IntoIterator<IntoIter = U, Item = U::Item> +{ + #[inline] + fn next_back(&mut self) -> Option<U::Item> { self.inner.next_back() } + + #[inline] + fn try_rfold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try<Ok=Acc> + { + self.inner.try_rfold(init, fold) + } + + #[inline] + fn rfold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.inner.rfold(init, fold) + } +} + +#[unstable(feature = "fused", issue = "35602")] +impl<I, U> FusedIterator for Flatten<I> + where I: FusedIterator, U: Iterator, + I::Item: IntoIterator<IntoIter = U, Item = U::Item> {} + +/// Adapts an iterator by flattening it, for use in `flatten()` and `flat_map()`. +fn flatten_compat<I, U>(iter: I) -> FlattenCompat<I, U> { + FlattenCompat { iter, frontiter: None, backiter: None } +} + +/// Real logic of both `Flatten` and `FlatMap` which simply delegate to +/// this type. #[derive(Clone, Debug)] -pub struct Flatten<I, U> { +struct FlattenCompat<I, U> { iter: I, frontiter: Option<U>, backiter: Option<U>, } -#[unstable(feature = "iterator_flatten", issue = "0")] -impl<I: Iterator, U: Iterator> Iterator for Flatten<I, U> - where I::Item: IntoIterator<IntoIter = U, Item = U::Item> +impl<I, U> Iterator for FlattenCompat<I, U> + where I: Iterator, U: Iterator, + I::Item: IntoIterator<IntoIter = U, Item = U::Item> { type Item = U::Item; @@ -2498,8 +2657,7 @@ impl<I: Iterator, U: Iterator> Iterator for Flatten<I, U> } } -#[unstable(feature = "iterator_flatten", issue = "0")] -impl<I, U> DoubleEndedIterator for Flatten<I, U> +impl<I, U> DoubleEndedIterator for FlattenCompat<I, U> where I: DoubleEndedIterator, U: DoubleEndedIterator, I::Item: IntoIterator<IntoIter = U, Item = U::Item> { @@ -2555,10 +2713,6 @@ impl<I, U> DoubleEndedIterator for Flatten<I, U> } } -#[unstable(feature = "fused", issue = "35602")] -impl<I: FusedIterator, U: Iterator> FusedIterator for Flatten<I, U> - where I::Item: IntoIterator<IntoIter = U, Item = U::Item> {} - /// An iterator that yields `None` forever after the underlying iterator /// yields `None` once. /// diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index f28f5ef181ce9..edd75f7795ed7 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -836,8 +836,6 @@ fn test_iterator_scan() { assert_eq!(i, ys.len()); } -// Note: We test flatten() by testing flat_map(). - #[test] fn test_iterator_flat_map() { let xs = [0, 3, 6]; @@ -876,6 +874,44 @@ fn test_iterator_flat_map_fold() { assert_eq!(i, 0); } +#[test] +fn test_iterator_flatten() { + let xs = [0, 3, 6]; + let ys = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let it = xs.iter().map(|&x| (x..).step_by(1).take(3)).flatten(); + let mut i = 0; + for x in it { + assert_eq!(x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); +} + +/// Test `Flatten::fold` with items already picked off the front and back, +/// to make sure all parts of the `Flatten` are folded correctly. +#[test] +fn test_iterator_flatten_fold() { + let xs = [0, 3, 6]; + let ys = [1, 2, 3, 4, 5, 6, 7]; + let mut it = xs.iter().map(|&x| x..x+3).flatten(); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let i = it.fold(0, |i, x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().map(|&x| x..x+3).flatten(); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let i = it.rfold(ys.len(), |i, x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + #[test] fn test_inspect() { let xs = [1, 2, 3, 4]; @@ -1289,6 +1325,23 @@ fn test_double_ended_flat_map() { assert_eq!(it.next_back(), None); } +#[test] +fn test_double_ended_flatten() { + let u = [0,1]; + let v = [5,6,7,8]; + let mut it = u.iter().map(|x| &v[*x..v.len()]).flatten(); + assert_eq!(it.next_back().unwrap(), &8); + assert_eq!(it.next().unwrap(), &5); + assert_eq!(it.next_back().unwrap(), &7); + assert_eq!(it.next_back().unwrap(), &6); + assert_eq!(it.next_back().unwrap(), &8); + assert_eq!(it.next().unwrap(), &6); + assert_eq!(it.next_back().unwrap(), &7); + assert_eq!(it.next_back(), None); + assert_eq!(it.next(), None); + assert_eq!(it.next_back(), None); +} + #[test] fn test_double_ended_range() { assert_eq!((11..14).rev().collect::<Vec<_>>(), [13, 12, 11]); @@ -1980,3 +2033,54 @@ fn test_flat_map_try_folds() { assert_eq!(iter.try_rfold(0, i8::checked_add), None); assert_eq!(iter.next_back(), Some(35)); } + +#[test] +fn test_flatten_try_folds() { + let f = &|acc, x| i32::checked_add(acc*2/3, x); + let mr = &|x| (5*x)..(5*x + 5); + assert_eq!((0..10).map(mr).flatten().try_fold(7, f), (0..50).try_fold(7, f)); + assert_eq!((0..10).map(mr).flatten().try_rfold(7, f), (0..50).try_rfold(7, f)); + let mut iter = (0..10).map(mr).flatten(); + iter.next(); iter.next_back(); // have front and back iters in progress + assert_eq!(iter.try_rfold(7, f), (1..49).try_rfold(7, f)); + + let mut iter = (0..10).map(|x| (4*x)..(4*x + 4)).flatten(); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(17)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(35)); +} + +#[test] +fn test_functor_laws() { + // identity: + fn identity<T>(x: T) -> T { x } + assert_eq!((0..10).map(identity).sum::<usize>(), (0..10).sum()); + + // composition: + fn f(x: usize) -> usize { x + 3 } + fn g(x: usize) -> usize { x * 2 } + fn h(x: usize) -> usize { g(f(x)) } + assert_eq!((0..10).map(f).map(g).sum::<usize>(), (0..10).map(h).sum()); +} + +#[test] +fn test_monad_laws_left_identity() { + fn f(x: usize) -> impl Iterator<Item = usize> { + (0..10).map(move |y| x * y) + } + assert_eq!(once(42).flat_map(f.clone()).sum::<usize>(), f(42).sum()); +} + +#[test] +fn test_monad_laws_right_identity() { + assert_eq!((0..10).flat_map(|x| once(x)).sum::<usize>(), (0..10).sum()); +} + +#[test] +fn test_monad_laws_associativity() { + fn f(x: usize) -> impl Iterator<Item = usize> { 0..x } + fn g(x: usize) -> impl Iterator<Item = usize> { (0..x).rev() } + assert_eq!((0..10).flat_map(f).flat_map(g).sum::<usize>(), + (0..10).flat_map(|x| f(x).flat_map(g)).sum::<usize>()); +} diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 3e901a9d442ce..7954d52f6b1e3 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -25,6 +25,8 @@ #![feature(inclusive_range)] #![feature(inclusive_range_syntax)] #![feature(iterator_try_fold)] +#![feature(iterator_flatten)] +#![feature(conservative_impl_trait)] #![feature(iter_rfind)] #![feature(iter_rfold)] #![feature(iterator_repeat_with)] From 3d74c74fa0761542353ee1a2f4a832e9ba9b5ac4 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad <twingoow@gmail.com> Date: Wed, 14 Feb 2018 17:31:23 +0100 Subject: [PATCH 30/46] core::iter::Iterator::flatten: tracking issue is #48213 --- src/libcore/iter/iterator.rs | 2 +- src/libcore/iter/mod.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index 879696ba8e793..75a0e6aa2e6cc 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -1075,7 +1075,7 @@ pub trait Iterator { /// assert_eq!(merged, "alphabetagamma"); /// ``` #[inline] - #[unstable(feature = "iterator_flatten", issue = "0")] + #[unstable(feature = "iterator_flatten", issue = "48213")] fn flatten(self) -> Flatten<Self> where Self: Sized, Self::Item: IntoIterator { Flatten { inner: flatten_compat(self) } diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index e498f7d1b93c5..623cad754dd72 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -2494,13 +2494,13 @@ impl<I, U, F> FusedIterator for FlatMap<I, U, F> /// [`flatten`]: trait.Iterator.html#method.flatten /// [`Iterator`]: trait.Iterator.html #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -#[unstable(feature = "iterator_flatten", issue = "0")] +#[unstable(feature = "iterator_flatten", issue = "48213")] pub struct Flatten<I: Iterator> where I::Item: IntoIterator { inner: FlattenCompat<I, <I::Item as IntoIterator>::IntoIter>, } -#[unstable(feature = "iterator_flatten", issue = "0")] +#[unstable(feature = "iterator_flatten", issue = "48213")] impl<I, U> fmt::Debug for Flatten<I> where I: Iterator + fmt::Debug, U: Iterator + fmt::Debug, I::Item: IntoIterator<IntoIter = U, Item = U::Item>, @@ -2510,7 +2510,7 @@ impl<I, U> fmt::Debug for Flatten<I> } } -#[unstable(feature = "iterator_flatten", issue = "0")] +#[unstable(feature = "iterator_flatten", issue = "48213")] impl<I, U> Clone for Flatten<I> where I: Iterator + Clone, U: Iterator + Clone, I::Item: IntoIterator<IntoIter = U, Item = U::Item>, @@ -2518,7 +2518,7 @@ impl<I, U> Clone for Flatten<I> fn clone(&self) -> Self { Flatten { inner: self.inner.clone() } } } -#[unstable(feature = "iterator_flatten", issue = "0")] +#[unstable(feature = "iterator_flatten", issue = "48213")] impl<I, U> Iterator for Flatten<I> where I: Iterator, U: Iterator, I::Item: IntoIterator<IntoIter = U, Item = U::Item> @@ -2546,7 +2546,7 @@ impl<I, U> Iterator for Flatten<I> } } -#[unstable(feature = "iterator_flatten", issue = "0")] +#[unstable(feature = "iterator_flatten", issue = "48213")] impl<I, U> DoubleEndedIterator for Flatten<I> where I: DoubleEndedIterator, U: DoubleEndedIterator, I::Item: IntoIterator<IntoIter = U, Item = U::Item> From 819d57abc94d162e0d6f58fcbed757849f8305b4 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad <twingoow@gmail.com> Date: Fri, 16 Feb 2018 01:05:03 +0100 Subject: [PATCH 31/46] core::iter::Iterator::flatten: improve docs wrt. deep vs. shallow flatten per @clarcharr's review --- src/libcore/iter/iterator.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index 75a0e6aa2e6cc..e68e059385299 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -1074,6 +1074,26 @@ pub trait Iterator { /// .collect(); /// assert_eq!(merged, "alphabetagamma"); /// ``` + /// + /// Flattening once only removes one level of nesting: + /// + /// ``` + /// #![feature(iterator_flatten)] + /// + /// let d3 = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]; + /// + /// let d2 = d3.iter().flatten().collect::<Vec<_>>(); + /// assert_eq!(d2, [&[1, 2], &[3, 4], &[5, 6], &[7, 8]]); + /// + /// let d1 = d3.iter().flatten().flatten().collect::<Vec<_>>(); + /// assert_eq!(d1, [&1, &2, &3, &4, &5, &6, &7, &8]); + /// ``` + /// + /// Here we see that `flatten()` does not perform a "deep" flatten. + /// Instead, only one level of nesting is removed. That is, if you + /// `flatten()` a three-dimensional array the result will be + /// two-dimensional and not one-dimensional. To get a one-dimensional + /// structure, you have to `flatten()` again. #[inline] #[unstable(feature = "iterator_flatten", issue = "48213")] fn flatten(self) -> Flatten<Self> From 98d8fc192dc03945b5a09288fb6e12d688489928 Mon Sep 17 00:00:00 2001 From: newpavlov <newpavlov@gmail.com> Date: Tue, 20 Feb 2018 16:05:25 +0300 Subject: [PATCH 32/46] added rdrand feature and removed rdrnd feature --- src/librustc_trans/llvm_util.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/librustc_trans/llvm_util.rs b/src/librustc_trans/llvm_util.rs index b25562252e72e..4969f72412838 100644 --- a/src/librustc_trans/llvm_util.rs +++ b/src/librustc_trans/llvm_util.rs @@ -75,9 +75,9 @@ unsafe fn configure_llvm(sess: &Session) { llvm_args.as_ptr()); } -// WARNING: the features must be known to LLVM or the feature -// detection code will walk past the end of the feature array, -// leading to crashes. +// WARNING: the features after aplpying `to_llvm_feature` must be known +// to LLVM or the feature detection code will walk past the end of the feature +// array, leading to crashes. const ARM_WHITELIST: &'static [&'static str] = &["neon", "v7", "vfp2", "vfp3", "vfp4"]; @@ -86,7 +86,7 @@ const AARCH64_WHITELIST: &'static [&'static str] = &["neon", "v7"]; const X86_WHITELIST: &'static [&'static str] = &["avx", "avx2", "bmi", "bmi2", "sse", "sse2", "sse3", "sse4.1", "sse4.2", "ssse3", "tbm", "lzcnt", "popcnt", - "sse4a", "rdrnd", "rdseed", "fma", + "sse4a","fma", "rdrand", "rdseed", "xsave", "xsaveopt", "xsavec", "xsaves", "aes", "pclmulqdq", "avx512bw", "avx512cd", @@ -108,6 +108,7 @@ const MIPS_WHITELIST: &'static [&'static str] = &["msa"]; pub fn to_llvm_feature(s: &str) -> &str { match s { "pclmulqdq" => "pclmul", + "rdrand" => "rdrnd", s => s, } } From 4c6b9bcaa9eeca8e764b53a080dd6fa94c86e592 Mon Sep 17 00:00:00 2001 From: newpavlov <newpavlov@gmail.com> Date: Tue, 20 Feb 2018 16:08:22 +0300 Subject: [PATCH 33/46] features in alphabetic order --- src/librustc_trans/llvm_util.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/librustc_trans/llvm_util.rs b/src/librustc_trans/llvm_util.rs index 4969f72412838..be3d92020ab7d 100644 --- a/src/librustc_trans/llvm_util.rs +++ b/src/librustc_trans/llvm_util.rs @@ -83,18 +83,17 @@ const ARM_WHITELIST: &'static [&'static str] = &["neon", "v7", "vfp2", "vfp3", " const AARCH64_WHITELIST: &'static [&'static str] = &["neon", "v7"]; -const X86_WHITELIST: &'static [&'static str] = &["avx", "avx2", "bmi", "bmi2", "sse", - "sse2", "sse3", "sse4.1", "sse4.2", - "ssse3", "tbm", "lzcnt", "popcnt", - "sse4a","fma", "rdrand", "rdseed", - "xsave", "xsaveopt", "xsavec", - "xsaves", "aes", "pclmulqdq", - "avx512bw", "avx512cd", - "avx512dq", "avx512er", - "avx512f", "avx512ifma", - "avx512pf", "avx512vbmi", - "avx512vl", "avx512vpopcntdq", - "mmx", "fxsr"]; +const X86_WHITELIST: &'static [&'static str] = &["aes", "avx", "avx2", "avx512bw", + "avx512cd", "avx512dq", "avx512er", + "avx512f", "avx512ifma", "avx512pf", + "avx512vbmi", "avx512vl", "avx512vpopcntdq", + "bmi", "bmi2", "fma", "fxsr", + "lzcnt", "mmx", "pclmulqdq", + "popcnt", "rdrand", "rdseed", + "sse", "sse2", "sse3", "sse4.1", + "sse4.2", "sse4a", "ssse3", + "tbm", "xsave", "xsavec", + "xsaveopt", "xsaves"]; const HEXAGON_WHITELIST: &'static [&'static str] = &["hvx", "hvx-double"]; From 14b403c91ab9a1e4b776e00adcd9d88153e3b736 Mon Sep 17 00:00:00 2001 From: Vitali Lovich <vlovich@google.com> Date: Tue, 20 Feb 2018 13:07:21 -0800 Subject: [PATCH 34/46] Fix doc compile error --- src/libstd/sync/condvar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 7f9b566762884..3e40cbb37e3d8 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -469,7 +469,7 @@ impl Condvar { /// let result = cvar.wait_timeout_until( /// lock.lock().unwrap(), /// Duration::from_millis(100), - /// |started| started, + /// |&mut started| started, /// ).unwrap(); /// if result.1.timed_out() { /// // timed-out without the condition ever evaluating to true. From a33c1da861bed9dddd69f971131c4797eb6ebfeb Mon Sep 17 00:00:00 2001 From: Artyom Pavlov <newpavlov@gmail.com> Date: Wed, 21 Feb 2018 09:59:28 +0300 Subject: [PATCH 35/46] typo fix --- src/librustc_trans/llvm_util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_trans/llvm_util.rs b/src/librustc_trans/llvm_util.rs index be3d92020ab7d..6271dcdfb2433 100644 --- a/src/librustc_trans/llvm_util.rs +++ b/src/librustc_trans/llvm_util.rs @@ -75,7 +75,7 @@ unsafe fn configure_llvm(sess: &Session) { llvm_args.as_ptr()); } -// WARNING: the features after aplpying `to_llvm_feature` must be known +// WARNING: the features after applying `to_llvm_feature` must be known // to LLVM or the feature detection code will walk past the end of the feature // array, leading to crashes. From a895d438c251886666c979b1c68ed63fd357d03a Mon Sep 17 00:00:00 2001 From: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> Date: Sat, 17 Feb 2018 15:29:11 +0100 Subject: [PATCH 36/46] bootstrap: Add missing cputype matching for sparc --- src/bootstrap/bootstrap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 603a97ddfd412..5966bb65df9c8 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -294,7 +294,7 @@ def default_build_triple(): raise ValueError('unknown byteorder: {}'.format(sys.byteorder)) # only the n64 ABI is supported, indicate it ostype += 'abi64' - elif cputype == 'sparcv9' or cputype == 'sparc64': + elif cputype == 'sparc' or cputype == 'sparcv9' or cputype == 'sparc64': pass else: err = "unknown cpu type: {}".format(cputype) From 871e82e7a48f7ea0c08a30276c9be6f1cfcec087 Mon Sep 17 00:00:00 2001 From: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> Date: Sat, 17 Feb 2018 15:31:43 +0100 Subject: [PATCH 37/46] bootstrap: Add openssl configuration for sparc-unknown-linux-gnu --- src/bootstrap/native.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 29cd23bdbb197..b1528059e72cc 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -483,6 +483,7 @@ impl Step for Openssl { "powerpc64-unknown-linux-gnu" => "linux-ppc64", "powerpc64le-unknown-linux-gnu" => "linux-ppc64le", "s390x-unknown-linux-gnu" => "linux64-s390x", + "sparc-unknown-linux-gnu" => "linux-sparcv9", "sparc64-unknown-linux-gnu" => "linux64-sparcv9", "sparc64-unknown-netbsd" => "BSD-sparc64", "x86_64-apple-darwin" => "darwin64-x86_64-cc", From 100469f4e941037178c64c365b62efa856c0188a Mon Sep 17 00:00:00 2001 From: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> Date: Sat, 17 Feb 2018 15:48:05 +0100 Subject: [PATCH 38/46] librustc_back: Add support for sparc-linux-gnu --- src/librustc_back/target/mod.rs | 1 + .../target/sparc_unknown_linux_gnu.rs | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 src/librustc_back/target/sparc_unknown_linux_gnu.rs diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 2872c59157d6b..1ea23c1ea9ca6 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -146,6 +146,7 @@ supported_targets! { ("powerpc64-unknown-linux-gnu", powerpc64_unknown_linux_gnu), ("powerpc64le-unknown-linux-gnu", powerpc64le_unknown_linux_gnu), ("s390x-unknown-linux-gnu", s390x_unknown_linux_gnu), + ("sparc-unknown-linux-gnu", sparc_unknown_linux_gnu), ("sparc64-unknown-linux-gnu", sparc64_unknown_linux_gnu), ("arm-unknown-linux-gnueabi", arm_unknown_linux_gnueabi), ("arm-unknown-linux-gnueabihf", arm_unknown_linux_gnueabihf), diff --git a/src/librustc_back/target/sparc_unknown_linux_gnu.rs b/src/librustc_back/target/sparc_unknown_linux_gnu.rs new file mode 100644 index 0000000000000..a03e6937b2ca7 --- /dev/null +++ b/src/librustc_back/target/sparc_unknown_linux_gnu.rs @@ -0,0 +1,34 @@ +// Copyright 2018 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use LinkerFlavor; +use target::{Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + base.cpu = "v9".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mv8plus".to_string()); + base.exe_allocation_crate = None; + + Ok(Target { + llvm_target: "sparc-unknown-linux-gnu".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-p:32:32-i64:64-f128:64-n32-S64".to_string(), + arch: "sparc".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} From 84aae4ed12fcafb48216cd7b3f6ea448902f8c58 Mon Sep 17 00:00:00 2001 From: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> Date: Fri, 23 Feb 2018 11:22:19 +0100 Subject: [PATCH 39/46] Add sparc-unknown-linux-gnu target --- src/tools/build-manifest/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 4113f8fd124c7..0845179532224 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -86,6 +86,7 @@ static TARGETS: &'static [&'static str] = &[ "powerpc64-unknown-linux-gnu", "powerpc64le-unknown-linux-gnu", "s390x-unknown-linux-gnu", + "sparc-unknown-linux-gnu", "sparc64-unknown-linux-gnu", "sparcv9-sun-solaris", "wasm32-unknown-emscripten", From 0b6583ed97aa323cfa3e40d1cb1a1759fb0e1fc1 Mon Sep 17 00:00:00 2001 From: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> Date: Sat, 24 Feb 2018 01:08:02 +0100 Subject: [PATCH 40/46] bootstrap: Add openssl configuration for x86_64-unknown-linux-gnux32 --- src/bootstrap/native.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 29cd23bdbb197..248c2d9bd0caf 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -490,6 +490,7 @@ impl Step for Openssl { "x86_64-unknown-freebsd" => "BSD-x86_64", "x86_64-unknown-dragonfly" => "BSD-x86_64", "x86_64-unknown-linux-gnu" => "linux-x86_64", + "x86_64-unknown-linux-gnux32" => "linux-x32", "x86_64-unknown-linux-musl" => "linux-x86_64", "x86_64-unknown-netbsd" => "BSD-x86_64", _ => panic!("don't know how to configure OpenSSL for {}", target), From e3781c65852f7624fd58b723b66be7470cb41ea0 Mon Sep 17 00:00:00 2001 From: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> Date: Sat, 24 Feb 2018 01:15:48 +0100 Subject: [PATCH 41/46] test: Fix s390x-unknown-linux-gnu atomic-lock-free test not run for systemz --- src/test/run-make/atomic-lock-free/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/run-make/atomic-lock-free/Makefile b/src/test/run-make/atomic-lock-free/Makefile index 4849b0307423d..63b8608afc763 100644 --- a/src/test/run-make/atomic-lock-free/Makefile +++ b/src/test/run-make/atomic-lock-free/Makefile @@ -36,6 +36,8 @@ ifeq ($(filter powerpc,$(LLVM_COMPONENTS)),powerpc) nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add $(RUSTC) --target=powerpc64le-unknown-linux-gnu atomic_lock_free.rs nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add +endif +ifeq ($(filter systemz,$(LLVM_COMPONENTS)),systemz) $(RUSTC) --target=s390x-unknown-linux-gnu atomic_lock_free.rs nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add endif From 34b45c192f604b3dcb6f99c9d161a074fac45daf Mon Sep 17 00:00:00 2001 From: Bryan Drewery <bryan@shatow.net> Date: Fri, 23 Feb 2018 19:33:03 -0800 Subject: [PATCH 42/46] Workaround abort(2) on compilation error on FreeBSD. Same problem as OpenBSD, tracking bug #43575. --- src/librustc_back/target/freebsd_base.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc_back/target/freebsd_base.rs b/src/librustc_back/target/freebsd_base.rs index 21dca99aa5005..a0f84a6ab0495 100644 --- a/src/librustc_back/target/freebsd_base.rs +++ b/src/librustc_back/target/freebsd_base.rs @@ -33,6 +33,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, pre_link_args: args, position_independent_executables: true, + eliminate_frame_pointer: false, // FIXME 43575 relro_level: RelroLevel::Full, exe_allocation_crate: super::maybe_jemalloc(), .. Default::default() From 264a92182e037c46cdcf37890e3142eaf634444d Mon Sep 17 00:00:00 2001 From: penpalperson <16357077+penpalperson@users.noreply.github.com> Date: Sat, 24 Feb 2018 15:56:33 -0700 Subject: [PATCH 43/46] Added error-format flag to x.py. --- src/bootstrap/bin/rustc.rs | 5 +++++ src/bootstrap/builder.rs | 3 +++ src/bootstrap/config.rs | 2 ++ src/bootstrap/flags.rs | 3 +++ 4 files changed, 13 insertions(+) diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index 55d104b182698..ca35a896e08c8 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -61,6 +61,11 @@ fn main() { args.remove(n); } + if let Some(s) = env::var_os("RUSTC_ERROR_FORMAT") { + args.push("--error-format".into()); + args.push(s); + } + // Detect whether or not we're a build script depending on whether --target // is passed (a bit janky...) let target = args.windows(2) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 66a1c97246200..343d30922fded 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -598,6 +598,9 @@ impl<'a> Builder<'a> { if let Some(target_linker) = self.build.linker(target) { cargo.env("RUSTC_TARGET_LINKER", target_linker); } + if let Some(ref error_format) = self.config.rustc_error_format { + cargo.env("RUSTC_ERROR_FORMAT", error_format); + } if cmd != "build" && cmd != "check" { cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(self.compiler(2, self.build.build))); } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 812ca6d64fb6a..70a873043ad32 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -57,6 +57,7 @@ pub struct Config { pub profiler: bool, pub ignore_git: bool, pub exclude: Vec<PathBuf>, + pub rustc_error_format: Option<String>, pub run_host_only: bool, @@ -330,6 +331,7 @@ impl Config { config.test_miri = false; config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")]; + config.rustc_error_format = flags.rustc_error_format; config.on_fail = flags.on_fail; config.stage = flags.stage; config.src = flags.src; diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index eb5c3b8ce147f..8ca5910a11c0d 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -43,6 +43,7 @@ pub struct Flags { pub cmd: Subcommand, pub incremental: bool, pub exclude: Vec<PathBuf>, + pub rustc_error_format: Option<String>, } pub enum Subcommand { @@ -118,6 +119,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`"); opts.optopt("", "src", "path to the root of the rust checkout", "DIR"); opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS"); opts.optflag("h", "help", "print this help message"); + opts.optflag("", "error-format", "rustc error format"); // fn usage() let usage = |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! { @@ -370,6 +372,7 @@ Arguments: verbose: matches.opt_count("verbose"), stage, on_fail: matches.opt_str("on-fail"), + rustc_error_format: matches.opt_str("error-format"), keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()), build: matches.opt_str("build").map(|s| INTERNER.intern_string(s)), host: split(matches.opt_strs("host")) From 64236092e5aa8fca2b9175df5a1192fd3d46e29b Mon Sep 17 00:00:00 2001 From: Nathan Ringo <remexre@gmail.com> Date: Sat, 24 Feb 2018 23:48:51 -0600 Subject: [PATCH 44/46] Fixes docs for ASCII functions to no longer claim U+0021 is '@'. --- src/libcore/num/mod.rs | 2 +- src/libstd/ascii.rs | 2 +- src/libstd_unicode/char.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 43330b63f9b9c..7d5aa25016bdd 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -2892,7 +2892,7 @@ impl u8 { } /// Checks if the value is an ASCII graphic character: - /// U+0021 '@' ... U+007E '~'. + /// U+0021 '!' ... U+007E '~'. /// /// # Examples /// diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs index 82e1a3447dc5d..430c9df396a5e 100644 --- a/src/libstd/ascii.rs +++ b/src/libstd/ascii.rs @@ -245,7 +245,7 @@ pub trait AsciiExt { fn is_ascii_punctuation(&self) -> bool { unimplemented!(); } /// Checks if the value is an ASCII graphic character: - /// U+0021 '@' ... U+007E '~'. + /// U+0021 '!' ... U+007E '~'. /// For strings, true if all characters in the string are /// ASCII graphic characters. /// diff --git a/src/libstd_unicode/char.rs b/src/libstd_unicode/char.rs index b4be4a9691183..844ff7a3c1252 100644 --- a/src/libstd_unicode/char.rs +++ b/src/libstd_unicode/char.rs @@ -1332,7 +1332,7 @@ impl char { } /// Checks if the value is an ASCII graphic character: - /// U+0021 '@' ... U+007E '~'. + /// U+0021 '!' ... U+007E '~'. /// /// # Examples /// From 0aa753ba30254631ce05f37cd2ff0dd428d931c5 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar <manishsmail@gmail.com> Date: Sat, 24 Feb 2018 21:06:29 -0800 Subject: [PATCH 45/46] 1.25.0 -> 1.26.- --- src/liballoc/btree/map.rs | 2 +- src/libstd/collections/hash/map.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index b98489c516a05..618ef81fdd981 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -2128,7 +2128,7 @@ impl<'a, K: Ord, V> Entry<'a, K, V> { /// .or_insert(42); /// assert_eq!(map["poneyland"], 43); /// ``` - #[stable(feature = "entry_and_modify", since = "1.25.0")] + #[stable(feature = "entry_and_modify", since = "1.26.0")] pub fn and_modify<F>(self, mut f: F) -> Self where F: FnMut(&mut V) { diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 80e52123a0177..b63a45ecade43 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -2080,7 +2080,7 @@ impl<'a, K, V> Entry<'a, K, V> { /// .or_insert(42); /// assert_eq!(map["poneyland"], 43); /// ``` - #[stable(feature = "entry_and_modify", since = "1.25.0")] + #[stable(feature = "entry_and_modify", since = "1.26.0")] pub fn and_modify<F>(self, mut f: F) -> Self where F: FnMut(&mut V) { From a554a2f5645f12cf42d311ac11bcaad594c91347 Mon Sep 17 00:00:00 2001 From: Corey Farwell <coreyf@rwell.org> Date: Mon, 19 Feb 2018 08:45:45 -0500 Subject: [PATCH 46/46] Return error if timeout is zero-Duration on Redox. --- src/libstd/sys/redox/net/tcp.rs | 10 +++++++++- src/libstd/sys/redox/net/udp.rs | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/libstd/sys/redox/net/tcp.rs b/src/libstd/sys/redox/net/tcp.rs index 319965ab3965e..b5664908479cf 100644 --- a/src/libstd/sys/redox/net/tcp.rs +++ b/src/libstd/sys/redox/net/tcp.rs @@ -9,7 +9,7 @@ // except according to those terms. use cmp; -use io::{Error, ErrorKind, Result}; +use io::{self, Error, ErrorKind, Result}; use mem; use net::{SocketAddr, Shutdown}; use path::Path; @@ -130,6 +130,10 @@ impl TcpStream { pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> { let file = self.0.dup(b"read_timeout")?; if let Some(duration) = duration_option { + if duration.as_secs() == 0 && duration.subsec_nanos() == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } file.write(&TimeSpec { tv_sec: duration.as_secs() as i64, tv_nsec: duration.subsec_nanos() as i32 @@ -143,6 +147,10 @@ impl TcpStream { pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> { let file = self.0.dup(b"write_timeout")?; if let Some(duration) = duration_option { + if duration.as_secs() == 0 && duration.subsec_nanos() == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } file.write(&TimeSpec { tv_sec: duration.as_secs() as i64, tv_nsec: duration.subsec_nanos() as i32 diff --git a/src/libstd/sys/redox/net/udp.rs b/src/libstd/sys/redox/net/udp.rs index 7e7666e7ef364..2ed67bd2836f2 100644 --- a/src/libstd/sys/redox/net/udp.rs +++ b/src/libstd/sys/redox/net/udp.rs @@ -10,7 +10,7 @@ use cell::UnsafeCell; use cmp; -use io::{Error, ErrorKind, Result}; +use io::{self, Error, ErrorKind, Result}; use mem; use net::{SocketAddr, Ipv4Addr, Ipv6Addr}; use path::Path; @@ -179,6 +179,10 @@ impl UdpSocket { pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> { let file = self.0.dup(b"read_timeout")?; if let Some(duration) = duration_option { + if duration.as_secs() == 0 && duration.subsec_nanos() == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } file.write(&TimeSpec { tv_sec: duration.as_secs() as i64, tv_nsec: duration.subsec_nanos() as i32 @@ -192,6 +196,10 @@ impl UdpSocket { pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> { let file = self.0.dup(b"write_timeout")?; if let Some(duration) = duration_option { + if duration.as_secs() == 0 && duration.subsec_nanos() == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } file.write(&TimeSpec { tv_sec: duration.as_secs() as i64, tv_nsec: duration.subsec_nanos() as i32