diff options
Diffstat (limited to 'src')
30 files changed, 327 insertions, 202 deletions
diff --git a/src/Cargo.lock b/src/Cargo.lock index 3a27107f825..d5a1d18a676 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -2044,6 +2044,7 @@ dependencies = [ "parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-rayon 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-rayon-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_cratesio_shim 0.0.0", "serialize 0.0.0", "stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 161493f3892..90bc2f9769c 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -566,15 +566,17 @@ impl<T: Clone, V: Borrow<[T]>> SliceConcatExt<T> for [V] { } fn join(&self, sep: &T) -> Vec<T> { + let mut iter = self.iter(); + let first = match iter.next() { + Some(first) => first, + None => return vec![], + }; let size = self.iter().fold(0, |acc, v| acc + v.borrow().len()); let mut result = Vec::with_capacity(size + self.len()); - let mut first = true; - for v in self { - if first { - first = false - } else { - result.push(sep.clone()) - } + result.extend_from_slice(first.borrow()); + + for v in iter { + result.push(sep.clone()); result.extend_from_slice(v.borrow()) } result diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 823e56b64e3..32ca8d1fa5e 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -86,52 +86,108 @@ impl<S: Borrow<str>> SliceConcatExt<str> for [S] { type Output = String; fn concat(&self) -> String { - if self.is_empty() { - return String::new(); - } - - // `len` calculation may overflow but push_str will check boundaries - let len = self.iter().map(|s| s.borrow().len()).sum(); - let mut result = String::with_capacity(len); - - for s in self { - result.push_str(s.borrow()) - } - - result + self.join("") } fn join(&self, sep: &str) -> String { - if self.is_empty() { - return String::new(); - } - - // concat is faster - if sep.is_empty() { - return self.concat(); + unsafe { + String::from_utf8_unchecked( join_generic_copy(self, sep.as_bytes()) ) } + } - // this is wrong without the guarantee that `self` is non-empty - // `len` calculation may overflow but push_str but will check boundaries - let len = sep.len() * (self.len() - 1) + - self.iter().map(|s| s.borrow().len()).sum::<usize>(); - let mut result = String::with_capacity(len); - let mut first = true; + fn connect(&self, sep: &str) -> String { + self.join(sep) + } +} - for s in self { - if first { - first = false; - } else { - result.push_str(sep); +macro_rules! spezialize_for_lengths { + ($separator:expr, $target:expr, $iter:expr; $($num:expr),*) => { + let mut target = $target; + let iter = $iter; + let sep_bytes = $separator; + match $separator.len() { + $( + // loops with hardcoded sizes run much faster + // specialize the cases with small separator lengths + $num => { + for s in iter { + copy_slice_and_advance!(target, sep_bytes); + copy_slice_and_advance!(target, s.borrow().as_ref()); + } + }, + )* + _ => { + // arbitrary non-zero size fallback + for s in iter { + copy_slice_and_advance!(target, sep_bytes); + copy_slice_and_advance!(target, s.borrow().as_ref()); + } } - result.push_str(s.borrow()); } - result + }; +} + +macro_rules! copy_slice_and_advance { + ($target:expr, $bytes:expr) => { + let len = $bytes.len(); + let (head, tail) = {$target}.split_at_mut(len); + head.copy_from_slice($bytes); + $target = tail; } +} - fn connect(&self, sep: &str) -> String { - self.join(sep) +// Optimized join implementation that works for both Vec<T> (T: Copy) and String's inner vec +// Currently (2018-05-13) there is a bug with type inference and specialization (see issue #36262) +// For this reason SliceConcatExt<T> is not specialized for T: Copy and SliceConcatExt<str> is the +// only user of this function. It is left in place for the time when that is fixed. +// +// the bounds for String-join are S: Borrow<str> and for Vec-join Borrow<[T]> +// [T] and str both impl AsRef<[T]> for some T +// => s.borrow().as_ref() and we always have slices +fn join_generic_copy<B, T, S>(slice: &[S], sep: &[T]) -> Vec<T> +where + T: Copy, + B: AsRef<[T]> + ?Sized, + S: Borrow<B>, +{ + let sep_len = sep.len(); + let mut iter = slice.iter(); + + // the first slice is the only one without a separator preceding it + let first = match iter.next() { + Some(first) => first, + None => return vec![], + }; + + // compute the exact total length of the joined Vec + // if the `len` calculation overflows, we'll panic + // we would have run out of memory anyway and the rest of the function requires + // the entire Vec pre-allocated for safety + let len = sep_len.checked_mul(iter.len()).and_then(|n| { + slice.iter() + .map(|s| s.borrow().as_ref().len()) + .try_fold(n, usize::checked_add) + }).expect("attempt to join into collection with len > usize::MAX"); + + // crucial for safety + let mut result = Vec::with_capacity(len); + assert!(result.capacity() >= len); + + result.extend_from_slice(first.borrow().as_ref()); + + unsafe { + { + let pos = result.len(); + let target = result.get_unchecked_mut(pos..len); + + // copy separator and slices over without bounds checks + // generate loops with hardcoded offsets for small separators + // massive improvements possible (~ x2) + spezialize_for_lengths!(sep, target, iter; 0, 1, 2, 3, 4); + } + result.set_len(len); } + result } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs index 6fd0b33f02a..3b7eec38609 100644 --- a/src/liballoc/tests/slice.rs +++ b/src/liballoc/tests/slice.rs @@ -610,6 +610,15 @@ fn test_join() { } #[test] +fn test_join_nocopy() { + let v: [String; 0] = []; + assert_eq!(v.join(","), ""); + assert_eq!(["a".to_string(), "ab".into()].join(","), "a,ab"); + assert_eq!(["a".to_string(), "ab".into(), "abc".into()].join(","), "a,ab,abc"); + assert_eq!(["a".to_string(), "ab".into(), "".into()].join(","), "a,ab,"); +} + +#[test] fn test_insert() { let mut a = vec![1, 2, 4]; a.insert(2, 3); diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index d11bf5dc3e9..03d295d16e6 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -162,6 +162,19 @@ fn test_join_for_different_lengths() { test_join!("-a-bc", ["", "a", "bc"], "-"); } +// join has fast paths for small separators up to 4 bytes +// this tests the slow paths. +#[test] +fn test_join_for_different_lengths_with_long_separator() { + assert_eq!("~~~~~".len(), 15); + + let empty: &[&str] = &[]; + test_join!("", empty, "~~~~~"); + test_join!("a", ["a"], "~~~~~"); + test_join!("a~~~~~b", ["a", "b"], "~~~~~"); + test_join!("~~~~~a~~~~~bc", ["", "a", "bc"], "~~~~~"); +} + #[test] fn test_unsafe_slice() { assert_eq!("ab", unsafe {"abc".slice_unchecked(0, 2)}); diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 674c4fb57c7..6172a98bca6 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -392,6 +392,14 @@ impl From<AllocErr> for CollectionAllocErr { } } +#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] +impl From<LayoutErr> for CollectionAllocErr { + #[inline] + fn from(_: LayoutErr) -> Self { + CollectionAllocErr::CapacityOverflow + } +} + /// A memory allocator that can be registered to be the one backing `std::alloc::Global` /// though the `#[global_allocator]` attributes. pub unsafe trait GlobalAlloc { diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 77db165bcbd..3d3f63ecf37 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -294,7 +294,7 @@ pub trait Copy : Clone { /// This trait is automatically implemented when the compiler determines /// it's appropriate. /// -/// The precise definition is: a type `T` is `Sync` if `&T` is +/// The precise definition is: a type `T` is `Sync` if and only if `&T` is /// [`Send`][send]. In other words, if there is no possibility of /// [undefined behavior][ub] (including data races) when passing /// `&T` references between threads. diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 013d0334d41..ea652ad811e 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -2313,7 +2313,7 @@ Basic usage: ``` ", $Feature, "assert_eq!((", stringify!($SelfT), "::max_value() - 2).checked_add(1), ", "Some(", stringify!($SelfT), "::max_value() - 1)); -assert_eq!((", stringify!($SelfT), "::max_value() - 2).checked_add(3),None);", $EndFeature, " +assert_eq!((", stringify!($SelfT), "::max_value() - 2).checked_add(3), None);", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -4454,6 +4454,20 @@ macro_rules! impl_from { } } +// Bool -> Any +impl_from! { bool, u8, #[stable(feature = "from_bool", since = "1.28.0")] } +impl_from! { bool, u16, #[stable(feature = "from_bool", since = "1.28.0")] } +impl_from! { bool, u32, #[stable(feature = "from_bool", since = "1.28.0")] } +impl_from! { bool, u64, #[stable(feature = "from_bool", since = "1.28.0")] } +impl_from! { bool, u128, #[stable(feature = "from_bool", since = "1.28.0")] } +impl_from! { bool, usize, #[stable(feature = "from_bool", since = "1.28.0")] } +impl_from! { bool, i8, #[stable(feature = "from_bool", since = "1.28.0")] } +impl_from! { bool, i16, #[stable(feature = "from_bool", since = "1.28.0")] } +impl_from! { bool, i32, #[stable(feature = "from_bool", since = "1.28.0")] } +impl_from! { bool, i64, #[stable(feature = "from_bool", since = "1.28.0")] } +impl_from! { bool, i128, #[stable(feature = "from_bool", since = "1.28.0")] } +impl_from! { bool, isize, #[stable(feature = "from_bool", since = "1.28.0")] } + // Unsigned -> Unsigned impl_from! { u8, u16, #[stable(feature = "lossless_int_conv", since = "1.5.0")] } impl_from! { u8, u32, #[stable(feature = "lossless_int_conv", since = "1.5.0")] } diff --git a/src/libcore/tests/num/mod.rs b/src/libcore/tests/num/mod.rs index f3439890fce..b5e6a019a22 100644 --- a/src/libcore/tests/num/mod.rs +++ b/src/libcore/tests/num/mod.rs @@ -134,6 +134,15 @@ fn test_infallible_try_from_int_error() { } macro_rules! test_impl_from { + ($fn_name:ident, bool, $target: ty) => { + #[test] + fn $fn_name() { + let one: $target = 1; + let zero: $target = 0; + assert_eq!(one, <$target>::from(true)); + assert_eq!(zero, <$target>::from(false)); + } + }; ($fn_name: ident, $Small: ty, $Large: ty) => { #[test] fn $fn_name() { @@ -173,6 +182,18 @@ test_impl_from! { test_u16i32, u16, i32 } test_impl_from! { test_u16i64, u16, i64 } test_impl_from! { test_u32i64, u32, i64 } +// Bool -> Integer +test_impl_from! { test_boolu8, bool, u8 } +test_impl_from! { test_boolu16, bool, u16 } +test_impl_from! { test_boolu32, bool, u32 } +test_impl_from! { test_boolu64, bool, u64 } +test_impl_from! { test_boolu128, bool, u128 } +test_impl_from! { test_booli8, bool, i8 } +test_impl_from! { test_booli16, bool, i16 } +test_impl_from! { test_booli32, bool, i32 } +test_impl_from! { test_booli64, bool, i64 } +test_impl_from! { test_booli128, bool, i128 } + // Signed -> Float test_impl_from! { test_i8f32, i8, f32 } test_impl_from! { test_i8f64, i8, f64 } diff --git a/src/libcore/tests/result.rs b/src/libcore/tests/result.rs index ce41bde8342..d9927ce4487 100644 --- a/src/libcore/tests/result.rs +++ b/src/libcore/tests/result.rs @@ -220,13 +220,15 @@ fn test_try() { assert_eq!(try_result_none(), None); fn try_result_ok() -> Result<u8, u8> { - let val = Ok(1)?; + let result: Result<u8, u8> = Ok(1); + let val = result?; Ok(val) } assert_eq!(try_result_ok(), Ok(1)); fn try_result_err() -> Result<u8, u8> { - let val = Err(1)?; + let result: Result<u8, u8> = Err(1); + let val = result?; Ok(val) } assert_eq!(try_result_err(), Err(1)); diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 9d037cefcee..befc1ba064a 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -36,6 +36,8 @@ #![feature(lang_items)] #![feature(optin_builtin_traits)] +#![recursion_limit="256"] + extern crate syntax; extern crate syntax_pos; extern crate rustc_errors; diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 8df66d8d688..076d56fb808 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -845,10 +845,10 @@ impl Session { /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. /// This expends fuel if applicable, and records fuel if applicable. pub fn consider_optimizing<T: Fn() -> String>(&self, crate_name: &str, msg: T) -> bool { - assert!(self.query_threads() == 1); let mut ret = true; match self.optimization_fuel_crate { Some(ref c) if c == crate_name => { + assert!(self.query_threads() == 1); let fuel = self.optimization_fuel_limit.get(); ret = fuel != 0; if fuel == 0 && !self.out_of_fuel.get() { @@ -862,6 +862,7 @@ impl Session { } match self.print_fuel_crate { Some(ref c) if c == crate_name => { + assert!(self.query_threads() == 1); self.print_fuel.set(self.print_fuel.get() + 1); } _ => {} diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 68f55b49933..35b2ce50da7 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -58,7 +58,7 @@ use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap, StableVec}; use arena::{TypedArena, SyncDroplessArena}; use rustc_data_structures::indexed_vec::IndexVec; -use rustc_data_structures::sync::{Lrc, Lock}; +use rustc_data_structures::sync::{self, Lrc, Lock, WorkerLocal}; use std::any::Any; use std::borrow::Borrow; use std::cmp::Ordering; @@ -80,14 +80,14 @@ use syntax_pos::Span; use hir; pub struct AllArenas<'tcx> { - pub global: GlobalArenas<'tcx>, + pub global: WorkerLocal<GlobalArenas<'tcx>>, pub interner: SyncDroplessArena, } impl<'tcx> AllArenas<'tcx> { pub fn new() -> Self { AllArenas { - global: GlobalArenas::new(), + global: WorkerLocal::new(|_| GlobalArenas::new()), interner: SyncDroplessArena::new(), } } @@ -854,7 +854,7 @@ impl<'a, 'gcx, 'tcx> Deref for TyCtxt<'a, 'gcx, 'tcx> { } pub struct GlobalCtxt<'tcx> { - global_arenas: &'tcx GlobalArenas<'tcx>, + global_arenas: &'tcx WorkerLocal<GlobalArenas<'tcx>>, global_interners: CtxtInterners<'tcx>, cstore: &'tcx CrateStoreDyn, @@ -1179,6 +1179,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { output_filenames: Arc::new(output_filenames.clone()), }; + sync::assert_send_val(&gcx); + tls::enter_global(gcx, f) } @@ -1704,7 +1706,7 @@ pub mod tls { use ty::maps; use errors::{Diagnostic, TRACK_DIAGNOSTICS}; use rustc_data_structures::OnDrop; - use rustc_data_structures::sync::Lrc; + use rustc_data_structures::sync::{self, Lrc}; use dep_graph::OpenTask; /// This is the implicit state of rustc. It contains the current @@ -1832,6 +1834,10 @@ pub mod tls { if context == 0 { f(None) } else { + // We could get a ImplicitCtxt pointer from another thread. + // Ensure that ImplicitCtxt is Sync + sync::assert_sync::<ImplicitCtxt>(); + unsafe { f(Some(&*(context as *const ImplicitCtxt))) } } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 419bffcae06..0bb0208e2a1 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -617,6 +617,8 @@ pub struct Slice<T> { opaque: OpaqueSliceContents, } +unsafe impl<T: Sync> Sync for Slice<T> {} + impl<T: Copy> Slice<T> { #[inline] fn from_arena<'tcx>(arena: &'tcx SyncDroplessArena, slice: &[T]) -> &'tcx Slice<T> { diff --git a/src/librustc_codegen_utils/lib.rs b/src/librustc_codegen_utils/lib.rs index 0c18571f4ff..d09e8f4845e 100644 --- a/src/librustc_codegen_utils/lib.rs +++ b/src/librustc_codegen_utils/lib.rs @@ -23,6 +23,8 @@ #![feature(quote)] #![feature(rustc_diagnostic_macros)] +#![recursion_limit="256"] + extern crate ar; extern crate flate2; #[macro_use] diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index 15956829ff9..17ee771e529 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml @@ -17,6 +17,7 @@ cfg-if = "0.1.2" stable_deref_trait = "1.0.0" parking_lot_core = "0.2.8" rustc-rayon = "0.1.0" +rustc-rayon-core = "0.1.0" rustc-hash = "1.0.1" [dependencies.parking_lot] diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 23a920739b9..7046a2a2a49 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -44,6 +44,7 @@ extern crate parking_lot; extern crate cfg_if; extern crate stable_deref_trait; extern crate rustc_rayon as rayon; +extern crate rustc_rayon_core as rayon_core; extern crate rustc_hash; // See librustc_cratesio_shim/Cargo.toml for a comment explaining this. diff --git a/src/librustc_data_structures/sync.rs b/src/librustc_data_structures/sync.rs index 36617631330..6f7d9e1b54b 100644 --- a/src/librustc_data_structures/sync.rs +++ b/src/librustc_data_structures/sync.rs @@ -36,7 +36,6 @@ use std::marker::PhantomData; use std::fmt::Debug; use std::fmt::Formatter; use std::fmt; -use std; use std::ops::{Deref, DerefMut}; use owning_ref::{Erased, OwningRef}; @@ -101,6 +100,33 @@ cfg_if! { use std::cell::Cell; #[derive(Debug)] + pub struct WorkerLocal<T>(OneThread<T>); + + impl<T> WorkerLocal<T> { + /// Creates a new worker local where the `initial` closure computes the + /// value this worker local should take for each thread in the thread pool. + #[inline] + pub fn new<F: FnMut(usize) -> T>(mut f: F) -> WorkerLocal<T> { + WorkerLocal(OneThread::new(f(0))) + } + + /// Returns the worker-local value for each thread + #[inline] + pub fn into_inner(self) -> Vec<T> { + vec![OneThread::into_inner(self.0)] + } + } + + impl<T> Deref for WorkerLocal<T> { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &T { + &*self.0 + } + } + + #[derive(Debug)] pub struct MTLock<T>(T); impl<T> MTLock<T> { @@ -200,9 +226,12 @@ cfg_if! { use parking_lot::Mutex as InnerLock; use parking_lot::RwLock as InnerRwLock; + use std; use std::thread; pub use rayon::{join, scope}; + pub use rayon_core::WorkerLocal; + pub use rayon::iter::ParallelIterator; use rayon::iter::IntoParallelIterator; @@ -638,7 +667,9 @@ pub struct OneThread<T> { inner: T, } +#[cfg(parallel_queries)] unsafe impl<T> std::marker::Sync for OneThread<T> {} +#[cfg(parallel_queries)] unsafe impl<T> std::marker::Send for OneThread<T> {} impl<T> OneThread<T> { diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index 7ecf2eba43d..d76ca5bdf27 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -23,6 +23,8 @@ #![feature(specialization)] #![feature(rustc_private)] +#![recursion_limit="256"] + extern crate libc; #[macro_use] extern crate log; diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index bfb8c282d37..f32f6eda8ff 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -14,6 +14,8 @@ #![feature(rustc_diagnostic_macros)] +#![recursion_limit="256"] + #[macro_use] extern crate rustc; #[macro_use] extern crate syntax; extern crate rustc_typeck; diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index e57a793ff42..64dcd3e5175 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -15,6 +15,8 @@ #![cfg_attr(stage0, feature(macro_lifetime_matcher))] #![allow(unused_attributes)] +#![recursion_limit="256"] + #[macro_use] extern crate rustc; diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs index 733d8e1708e..7fa69cb9833 100644 --- a/src/librustc_traits/lib.rs +++ b/src/librustc_traits/lib.rs @@ -17,6 +17,8 @@ #![feature(iterator_find_map)] #![feature(in_band_lifetimes)] +#![recursion_limit="256"] + extern crate chalk_engine; #[macro_use] extern crate log; diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 8a575c14787..6031984350b 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -119,11 +119,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } }; - let note_str = format!("candidate #{} is defined in an impl{} \ - for the type `{}`", - idx + 1, - insertion, - impl_ty); + let note_str = if sources.len() > 1 { + format!("candidate #{} is defined in an impl{} for the type `{}`", + idx + 1, + insertion, + impl_ty) + } else { + format!("the candidate is defined in an impl{} for the type `{}`", + insertion, + impl_ty) + }; if let Some(note_span) = note_span { // We have a span pointing to the method. Show note with snippet. err.span_note(self.tcx.sess.codemap().def_span(note_span), ¬e_str); @@ -137,11 +142,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .unwrap(); let item_span = self.tcx.sess.codemap() .def_span(self.tcx.def_span(item.def_id)); - span_note!(err, - item_span, - "candidate #{} is defined in the trait `{}`", - idx + 1, - self.tcx.item_path_str(trait_did)); + if sources.len() > 1 { + span_note!(err, + item_span, + "candidate #{} is defined in the trait `{}`", + idx + 1, + self.tcx.item_path_str(trait_did)); + } else { + span_note!(err, + item_span, + "the candidate is defined in the trait `{}`", + self.tcx.item_path_str(trait_did)); + } err.help(&format!("to disambiguate the method call, write `{}::{}({}{})` \ instead", self.tcx.item_path_str(trait_did), @@ -285,7 +297,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { tcx.sess.diagnostic().struct_dummy() }; - if let Some(def) = actual.ty_adt_def() { + if let Some(def) = actual.ty_adt_def() { if let Some(full_sp) = tcx.hir.span_if_local(def.did) { let def_sp = tcx.sess.codemap().def_span(full_sp); err.span_label(def_sp, format!("{} `{}` not found {}", @@ -368,7 +380,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if !static_sources.is_empty() { err.note("found the following associated functions; to be used as methods, \ functions must have a `self` parameter"); - err.help(&format!("try with `{}::{}`", self.ty_to_string(actual), item_name)); + err.span_label(span, "this is an associated function, not a method"); + } + if static_sources.len() == 1 { + if let Some(expr) = rcvr_expr { + err.span_suggestion(expr.span.to(span), + "use associated function syntax instead", + format!("{}::{}", + self.ty_to_string(actual), + item_name)); + } else { + err.help(&format!("try with `{}::{}`", + self.ty_to_string(actual), item_name)); + } + + report_candidates(&mut err, static_sources); + } else if static_sources.len() > 1 { report_candidates(&mut err, static_sources); } @@ -468,9 +495,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } else { let limit = if candidates.len() == 5 { 5 } else { 4 }; for (i, trait_did) in candidates.iter().take(limit).enumerate() { - msg.push_str(&format!("\ncandidate #{}: `use {};`", - i + 1, - self.tcx.item_path_str(*trait_did))); + if candidates.len() > 1 { + msg.push_str(&format!("\ncandidate #{}: `use {};`", + i + 1, + self.tcx.item_path_str(*trait_did))); + } else { + msg.push_str(&format!("\n`use {};`", + self.tcx.item_path_str(*trait_did))); + } } if candidates.len() > limit { msg.push_str(&format!("\nand {} others", candidates.len() - limit)); diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 4f9dffc7c95..3b1a3a439e7 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -19,21 +19,22 @@ use core::sync::atomic::{AtomicPtr, Ordering}; use core::{mem, ptr}; +use sys_common::util::dumb_print; static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// Registers a custom OOM hook, replacing any that was previously registered. /// -/// The OOM hook is invoked when an infallible memory allocation fails. -/// The default hook prints a message to standard error and aborts the -/// execution, but this behavior can be customized with the [`set_oom_hook`] -/// and [`take_oom_hook`] functions. +/// The OOM hook is invoked when an infallible memory allocation fails, before +/// the runtime aborts. The default hook prints a message to standard error, +/// but this behavior can be customized with the [`set_oom_hook`] and +/// [`take_oom_hook`] functions. /// /// The hook is provided with a `Layout` struct which contains information /// about the allocation that failed. /// /// The OOM hook is a global resource. -pub fn set_oom_hook(hook: fn(Layout) -> !) { +pub fn set_oom_hook(hook: fn(Layout)) { HOOK.store(hook as *mut (), Ordering::SeqCst); } @@ -42,7 +43,7 @@ pub fn set_oom_hook(hook: fn(Layout) -> !) { /// *See also the function [`set_oom_hook`].* /// /// If no custom hook is registered, the default hook will be returned. -pub fn take_oom_hook() -> fn(Layout) -> ! { +pub fn take_oom_hook() -> fn(Layout) { let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst); if hook.is_null() { default_oom_hook @@ -51,8 +52,8 @@ pub fn take_oom_hook() -> fn(Layout) -> ! { } } -fn default_oom_hook(layout: Layout) -> ! { - rtabort!("memory allocation of {} bytes failed", layout.size()) +fn default_oom_hook(layout: Layout) { + dumb_print(format_args!("memory allocation of {} bytes failed", layout.size())); } #[cfg(not(test))] @@ -60,12 +61,13 @@ fn default_oom_hook(layout: Layout) -> ! { #[lang = "oom"] pub extern fn rust_oom(layout: Layout) -> ! { let hook = HOOK.load(Ordering::SeqCst); - let hook: fn(Layout) -> ! = if hook.is_null() { + let hook: fn(Layout) = if hook.is_null() { default_oom_hook } else { unsafe { mem::transmute(hook) } }; - hook(layout) + hook(layout); + unsafe { ::sys::abort_internal(); } } #[cfg(not(test))] diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index eed2debcaa2..c62a409ac02 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -8,11 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use alloc::{Global, Alloc, Layout, CollectionAllocErr, oom}; -use cmp; +use alloc::{Global, Alloc, Layout, LayoutErr, CollectionAllocErr, oom}; use hash::{BuildHasher, Hash, Hasher}; use marker; -use mem::{align_of, size_of, needs_drop}; +use mem::{size_of, needs_drop}; use mem; use ops::{Deref, DerefMut}; use ptr::{self, Unique, NonNull}; @@ -651,64 +650,12 @@ impl<K, V, M> GapThenFull<K, V, M> } } - -/// Rounds up to a multiple of a power of two. Returns the closest multiple -/// of `target_alignment` that is higher or equal to `unrounded`. -/// -/// # Panics -/// -/// Panics if `target_alignment` is not a power of two. -#[inline] -fn round_up_to_next(unrounded: usize, target_alignment: usize) -> usize { - assert!(target_alignment.is_power_of_two()); - (unrounded + target_alignment - 1) & !(target_alignment - 1) -} - -#[test] -fn test_rounding() { - assert_eq!(round_up_to_next(0, 4), 0); - assert_eq!(round_up_to_next(1, 4), 4); - assert_eq!(round_up_to_next(2, 4), 4); - assert_eq!(round_up_to_next(3, 4), 4); - assert_eq!(round_up_to_next(4, 4), 4); - assert_eq!(round_up_to_next(5, 4), 8); -} - -// Returns a tuple of (pairs_offset, end_of_pairs_offset), -// from the start of a mallocated array. -#[inline] -fn calculate_offsets(hashes_size: usize, - pairs_size: usize, - pairs_align: usize) - -> (usize, usize, bool) { - let pairs_offset = round_up_to_next(hashes_size, pairs_align); - let (end_of_pairs, oflo) = pairs_offset.overflowing_add(pairs_size); - - (pairs_offset, end_of_pairs, oflo) -} - -// Returns a tuple of (minimum required malloc alignment, -// array_size), from the start of a mallocated array. -fn calculate_allocation(hash_size: usize, - hash_align: usize, - pairs_size: usize, - pairs_align: usize) - -> (usize, usize, bool) { - let (_, end_of_pairs, oflo) = calculate_offsets(hash_size, pairs_size, pairs_align); - - let align = cmp::max(hash_align, pairs_align); - - (align, end_of_pairs, oflo) -} - -#[test] -fn test_offset_calculation() { - assert_eq!(calculate_allocation(128, 8, 16, 8), (8, 144, false)); - assert_eq!(calculate_allocation(3, 1, 2, 1), (1, 5, false)); - assert_eq!(calculate_allocation(6, 2, 12, 4), (4, 20, false)); - assert_eq!(calculate_offsets(128, 15, 4), (128, 143, false)); - assert_eq!(calculate_offsets(3, 2, 4), (4, 6, false)); - assert_eq!(calculate_offsets(6, 12, 4), (8, 20, false)); +// Returns a Layout which describes the allocation required for a hash table, +// and the offset of the array of (key, value) pairs in the allocation. +fn calculate_layout<K, V>(capacity: usize) -> Result<(Layout, usize), LayoutErr> { + let hashes = Layout::array::<HashUint>(capacity)?; + let pairs = Layout::array::<(K, V)>(capacity)?; + hashes.extend(pairs) } pub(crate) enum Fallibility { @@ -735,37 +682,11 @@ impl<K, V> RawTable<K, V> { }); } - // No need for `checked_mul` before a more restrictive check performed - // later in this method. - let hashes_size = capacity.wrapping_mul(size_of::<HashUint>()); - let pairs_size = capacity.wrapping_mul(size_of::<(K, V)>()); - // Allocating hashmaps is a little tricky. We need to allocate two // arrays, but since we know their sizes and alignments up front, // we just allocate a single array, and then have the subarrays // point into it. - // - // This is great in theory, but in practice getting the alignment - // right is a little subtle. Therefore, calculating offsets has been - // factored out into a different function. - let (alignment, size, oflo) = calculate_allocation(hashes_size, - align_of::<HashUint>(), - pairs_size, - align_of::<(K, V)>()); - if oflo { - return Err(CollectionAllocErr::CapacityOverflow); - } - - // One check for overflow that covers calculation and rounding of size. - let size_of_bucket = size_of::<HashUint>().checked_add(size_of::<(K, V)>()) - .ok_or(CollectionAllocErr::CapacityOverflow)?; - let capacity_mul_size_of_bucket = capacity.checked_mul(size_of_bucket); - if capacity_mul_size_of_bucket.is_none() || size < capacity_mul_size_of_bucket.unwrap() { - return Err(CollectionAllocErr::CapacityOverflow); - } - - let layout = Layout::from_size_align(size, alignment) - .map_err(|_| CollectionAllocErr::CapacityOverflow)?; + let (layout, _) = calculate_layout::<K, V>(capacity)?; let buffer = Global.alloc(layout).map_err(|e| match fallibility { Infallible => oom(layout), Fallible => e, @@ -790,18 +711,12 @@ impl<K, V> RawTable<K, V> { } fn raw_bucket_at(&self, index: usize) -> RawBucket<K, V> { - let hashes_size = self.capacity() * size_of::<HashUint>(); - let pairs_size = self.capacity() * size_of::<(K, V)>(); - - let (pairs_offset, _, oflo) = - calculate_offsets(hashes_size, pairs_size, align_of::<(K, V)>()); - debug_assert!(!oflo, "capacity overflow"); - + let (_, pairs_offset) = calculate_layout::<K, V>(self.capacity()).unwrap(); let buffer = self.hashes.ptr() as *mut u8; unsafe { RawBucket { hash_start: buffer as *mut HashUint, - pair_start: buffer.offset(pairs_offset as isize) as *const (K, V), + pair_start: buffer.add(pairs_offset) as *const (K, V), idx: index, _marker: marker::PhantomData, } @@ -1194,18 +1109,9 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable<K, V> { } } - let hashes_size = self.capacity() * size_of::<HashUint>(); - let pairs_size = self.capacity() * size_of::<(K, V)>(); - let (align, size, oflo) = calculate_allocation(hashes_size, - align_of::<HashUint>(), - pairs_size, - align_of::<(K, V)>()); - - debug_assert!(!oflo, "should be impossible"); - + let (layout, _) = calculate_layout::<K, V>(self.capacity()).unwrap(); unsafe { - Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).as_opaque(), - Layout::from_size_align(size, align).unwrap()); + Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).as_opaque(), layout); // Remember how everything was allocated out of one buffer // during initialization? We only need one call to free here. } diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 442a0873ae0..987687ea8e8 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -295,8 +295,6 @@ pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> { /// # Examples /// /// ```no_run -/// #![feature(fs_read_write)] -/// /// use std::fs; /// use std::net::SocketAddr; /// diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index c4092dcd388..774340388e1 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -890,8 +890,11 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { ) }; if let Err(ref copy_err) = copy_result { - if let Some(libc::ENOSYS) = copy_err.raw_os_error() { - HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed); + match copy_err.raw_os_error() { + Some(libc::ENOSYS) | Some(libc::EPERM) => { + HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed); + } + _ => {} } } copy_result @@ -902,9 +905,13 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { Ok(ret) => written += ret as u64, Err(err) => { match err.raw_os_error() { - Some(os_err) if os_err == libc::ENOSYS || os_err == libc::EXDEV => { - // Either kernel is too old or the files are not mounted on the same fs. - // Try again with fallback method + Some(os_err) if os_err == libc::ENOSYS + || os_err == libc::EXDEV + || os_err == libc::EPERM => { + // Try fallback io::copy if either: + // - Kernel version is < 4.5 (ENOSYS) + // - Files are mounted on different fs (EXDEV) + // - copy_file_range is disallowed, for example by seccomp (EPERM) assert_eq!(written, 0); let ret = io::copy(&mut reader, &mut writer)?; writer.set_permissions(perm)?; diff --git a/src/test/ui/hygiene/no_implicit_prelude.stderr b/src/test/ui/hygiene/no_implicit_prelude.stderr index 5753d1a32f7..b3d82e9094b 100644 --- a/src/test/ui/hygiene/no_implicit_prelude.stderr +++ b/src/test/ui/hygiene/no_implicit_prelude.stderr @@ -22,7 +22,7 @@ LL | ().clone() //~ ERROR no method named `clone` found | = help: items from traits can only be used if the trait is in scope = note: the following trait is implemented but not in scope, perhaps add a `use` for it: - candidate #1: `use std::clone::Clone;` + `use std::clone::Clone;` error: aborting due to 3 previous errors diff --git a/src/test/ui/hygiene/trait_items.stderr b/src/test/ui/hygiene/trait_items.stderr index 56d9c585d6f..1b2975bcf1c 100644 --- a/src/test/ui/hygiene/trait_items.stderr +++ b/src/test/ui/hygiene/trait_items.stderr @@ -9,7 +9,7 @@ LL | pub macro m() { ().f() } //~ ERROR no method named `f` found for type ` | = help: items from traits can only be used if the trait is in scope = note: the following trait is implemented but not in scope, perhaps add a `use` for it: - candidate #1: `use foo::T;` + `use foo::T;` error: aborting due to previous error diff --git a/src/test/ui/span/issue-7575.stderr b/src/test/ui/span/issue-7575.stderr index dc2cd4c2ddc..e31134f843b 100644 --- a/src/test/ui/span/issue-7575.stderr +++ b/src/test/ui/span/issue-7575.stderr @@ -2,10 +2,9 @@ error[E0599]: no method named `f9` found for type `usize` in the current scope --> $DIR/issue-7575.rs:74:18 | LL | u.f8(42) + u.f9(342) + m.fff(42) - | ^^ + | ^^ this is an associated function, not a method | = note: found the following associated functions; to be used as methods, functions must have a `self` parameter - = help: try with `usize::f9` note: candidate #1 is defined in the trait `CtxtFn` --> $DIR/issue-7575.rs:16:5 | @@ -37,11 +36,13 @@ LL | struct Myisize(isize); | ---------------------- method `fff` not found for this ... LL | u.f8(42) + u.f9(342) + m.fff(42) - | ^^^ + | --^^^ + | | | + | | this is an associated function, not a method + | help: use associated function syntax instead: `Myisize::fff` | = note: found the following associated functions; to be used as methods, functions must have a `self` parameter - = help: try with `Myisize::fff` -note: candidate #1 is defined in an impl for the type `Myisize` +note: the candidate is defined in an impl for the type `Myisize` --> $DIR/issue-7575.rs:51:5 | LL | fn fff(i: isize) -> isize { @@ -51,11 +52,10 @@ error[E0599]: no method named `is_str` found for type `T` in the current scope --> $DIR/issue-7575.rs:82:7 | LL | t.is_str() - | ^^^^^^ + | ^^^^^^ this is an associated function, not a method | = note: found the following associated functions; to be used as methods, functions must have a `self` parameter - = help: try with `T::is_str` -note: candidate #1 is defined in the trait `ManyImplTrait` +note: the candidate is defined in the trait `ManyImplTrait` --> $DIR/issue-7575.rs:57:5 | LL | fn is_str() -> bool { |
