diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index c470ae7eb3030..3465e386cd925 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -36,8 +36,10 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then s3url="s3://$SCCACHE_BUCKET/docker/$cksum" url="https://s3-us-west-1.amazonaws.com/$SCCACHE_BUCKET/docker/$cksum" echo "Attempting to download $s3url" + rm -f /tmp/rustci_docker_cache set +e - loaded_images=$(curl $url | docker load | sed 's/.* sha/sha/') + retry curl -f -L -C - -o /tmp/rustci_docker_cache "$url" + loaded_images=$(docker load -i /tmp/rustci_docker_cache | sed 's/.* sha/sha/') set -e echo "Downloaded containers:\n$loaded_images" fi diff --git a/src/ci/docker/x86_64-gnu-tools/checktools.sh b/src/ci/docker/x86_64-gnu-tools/checktools.sh index 3fed0175371a7..d71d5daf8113b 100755 --- a/src/ci/docker/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/x86_64-gnu-tools/checktools.sh @@ -23,6 +23,8 @@ SIX_WEEK_CYCLE="$(( ($(date +%s) / 604800 - 3) % 6 ))" touch "$TOOLSTATE_FILE" +# Try to test all the tools and store the build/test success in the TOOLSTATE_FILE + set +e python2.7 "$X_PY" test --no-fail-fast \ src/doc/book \ @@ -38,6 +40,7 @@ set -e cat "$TOOLSTATE_FILE" echo +# This function checks that if a tool's submodule changed, the tool's state must improve verify_status() { echo "Verifying status of $1..." if echo "$CHANGED_FILES" | grep -q "^M[[:blank:]]$2$"; then @@ -57,17 +60,36 @@ verify_status() { fi } +# deduplicates the submodule check and the assertion that on beta some tools MUST be passing +check_dispatch() { + if [ "$1" = submodule_changed ]; then + # ignore $2 (branch id) + verify_status $3 $4 + elif [ "$2" = beta ]; then + echo "Requiring test passing for $3..." + if grep -q '"'"$3"'":"\(test\|build\)-fail"' "$TOOLSTATE_FILE"; then + exit 4 + fi + fi +} + +# list all tools here +status_check() { + check_dispatch $1 beta book src/doc/book + check_dispatch $1 beta nomicon src/doc/nomicon + check_dispatch $1 beta reference src/doc/reference + check_dispatch $1 beta rust-by-example src/doc/rust-by-example + check_dispatch $1 beta rls src/tool/rls + check_dispatch $1 beta rustfmt src/tool/rustfmt + # these tools are not required for beta to successfully branch + check_dispatch $1 nightly clippy-driver src/tool/clippy + check_dispatch $1 nightly miri src/tool/miri +} + # If this PR is intended to update one of these tools, do not let the build pass # when they do not test-pass. -verify_status book src/doc/book -verify_status nomicon src/doc/nomicon -verify_status reference src/doc/reference -verify_status rust-by-example src/doc/rust-by-example -verify_status rls src/tool/rls -verify_status rustfmt src/tool/rustfmt -verify_status clippy-driver src/tool/clippy -verify_status miri src/tool/miri +status_check "submodule_changed" if [ "$RUST_RELEASE_CHANNEL" = nightly -a -n "${TOOLSTATE_REPO_ACCESS_TOKEN+is_set}" ]; then . "$(dirname $0)/repo.sh" @@ -86,6 +108,6 @@ $COMMIT\t$(cat "$TOOLSTATE_FILE") exit 0 fi -if grep -q fail "$TOOLSTATE_FILE"; then - exit 4 -fi +# abort compilation if an important tool doesn't build +# (this code is reachable if not on the nightly channel) +status_check "beta_required" diff --git a/src/ci/shared.sh b/src/ci/shared.sh index 4a08683e3ee86..bb6945f0fd6bb 100644 --- a/src/ci/shared.sh +++ b/src/ci/shared.sh @@ -21,11 +21,12 @@ function retry { while true; do "$@" && break || { if [[ $n -lt $max ]]; then + sleep $n # don't retry immediately ((n++)) echo "Command failed. Attempt $n/$max:" else echo "The command has failed after $n attempts." - exit 1 + return 1 fi } done diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 4d73d3aa07e66..5c6f6b22aae06 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -385,26 +385,7 @@ impl RawVec { } } - /// Ensures that the buffer contains at least enough space to hold - /// `used_cap + needed_extra_cap` elements. If it doesn't already, - /// will reallocate the minimum possible amount of memory necessary. - /// Generally this will be exactly the amount of memory necessary, - /// but in principle the allocator is free to give back more than - /// we asked for. - /// - /// If `used_cap` exceeds `self.cap()`, this may fail to actually allocate - /// the requested space. This is not really unsafe, but the unsafe - /// code *you* write that relies on the behavior of this function may break. - /// - /// # Panics - /// - /// * Panics if the requested capacity exceeds `usize::MAX` bytes. - /// * Panics on 32-bit platforms if the requested capacity exceeds - /// `isize::MAX` bytes. - /// - /// # Aborts - /// - /// Aborts on OOM + /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. pub fn try_reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) -> Result<(), CollectionAllocErr> { @@ -441,6 +422,26 @@ impl RawVec { } } + /// Ensures that the buffer contains at least enough space to hold + /// `used_cap + needed_extra_cap` elements. If it doesn't already, + /// will reallocate the minimum possible amount of memory necessary. + /// Generally this will be exactly the amount of memory necessary, + /// but in principle the allocator is free to give back more than + /// we asked for. + /// + /// If `used_cap` exceeds `self.cap()`, this may fail to actually allocate + /// the requested space. This is not really unsafe, but the unsafe + /// code *you* write that relies on the behavior of this function may break. + /// + /// # Panics + /// + /// * Panics if the requested capacity exceeds `usize::MAX` bytes. + /// * Panics on 32-bit platforms if the requested capacity exceeds + /// `isize::MAX` bytes. + /// + /// # Aborts + /// + /// Aborts on OOM pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve_exact(used_cap, needed_extra_cap) { Err(CapacityOverflow) => capacity_overflow(), @@ -463,6 +464,42 @@ impl RawVec { Ok(cmp::max(double_cap, required_cap)) } + /// The same as `reserve`, but returns on errors instead of panicking or aborting. + pub fn try_reserve(&mut self, used_cap: usize, needed_extra_cap: usize) + -> Result<(), CollectionAllocErr> { + unsafe { + // NOTE: we don't early branch on ZSTs here because we want this + // to actually catch "asking for more than usize::MAX" in that case. + // If we make it past the first branch then we are guaranteed to + // panic. + + // Don't actually need any more capacity. + // Wrapping in case they give a bad `used_cap` + if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { + return Ok(()); + } + + let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)?; + let new_layout = Layout::array::(new_cap).map_err(|_| CapacityOverflow)?; + + // FIXME: may crash and burn on over-reserve + alloc_guard(new_layout.size())?; + + let res = match self.current_layout() { + Some(layout) => { + debug_assert!(new_layout.align() == layout.align()); + self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) + } + None => self.a.alloc(new_layout), + }; + + self.ptr = res?.cast().into(); + self.cap = new_cap; + + Ok(()) + } + } + /// Ensures that the buffer contains at least enough space to hold /// `used_cap + needed_extra_cap` elements. If it doesn't already have /// enough capacity, will reallocate enough space plus comfortable slack @@ -515,42 +552,6 @@ impl RawVec { /// # vector.push_all(&[1, 3, 5, 7, 9]); /// # } /// ``` - pub fn try_reserve(&mut self, used_cap: usize, needed_extra_cap: usize) - -> Result<(), CollectionAllocErr> { - unsafe { - // NOTE: we don't early branch on ZSTs here because we want this - // to actually catch "asking for more than usize::MAX" in that case. - // If we make it past the first branch then we are guaranteed to - // panic. - - // Don't actually need any more capacity. - // Wrapping in case they give a bad `used_cap` - if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { - return Ok(()); - } - - let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)?; - let new_layout = Layout::array::(new_cap).map_err(|_| CapacityOverflow)?; - - // FIXME: may crash and burn on over-reserve - alloc_guard(new_layout.size())?; - - let res = match self.current_layout() { - Some(layout) => { - debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) - } - None => self.a.alloc(new_layout), - }; - - self.ptr = res?.cast().into(); - self.cap = new_cap; - - Ok(()) - } - } - - /// The same as try_reserve, but errors are lowered to a call to oom(). pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve(used_cap, needed_extra_cap) { Err(CapacityOverflow) => capacity_overflow(), diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index d50a3458f20d1..6caf12aa7eb81 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -10,6 +10,8 @@ //! A dynamically-sized view into a contiguous sequence, `[T]`. //! +//! *[See also the slice primitive type](../../std/primitive.slice.html).* +//! //! Slices are a view into a block of memory represented as a pointer and a //! length. //! @@ -78,8 +80,6 @@ //! * Further methods that return iterators are [`.split`], [`.splitn`], //! [`.chunks`], [`.windows`] and more. //! -//! *[See also the slice primitive type](../../std/primitive.slice.html).* -//! //! [`Clone`]: ../../std/clone/trait.Clone.html //! [`Eq`]: ../../std/cmp/trait.Eq.html //! [`Ord`]: ../../std/cmp/trait.Ord.html diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 9e693c89be90d..42efdea74b1ab 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -10,6 +10,8 @@ //! Unicode string slices. //! +//! *[See also the `str` primitive type](../../std/primitive.str.html).* +//! //! The `&str` type is one of the two main string types, the other being `String`. //! Unlike its `String` counterpart, its contents are borrowed. //! @@ -29,8 +31,6 @@ //! ``` //! let hello_world: &'static str = "Hello, world!"; //! ``` -//! -//! *[See also the `str` primitive type](../../std/primitive.str.html).* #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 35d0a69a05abe..690cbcb559bbf 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2533,9 +2533,11 @@ impl<'a, T> Drop for Drain<'a, T> { // memmove back untouched tail, update to new length let start = source_vec.len(); let tail = self.tail_start; - let src = source_vec.as_ptr().offset(tail as isize); - let dst = source_vec.as_mut_ptr().offset(start as isize); - ptr::copy(src, dst, self.tail_len); + if tail != start { + let src = source_vec.as_ptr().offset(tail as isize); + let dst = source_vec.as_mut_ptr().offset(start as isize); + ptr::copy(src, dst, self.tail_len); + } source_vec.set_len(start + self.tail_len); } } diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 672119eba7f9d..4a7dc13f0f2ca 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -11,9 +11,9 @@ //! This module provides constants which are specific to the implementation //! of the `f32` floating point data type. //! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! //! *[See also the `f32` primitive type](../../std/primitive.f32.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 220b23a1e6a01..801de5e87bd10 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -11,9 +11,9 @@ //! This module provides constants which are specific to the implementation //! of the `f64` floating point data type. //! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! //! *[See also the `f64` primitive type](../../std/primitive.f64.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index b01a769eda7fd..697e6a3efde28 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -411,6 +411,21 @@ impl RangeInclusive { pub fn end(&self) -> &Idx { &self.end } + + /// Destructures the RangeInclusive into (lower bound, upper (inclusive) bound). + /// + /// # Examples + /// + /// ``` + /// #![feature(inclusive_range_methods)] + /// + /// assert_eq!((3..=5).into_inner(), (3, 5)); + /// ``` + #[unstable(feature = "inclusive_range_methods", issue = "49022")] + #[inline] + pub fn into_inner(self) -> (Idx, Idx) { + (self.start, self.end) + } } #[stable(feature = "inclusive_range", since = "1.26.0")] diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 8451e5987e908..c55df9b39b81b 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -755,6 +755,7 @@ impl Term { } } + // FIXME: Remove this, do not stabilize /// Get a reference to the interned string. #[unstable(feature = "proc_macro", issue = "38356")] pub fn as_str(&self) -> &str { @@ -779,7 +780,7 @@ impl Term { #[unstable(feature = "proc_macro", issue = "38356")] impl fmt::Display for Term { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.as_str().fmt(f) + self.sym.as_str().fmt(f) } } @@ -1176,7 +1177,7 @@ impl TokenTree { }, self::TokenTree::Term(tt) => { let ident = ast::Ident::new(tt.sym, tt.span.0); - let sym_str = tt.sym.as_str(); + let sym_str = tt.sym.to_string(); let token = if sym_str.starts_with("'") { Lifetime(ident) } else if sym_str.starts_with("r#") { diff --git a/src/libproc_macro/quote.rs b/src/libproc_macro/quote.rs index d1f8e75192ae1..70f0b07839981 100644 --- a/src/libproc_macro/quote.rs +++ b/src/libproc_macro/quote.rs @@ -183,7 +183,7 @@ impl Quote for Op { impl Quote for Term { fn quote(self) -> TokenStream { - quote!(::Term::new((quote self.as_str()), (quote self.span()))) + quote!(::Term::new((quote self.sym.as_str()), (quote self.span()))) } } diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs index 22ab1b15c8b8e..03aff64100558 100644 --- a/src/librustc/dep_graph/graph.rs +++ b/src/librustc/dep_graph/graph.rs @@ -12,6 +12,7 @@ use errors::DiagnosticBuilder; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::small_vec::SmallVec; use rustc_data_structures::sync::{Lrc, RwLock, ReadGuard, Lock}; use std::env; use std::hash::Hash; @@ -77,7 +78,7 @@ struct DepGraphData { /// things available to us. If we find that they are not dirty, we /// load the path to the file storing those work-products here into /// this map. We can later look for and extract that data. - previous_work_products: RwLock>, + previous_work_products: FxHashMap, /// Work-products that we generate in this run. work_products: RwLock>, @@ -90,7 +91,8 @@ struct DepGraphData { impl DepGraph { - pub fn new(prev_graph: PreviousDepGraph) -> DepGraph { + pub fn new(prev_graph: PreviousDepGraph, + prev_work_products: FxHashMap) -> DepGraph { // Pre-allocate the fingerprints array. We over-allocate a little so // that we hopefully don't have to re-allocate during this compilation // session. @@ -100,7 +102,7 @@ impl DepGraph { (prev_graph_node_count * 115) / 100); DepGraph { data: Some(Lrc::new(DepGraphData { - previous_work_products: RwLock::new(FxHashMap()), + previous_work_products: prev_work_products, work_products: RwLock::new(FxHashMap()), dep_node_debug: Lock::new(FxHashMap()), current: Lock::new(CurrentDepGraph::new()), @@ -131,7 +133,7 @@ impl DepGraph { let mut edges = Vec::new(); for (index, edge_targets) in current_dep_graph.edges.iter_enumerated() { let from = current_dep_graph.nodes[index]; - for &edge_target in edge_targets { + for &edge_target in edge_targets.iter() { let to = current_dep_graph.nodes[edge_target]; edges.push((from, to)); } @@ -209,7 +211,7 @@ impl DepGraph { self.with_task_impl(key, cx, arg, false, task, |key| OpenTask::Regular(Lock::new(RegularOpenTask { node: key, - reads: Vec::new(), + reads: SmallVec::new(), read_set: FxHashSet(), })), |data, key, task| data.borrow_mut().complete_task(key, task)) @@ -230,7 +232,7 @@ impl DepGraph { self.with_task_impl(key, cx, input, true, identity_fn, |_| OpenTask::Ignore, - |data, key, _| data.borrow_mut().alloc_node(key, Vec::new())) + |data, key, _| data.borrow_mut().alloc_node(key, SmallVec::new())) } fn with_task_impl<'gcx, C, A, R>( @@ -353,7 +355,7 @@ impl DepGraph { if let Some(ref data) = self.data { let (result, open_task) = ty::tls::with_context(|icx| { let task = OpenTask::Anon(Lock::new(AnonOpenTask { - reads: Vec::new(), + reads: SmallVec::new(), read_set: FxHashSet(), })); @@ -460,19 +462,6 @@ impl DepGraph { self.data.as_ref().unwrap().previous.node_to_index(dep_node) } - /// Indicates that a previous work product exists for `v`. This is - /// invoked during initial start-up based on what nodes are clean - /// (and what files exist in the incr. directory). - pub fn insert_previous_work_product(&self, v: &WorkProductId, data: WorkProduct) { - debug!("insert_previous_work_product({:?}, {:?})", v, data); - self.data - .as_ref() - .unwrap() - .previous_work_products - .borrow_mut() - .insert(v.clone(), data); - } - /// Indicates that we created the given work-product in this run /// for `v`. This record will be preserved and loaded in the next /// run. @@ -492,7 +481,7 @@ impl DepGraph { self.data .as_ref() .and_then(|data| { - data.previous_work_products.borrow().get(v).cloned() + data.previous_work_products.get(v).cloned() }) } @@ -504,8 +493,8 @@ impl DepGraph { /// Access the map of work-products created during the cached run. Only /// used during saving of the dep-graph. - pub fn previous_work_products(&self) -> ReadGuard> { - self.data.as_ref().unwrap().previous_work_products.borrow() + pub fn previous_work_products(&self) -> &FxHashMap { + &self.data.as_ref().unwrap().previous_work_products } #[inline(always)] @@ -534,15 +523,9 @@ impl DepGraph { } pub fn serialize(&self) -> SerializedDepGraph { - let mut fingerprints = self.fingerprints.borrow_mut(); let current_dep_graph = self.data.as_ref().unwrap().current.borrow(); - // Make sure we don't run out of bounds below. - if current_dep_graph.nodes.len() > fingerprints.len() { - fingerprints.resize(current_dep_graph.nodes.len(), Fingerprint::ZERO); - } - - let fingerprints = fingerprints.clone().convert_index_type(); + let fingerprints = self.fingerprints.borrow().clone().convert_index_type(); let nodes = current_dep_graph.nodes.clone().convert_index_type(); let total_edge_count: usize = current_dep_graph.edges.iter() @@ -626,7 +609,7 @@ impl DepGraph { debug_assert!(data.colors.borrow().get(prev_dep_node_index).is_none()); - let mut current_deps = Vec::new(); + let mut current_deps = SmallVec::new(); for &dep_dep_node_index in prev_deps { let dep_dep_node_color = data.colors.borrow().get(dep_dep_node_index); @@ -923,7 +906,7 @@ pub enum WorkProductFileKind { pub(super) struct CurrentDepGraph { nodes: IndexVec, - edges: IndexVec>, + edges: IndexVec>, node_to_node_index: FxHashMap, forbidden_edge: Option, @@ -1061,7 +1044,7 @@ impl CurrentDepGraph { } = task { debug_assert_eq!(node, key); let krate_idx = self.node_to_node_index[&DepNode::new_no_params(DepKind::Krate)]; - self.alloc_node(node, vec![krate_idx]) + self.alloc_node(node, SmallVec::one(krate_idx)) } else { bug!("complete_eval_always_task() - Expected eval always task to be popped"); } @@ -1107,7 +1090,7 @@ impl CurrentDepGraph { fn alloc_node(&mut self, dep_node: DepNode, - edges: Vec) + edges: SmallVec<[DepNodeIndex; 8]>) -> DepNodeIndex { debug_assert_eq!(self.edges.len(), self.nodes.len()); debug_assert_eq!(self.node_to_node_index.len(), self.nodes.len()); @@ -1122,12 +1105,12 @@ impl CurrentDepGraph { pub struct RegularOpenTask { node: DepNode, - reads: Vec, + reads: SmallVec<[DepNodeIndex; 8]>, read_set: FxHashSet, } pub struct AnonOpenTask { - reads: Vec, + reads: SmallVec<[DepNodeIndex; 8]>, read_set: FxHashSet, } diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 83dac033f9408..245663494ddef 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -95,6 +95,23 @@ pub enum Lto { Fat, } +#[derive(Clone, PartialEq, Hash)] +pub enum CrossLangLto { + LinkerPlugin(PathBuf), + NoLink, + Disabled +} + +impl CrossLangLto { + pub fn embed_bitcode(&self) -> bool { + match *self { + CrossLangLto::LinkerPlugin(_) | + CrossLangLto::NoLink => true, + CrossLangLto::Disabled => false, + } + } +} + #[derive(Clone, Copy, PartialEq, Hash)] pub enum DebugInfoLevel { NoDebugInfo, @@ -412,6 +429,7 @@ top_level_options!( // Remap source path prefixes in all output (messages, object files, debug, etc) remap_path_prefix: Vec<(PathBuf, PathBuf)> [UNTRACKED], + edition: Edition [TRACKED], } ); @@ -777,11 +795,15 @@ macro_rules! options { Some("`string` or `string=string`"); pub const parse_lto: Option<&'static str> = Some("one of `thin`, `fat`, or omitted"); + pub const parse_cross_lang_lto: Option<&'static str> = + Some("either a boolean (`yes`, `no`, `on`, `off`, etc), `no-link`, \ + or the path to the linker plugin"); } #[allow(dead_code)] mod $mod_set { - use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer, Lto}; + use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer, Lto, + CrossLangLto}; use rustc_target::spec::{LinkerFlavor, PanicStrategy, RelroLevel}; use std::path::PathBuf; @@ -986,6 +1008,26 @@ macro_rules! options { true } + fn parse_cross_lang_lto(slot: &mut CrossLangLto, v: Option<&str>) -> bool { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + *slot = if bool_arg.unwrap() { + CrossLangLto::NoLink + } else { + CrossLangLto::Disabled + }; + return true + } + } + + *slot = match v { + None | + Some("no-link") => CrossLangLto::NoLink, + Some(path) => CrossLangLto::LinkerPlugin(PathBuf::from(path)), + }; + true + } } ) } @@ -1295,7 +1337,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "make the current crate share its generic instantiations"), chalk: bool = (false, parse_bool, [TRACKED], "enable the experimental Chalk-based trait solving engine"), - cross_lang_lto: bool = (false, parse_bool, [TRACKED], + cross_lang_lto: CrossLangLto = (CrossLangLto::Disabled, parse_cross_lang_lto, [TRACKED], "generate build artifacts that are compatible with linker-based LTO."), } @@ -2327,7 +2369,7 @@ mod dep_tracking { use std::path::PathBuf; use std::collections::hash_map::DefaultHasher; use super::{CrateType, DebugInfoLevel, ErrorOutputType, Lto, OptLevel, OutputTypes, - Passes, Sanitizer}; + Passes, Sanitizer, CrossLangLto}; use syntax::feature_gate::UnstableFeatures; use rustc_target::spec::{PanicStrategy, RelroLevel, TargetTriple}; use syntax::edition::Edition; @@ -2391,6 +2433,7 @@ mod dep_tracking { impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(TargetTriple); impl_dep_tracking_hash_via_hash!(Edition); + impl_dep_tracking_hash_via_hash!(CrossLangLto); impl_dep_tracking_hash_for_sortable_vec_of!(String); impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf); @@ -2455,7 +2498,7 @@ mod tests { use lint; use middle::cstore; use session::config::{build_configuration, build_session_options_and_crate_config}; - use session::config::Lto; + use session::config::{Lto, CrossLangLto}; use session::build_session; use std::collections::{BTreeMap, BTreeSet}; use std::iter::FromIterator; @@ -3111,6 +3154,10 @@ mod tests { opts = reference.clone(); opts.debugging_opts.relro_level = Some(RelroLevel::Full); assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.debugging_opts.cross_lang_lto = CrossLangLto::NoLink; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); } #[test] diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 2ab72ba20bf4f..23f84881c7980 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -657,6 +657,13 @@ impl Session { } } + pub fn target_cpu(&self) -> &str { + match self.opts.cg.target_cpu { + Some(ref s) => &**s, + None => &*self.target.target.options.cpu + } + } + pub fn must_not_eliminate_frame_pointers(&self) -> bool { if let Some(x) = self.opts.cg.force_frame_pointers { x diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 2fb811eba1e9a..1e74039503d51 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -980,15 +980,16 @@ where let dep_graph = match future_dep_graph { None => DepGraph::new_disabled(), Some(future) => { - let prev_graph = time(sess, "blocked while dep-graph loading finishes", || { - future - .open() - .unwrap_or_else(|e| rustc_incremental::LoadResult::Error { - message: format!("could not decode incremental cache: {:?}", e), - }) - .open(sess) - }); - DepGraph::new(prev_graph) + let (prev_graph, prev_work_products) = + time(sess, "blocked while dep-graph loading finishes", || { + future + .open() + .unwrap_or_else(|e| rustc_incremental::LoadResult::Error { + message: format!("could not decode incremental cache: {:?}", e), + }) + .open(sess) + }); + DepGraph::new(prev_graph, prev_work_products) } }; let hir_forest = time(sess, "lowering ast -> hir", || { diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index 44d6e532f79bb..f846759545eb9 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs @@ -10,7 +10,8 @@ //! Code to save/load the dep-graph from files. -use rustc::dep_graph::{PreviousDepGraph, SerializedDepGraph}; +use rustc_data_structures::fx::FxHashMap; +use rustc::dep_graph::{PreviousDepGraph, SerializedDepGraph, WorkProduct, WorkProductId}; use rustc::session::Session; use rustc::ty::TyCtxt; use rustc::ty::maps::OnDiskCache; @@ -32,65 +33,22 @@ pub fn dep_graph_tcx_init<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.allocate_metadata_dep_nodes(); tcx.precompute_in_scope_traits_hashes(); - - if tcx.sess.incr_comp_session_dir_opt().is_none() { - // If we are only building with -Zquery-dep-graph but without an actual - // incr. comp. session directory, we exit here. Otherwise we'd fail - // when trying to load work products. - return - } - - let work_products_path = work_products_path(tcx.sess); - let load_result = load_data(tcx.sess.opts.debugging_opts.incremental_info, &work_products_path); - - if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result { - // Decode the list of work_products - let mut work_product_decoder = Decoder::new(&work_products_data[..], start_pos); - let work_products: Vec = - RustcDecodable::decode(&mut work_product_decoder).unwrap_or_else(|e| { - let msg = format!("Error decoding `work-products` from incremental \ - compilation session directory: {}", e); - tcx.sess.fatal(&msg[..]) - }); - - for swp in work_products { - let mut all_files_exist = true; - for &(_, ref file_name) in swp.work_product.saved_files.iter() { - let path = in_incr_comp_dir_sess(tcx.sess, file_name); - if !path.exists() { - all_files_exist = false; - - if tcx.sess.opts.debugging_opts.incremental_info { - eprintln!("incremental: could not find file for work \ - product: {}", path.display()); - } - } - } - - if all_files_exist { - debug!("reconcile_work_products: all files for {:?} exist", swp); - tcx.dep_graph.insert_previous_work_product(&swp.id, swp.work_product); - } else { - debug!("reconcile_work_products: some file for {:?} does not exist", swp); - delete_dirty_work_product(tcx, swp); - } - } - } } +type WorkProductMap = FxHashMap; + pub enum LoadResult { Ok { data: T }, DataOutOfDate, Error { message: String }, } - -impl LoadResult { - pub fn open(self, sess: &Session) -> PreviousDepGraph { +impl LoadResult<(PreviousDepGraph, WorkProductMap)> { + pub fn open(self, sess: &Session) -> (PreviousDepGraph, WorkProductMap) { match self { LoadResult::Error { message } => { sess.warn(&message); - PreviousDepGraph::new(SerializedDepGraph::new()) + (PreviousDepGraph::new(SerializedDepGraph::new()), FxHashMap()) }, LoadResult::DataOutOfDate => { if let Err(err) = delete_all_session_dir_contents(sess) { @@ -98,7 +56,7 @@ impl LoadResult { incremental compilation session directory contents `{}`: {}.", dep_graph_path(sess).display(), err)); } - PreviousDepGraph::new(SerializedDepGraph::new()) + (PreviousDepGraph::new(SerializedDepGraph::new()), FxHashMap()) } LoadResult::Ok { data } => data } @@ -125,10 +83,10 @@ fn load_data(report_incremental_info: bool, path: &Path) -> LoadResult<(Vec, } } -fn delete_dirty_work_product(tcx: TyCtxt, +fn delete_dirty_work_product(sess: &Session, swp: SerializedWorkProduct) { debug!("delete_dirty_work_product({:?})", swp); - work_product::delete_workproduct_files(tcx.sess, &swp.work_product); + work_product::delete_workproduct_files(sess, &swp.work_product); } /// Either a result that has already be computed or a @@ -149,7 +107,7 @@ impl MaybeAsync { /// Launch a thread and load the dependency graph in the background. pub fn load_dep_graph(sess: &Session) -> - MaybeAsync> + MaybeAsync> { // Since `sess` isn't `Sync`, we perform all accesses to `sess` // before we fire the background thread. @@ -159,7 +117,7 @@ pub fn load_dep_graph(sess: &Session) -> if sess.opts.incremental.is_none() { // No incremental compilation. return MaybeAsync::Sync(LoadResult::Ok { - data: PreviousDepGraph::new(SerializedDepGraph::new()) + data: (PreviousDepGraph::new(SerializedDepGraph::new()), FxHashMap()) }); } @@ -169,6 +127,50 @@ pub fn load_dep_graph(sess: &Session) -> let report_incremental_info = sess.opts.debugging_opts.incremental_info; let expected_hash = sess.opts.dep_tracking_hash(); + let mut prev_work_products = FxHashMap(); + + // If we are only building with -Zquery-dep-graph but without an actual + // incr. comp. session directory, we skip this. Otherwise we'd fail + // when trying to load work products. + if sess.incr_comp_session_dir_opt().is_some() { + let work_products_path = work_products_path(sess); + let load_result = load_data(report_incremental_info, &work_products_path); + + if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result { + // Decode the list of work_products + let mut work_product_decoder = Decoder::new(&work_products_data[..], start_pos); + let work_products: Vec = + RustcDecodable::decode(&mut work_product_decoder).unwrap_or_else(|e| { + let msg = format!("Error decoding `work-products` from incremental \ + compilation session directory: {}", e); + sess.fatal(&msg[..]) + }); + + for swp in work_products { + let mut all_files_exist = true; + for &(_, ref file_name) in swp.work_product.saved_files.iter() { + let path = in_incr_comp_dir_sess(sess, file_name); + if !path.exists() { + all_files_exist = false; + + if sess.opts.debugging_opts.incremental_info { + eprintln!("incremental: could not find file for work \ + product: {}", path.display()); + } + } + } + + if all_files_exist { + debug!("reconcile_work_products: all files for {:?} exist", swp); + prev_work_products.insert(swp.id, swp.work_product); + } else { + debug!("reconcile_work_products: some file for {:?} does not exist", swp); + delete_dirty_work_product(sess, swp); + } + } + } + } + MaybeAsync::Async(std::thread::spawn(move || { time_ext(time_passes, None, "background load prev dep-graph", move || { match load_data(report_incremental_info, &path) { @@ -195,7 +197,7 @@ pub fn load_dep_graph(sess: &Session) -> let dep_graph = SerializedDepGraph::decode(&mut decoder) .expect("Error reading cached dep-graph"); - LoadResult::Ok { data: PreviousDepGraph::new(dep_graph) } + LoadResult::Ok { data: (PreviousDepGraph::new(dep_graph), prev_work_products) } } } }) diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index d0237071a6058..87b3a2dc69ff9 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -51,7 +51,6 @@ pub struct Library { pub struct CrateLoader<'a> { pub sess: &'a Session, cstore: &'a CStore, - next_crate_num: CrateNum, local_crate_name: Symbol, } @@ -102,7 +101,6 @@ impl<'a> CrateLoader<'a> { CrateLoader { sess, cstore, - next_crate_num: cstore.next_crate_num(), local_crate_name: Symbol::intern(local_crate_name), } } @@ -198,8 +196,7 @@ impl<'a> CrateLoader<'a> { self.verify_no_symbol_conflicts(span, &crate_root); // Claim this crate number and cache it - let cnum = self.next_crate_num; - self.next_crate_num = CrateNum::from_u32(cnum.as_u32() + 1); + let cnum = self.cstore.alloc_new_crate_num(); // Stash paths for top-most crate locally if necessary. let crate_paths = if root.is_none() { @@ -219,6 +216,8 @@ impl<'a> CrateLoader<'a> { let cnum_map = self.resolve_crate_deps(root, &crate_root, &metadata, cnum, span, dep_kind); + let dependencies: Vec = cnum_map.iter().cloned().collect(); + let def_path_table = record_time(&self.sess.perf_stats.decode_def_path_tables_time, || { crate_root.def_path_table.decode((&metadata, self.sess)) }); @@ -239,8 +238,9 @@ impl<'a> CrateLoader<'a> { }), root: crate_root, blob: metadata, - cnum_map: Lock::new(cnum_map), + cnum_map, cnum, + dependencies: Lock::new(dependencies), codemap_import_info: RwLock::new(vec![]), attribute_cache: Lock::new([Vec::new(), Vec::new()]), dep_kind: Lock::new(dep_kind), @@ -392,7 +392,7 @@ impl<'a> CrateLoader<'a> { // Propagate the extern crate info to dependencies. extern_crate.direct = false; - for &dep_cnum in cmeta.cnum_map.borrow().iter() { + for &dep_cnum in cmeta.dependencies.borrow().iter() { self.update_extern_crate(dep_cnum, extern_crate, visited); } } @@ -1040,7 +1040,7 @@ impl<'a> CrateLoader<'a> { } info!("injecting a dep from {} to {}", cnum, krate); - data.cnum_map.borrow_mut().push(krate); + data.dependencies.borrow_mut().push(krate); }); } } diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index 64bbcf436cb9e..9bbce563b61dc 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -64,8 +64,9 @@ pub struct CrateMetadata { pub extern_crate: Lock>, pub blob: MetadataBlob, - pub cnum_map: Lock, + pub cnum_map: CrateNumMap, pub cnum: CrateNum, + pub dependencies: Lock>, pub codemap_import_info: RwLock>, pub attribute_cache: Lock<[Vec>>; 2]>, @@ -96,32 +97,34 @@ pub struct CStore { impl CStore { pub fn new(metadata_loader: Box) -> CStore { CStore { - metas: RwLock::new(IndexVec::new()), + // We add an empty entry for LOCAL_CRATE (which maps to zero) in + // order to make array indices in `metas` match with the + // corresponding `CrateNum`. This first entry will always remain + // `None`. + metas: RwLock::new(IndexVec::from_elem_n(None, 1)), extern_mod_crate_map: Lock::new(FxHashMap()), metadata_loader, } } - /// You cannot use this function to allocate a CrateNum in a thread-safe manner. - /// It is currently only used in CrateLoader which is single-threaded code. - pub fn next_crate_num(&self) -> CrateNum { - CrateNum::new(self.metas.borrow().len() + 1) + pub(super) fn alloc_new_crate_num(&self) -> CrateNum { + let mut metas = self.metas.borrow_mut(); + let cnum = CrateNum::new(metas.len()); + metas.push(None); + cnum } - pub fn get_crate_data(&self, cnum: CrateNum) -> Lrc { + pub(super) fn get_crate_data(&self, cnum: CrateNum) -> Lrc { self.metas.borrow()[cnum].clone().unwrap() } - pub fn set_crate_data(&self, cnum: CrateNum, data: Lrc) { - use rustc_data_structures::indexed_vec::Idx; - let mut met = self.metas.borrow_mut(); - while met.len() <= cnum.index() { - met.push(None); - } - met[cnum] = Some(data); + pub(super) fn set_crate_data(&self, cnum: CrateNum, data: Lrc) { + let mut metas = self.metas.borrow_mut(); + assert!(metas[cnum].is_none(), "Overwriting crate metadata entry"); + metas[cnum] = Some(data); } - pub fn iter_crate_data(&self, mut i: I) + pub(super) fn iter_crate_data(&self, mut i: I) where I: FnMut(CrateNum, &Lrc) { for (k, v) in self.metas.borrow().iter_enumerated() { @@ -131,20 +134,22 @@ impl CStore { } } - pub fn crate_dependencies_in_rpo(&self, krate: CrateNum) -> Vec { + pub(super) fn crate_dependencies_in_rpo(&self, krate: CrateNum) -> Vec { let mut ordering = Vec::new(); self.push_dependencies_in_postorder(&mut ordering, krate); ordering.reverse(); ordering } - pub fn push_dependencies_in_postorder(&self, ordering: &mut Vec, krate: CrateNum) { + pub(super) fn push_dependencies_in_postorder(&self, + ordering: &mut Vec, + krate: CrateNum) { if ordering.contains(&krate) { return; } let data = self.get_crate_data(krate); - for &dep in data.cnum_map.borrow().iter() { + for &dep in data.dependencies.borrow().iter() { if dep != krate { self.push_dependencies_in_postorder(ordering, dep); } @@ -153,7 +158,7 @@ impl CStore { ordering.push(krate); } - pub fn do_postorder_cnums_untracked(&self) -> Vec { + pub(super) fn do_postorder_cnums_untracked(&self) -> Vec { let mut ordering = Vec::new(); for (num, v) in self.metas.borrow().iter_enumerated() { if let &Some(_) = v { @@ -163,11 +168,11 @@ impl CStore { return ordering } - pub fn add_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId, cnum: CrateNum) { + pub(super) fn add_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId, cnum: CrateNum) { self.extern_mod_crate_map.borrow_mut().insert(emod_id, cnum); } - pub fn do_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId) -> Option { + pub(super) fn do_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId) -> Option { self.extern_mod_crate_map.borrow().get(&emod_id).cloned() } } diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 57f92707ccfe3..53d1ff156274e 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -246,7 +246,7 @@ impl<'a, 'tcx: 'a> TyDecoder<'a, 'tcx> for DecodeContext<'a, 'tcx> { if cnum == LOCAL_CRATE { self.cdata().cnum } else { - self.cdata().cnum_map.borrow()[cnum] + self.cdata().cnum_map[cnum] } } } @@ -932,7 +932,7 @@ impl<'a, 'tcx> CrateMetadata { // Translate a DefId from the current compilation environment to a DefId // for an external crate. fn reverse_translate_def_id(&self, did: DefId) -> Option { - for (local, &global) in self.cnum_map.borrow().iter_enumerated() { + for (local, &global) in self.cnum_map.iter_enumerated() { if global == did.krate { return Some(DefId { krate: local, @@ -1007,7 +1007,7 @@ impl<'a, 'tcx> CrateMetadata { .enumerate() .flat_map(|(i, link)| { let cnum = CrateNum::new(i + 1); - link.map(|link| (self.cnum_map.borrow()[cnum], link)) + link.map(|link| (self.cnum_map[cnum], link)) }) .collect() } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 92f9a9e8ba974..d39556e9bb197 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -970,6 +970,9 @@ fn link_args(cmd: &mut Linker, out_filename: &Path, trans: &CrateTranslation) { + // Linker plugins should be specified early in the list of arguments + cmd.cross_lang_lto(); + // The default library location, we need this to find the runtime. // The location of crates will be determined as needed. let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index ea3f5b408604e..2a84ffe79b285 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -21,7 +21,8 @@ use back::symbol_export; use rustc::hir::def_id::{LOCAL_CRATE, CrateNum}; use rustc::middle::dependency_format::Linkage; use rustc::session::Session; -use rustc::session::config::{self, CrateType, OptLevel, DebugInfoLevel}; +use rustc::session::config::{self, CrateType, OptLevel, DebugInfoLevel, + CrossLangLto}; use rustc::ty::TyCtxt; use rustc_target::spec::{LinkerFlavor, LldFlavor}; use serialize::{json, Encoder}; @@ -127,6 +128,7 @@ pub trait Linker { fn subsystem(&mut self, subsystem: &str); fn group_start(&mut self); fn group_end(&mut self); + fn cross_lang_lto(&mut self); // Should have been finalize(self), but we don't support self-by-value on trait objects (yet?). fn finalize(&mut self) -> Command; } @@ -434,6 +436,42 @@ impl<'a> Linker for GccLinker<'a> { self.linker_arg("--end-group"); } } + + fn cross_lang_lto(&mut self) { + match self.sess.opts.debugging_opts.cross_lang_lto { + CrossLangLto::Disabled | + CrossLangLto::NoLink => { + // Nothing to do + } + CrossLangLto::LinkerPlugin(ref path) => { + self.linker_arg(&format!("-plugin={}", path.display())); + + let opt_level = match self.sess.opts.optimize { + config::OptLevel::No => "O0", + config::OptLevel::Less => "O1", + config::OptLevel::Default => "O2", + config::OptLevel::Aggressive => "O3", + config::OptLevel::Size => "Os", + config::OptLevel::SizeMin => "Oz", + }; + + self.linker_arg(&format!("-plugin-opt={}", opt_level)); + self.linker_arg(&format!("-plugin-opt=mcpu={}", self.sess.target_cpu())); + + match self.sess.opts.cg.lto { + config::Lto::Thin | + config::Lto::ThinLocal => { + self.linker_arg(&format!("-plugin-opt=thin")); + } + config::Lto::Fat | + config::Lto::Yes | + config::Lto::No => { + // default to regular LTO + } + } + } + } + } } pub struct MsvcLinker<'a> { @@ -666,6 +704,10 @@ impl<'a> Linker for MsvcLinker<'a> { // MSVC doesn't need group indicators fn group_start(&mut self) {} fn group_end(&mut self) {} + + fn cross_lang_lto(&mut self) { + // Do nothing + } } pub struct EmLinker<'a> { @@ -832,6 +874,10 @@ impl<'a> Linker for EmLinker<'a> { // Appears not necessary on Emscripten fn group_start(&mut self) {} fn group_end(&mut self) {} + + fn cross_lang_lto(&mut self) { + // Do nothing + } } fn exported_symbols(tcx: TyCtxt, crate_type: CrateType) -> Vec { @@ -984,4 +1030,8 @@ impl Linker for WasmLd { // Not needed for now with LLD fn group_start(&mut self) {} fn group_end(&mut self) {} + + fn cross_lang_lto(&mut self) { + // Do nothing for now + } } diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index b6fae3eaff23a..64876e82309f0 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -174,10 +174,7 @@ pub fn target_machine_factory(sess: &Session, find_features: bool) let triple = &sess.target.target.llvm_target; let triple = CString::new(triple.as_bytes()).unwrap(); - let cpu = match sess.opts.cg.target_cpu { - Some(ref s) => &**s, - None => &*sess.target.target.options.cpu - }; + let cpu = sess.target_cpu(); let cpu = CString::new(cpu.as_bytes()).unwrap(); let features = attributes::llvm_target_features(sess) .collect::>() @@ -294,7 +291,7 @@ impl ModuleConfig { self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode; let embed_bitcode = sess.target.target.options.embed_bitcode || sess.opts.debugging_opts.embed_bitcode || - sess.opts.debugging_opts.cross_lang_lto; + sess.opts.debugging_opts.cross_lang_lto.embed_bitcode(); if embed_bitcode { match sess.opts.optimize { config::OptLevel::No | @@ -1358,7 +1355,8 @@ fn execute_work_item(cgcx: &CodegenContext, // Don't run LTO passes when cross-lang LTO is enabled. The linker // will do that for us in this case. - let needs_lto = needs_lto && !cgcx.opts.debugging_opts.cross_lang_lto; + let needs_lto = needs_lto && + !cgcx.opts.debugging_opts.cross_lang_lto.embed_bitcode(); if needs_lto { Ok(WorkItemResult::NeedsLTO(mtrans)) diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 4a175248d74a3..0c717b9f20125 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -12,8 +12,8 @@ use super::{FnCtxt, Needs}; use super::method::MethodCallee; -use rustc::ty::{self, Ty, TypeFoldable, TypeVariants}; -use rustc::ty::TypeVariants::{TyStr, TyRef, TyAdt}; +use rustc::ty::{self, Ty, TypeFoldable}; +use rustc::ty::TypeVariants::{TyRef, TyAdt, TyStr, TyUint, TyNever, TyTuple, TyChar, TyArray}; use rustc::ty::adjustment::{Adjustment, Adjust, AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; use rustc::infer::type_variable::TypeVariableOrigin; use errors; @@ -246,76 +246,151 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Err(()) => { // error types are considered "builtin" if !lhs_ty.references_error() { - if let IsAssign::Yes = is_assign { - struct_span_err!(self.tcx.sess, expr.span, E0368, - "binary assignment operation `{}=` \ - cannot be applied to type `{}`", - op.node.as_str(), - lhs_ty) - .span_label(lhs_expr.span, - format!("cannot use `{}=` on type `{}`", - op.node.as_str(), lhs_ty)) - .emit(); - } else { - let mut err = struct_span_err!(self.tcx.sess, expr.span, E0369, - "binary operation `{}` cannot be applied to type `{}`", - op.node.as_str(), - lhs_ty); - - if let TypeVariants::TyRef(_, ref ty_mut) = lhs_ty.sty { - if { - !self.infcx.type_moves_by_default(self.param_env, - ty_mut.ty, - lhs_expr.span) && - self.lookup_op_method(ty_mut.ty, - &[rhs_ty], - Op::Binary(op, is_assign)) - .is_ok() - } { - err.note( - &format!( - "this is a reference to a type that `{}` can be applied \ - to; you need to dereference this variable once for this \ - operation to work", - op.node.as_str())); + let codemap = self.tcx.sess.codemap(); + match is_assign { + IsAssign::Yes => { + let mut err = struct_span_err!(self.tcx.sess, expr.span, E0368, + "binary assignment operation `{}=` \ + cannot be applied to type `{}`", + op.node.as_str(), + lhs_ty); + err.span_label(lhs_expr.span, + format!("cannot use `{}=` on type `{}`", + op.node.as_str(), lhs_ty)); + let mut suggested_deref = false; + if let TyRef(_, mut ty_mut) = lhs_ty.sty { + if { + !self.infcx.type_moves_by_default(self.param_env, + ty_mut.ty, + lhs_expr.span) && + self.lookup_op_method(ty_mut.ty, + &[rhs_ty], + Op::Binary(op, is_assign)) + .is_ok() + } { + if let Ok(lstring) = codemap.span_to_snippet(lhs_expr.span) { + while let TyRef(_, ty_mut_inner) = ty_mut.ty.sty{ + ty_mut = ty_mut_inner; + } + let msg = &format!( + "`{}=` can be used on '{}', you can \ + dereference `{2}`: `*{2}`", + op.node.as_str(), + ty_mut.ty, + lstring + ); + err.help(msg); + suggested_deref = true; + } + } } + let missing_trait = match op.node { + hir::BiAdd => Some("std::ops::AddAssign"), + hir::BiSub => Some("std::ops::SubAssign"), + hir::BiMul => Some("std::ops::MulAssign"), + hir::BiDiv => Some("std::ops::DivAssign"), + hir::BiRem => Some("std::ops::RemAssign"), + hir::BiBitAnd => Some("std::ops::BitAndAssign"), + hir::BiBitXor => Some("std::ops::BitXorAssign"), + hir::BiBitOr => Some("std::ops::BitOrAssign"), + hir::BiShl => Some("std::ops::ShlAssign"), + hir::BiShr => Some("std::ops::ShrAssign"), + _ => None + }; + if let Some(missing_trait) = missing_trait { + if op.node == hir::BiAdd && + self.check_str_addition(expr, lhs_expr, rhs_expr, lhs_ty, + rhs_ty, &mut err) { + // This has nothing here because it means we did string + // concatenation (e.g. "Hello " + "World!"). This means + // we don't want the note in the else clause to be emitted + } else if let ty::TyParam(_) = lhs_ty.sty { + // FIXME: point to span of param + err.note(&format!( + "`{}` might need a bound for `{}`", + lhs_ty, missing_trait + )); + } else if !suggested_deref { + err.note(&format!( + "an implementation of `{}` might \ + be missing for `{}`", + missing_trait, lhs_ty + )); + } + } + err.emit(); } - - let missing_trait = match op.node { - hir::BiAdd => Some("std::ops::Add"), - hir::BiSub => Some("std::ops::Sub"), - hir::BiMul => Some("std::ops::Mul"), - hir::BiDiv => Some("std::ops::Div"), - hir::BiRem => Some("std::ops::Rem"), - hir::BiBitAnd => Some("std::ops::BitAnd"), - hir::BiBitOr => Some("std::ops::BitOr"), - hir::BiShl => Some("std::ops::Shl"), - hir::BiShr => Some("std::ops::Shr"), - hir::BiEq | hir::BiNe => Some("std::cmp::PartialEq"), - hir::BiLt | hir::BiLe | hir::BiGt | hir::BiGe => - Some("std::cmp::PartialOrd"), - _ => None - }; - - if let Some(missing_trait) = missing_trait { - if missing_trait == "std::ops::Add" && - self.check_str_addition(expr, lhs_expr, rhs_expr, lhs_ty, - rhs_ty, &mut err) { - // This has nothing here because it means we did string - // concatenation (e.g. "Hello " + "World!"). This means - // we don't want the note in the else clause to be emitted - } else if let ty::TyParam(_) = lhs_ty.sty { - // FIXME: point to span of param - err.note( - &format!("`{}` might need a bound for `{}`", - lhs_ty, missing_trait)); - } else { - err.note( - &format!("an implementation of `{}` might be missing for `{}`", - missing_trait, lhs_ty)); + IsAssign::No => { + let mut err = struct_span_err!(self.tcx.sess, expr.span, E0369, + "binary operation `{}` cannot be applied to type `{}`", + op.node.as_str(), + lhs_ty); + let mut suggested_deref = false; + if let TyRef(_, mut ty_mut) = lhs_ty.sty { + if { + !self.infcx.type_moves_by_default(self.param_env, + ty_mut.ty, + lhs_expr.span) && + self.lookup_op_method(ty_mut.ty, + &[rhs_ty], + Op::Binary(op, is_assign)) + .is_ok() + } { + if let Ok(lstring) = codemap.span_to_snippet(lhs_expr.span) { + while let TyRef(_, ty_mut_inner) = ty_mut.ty.sty{ + ty_mut = ty_mut_inner; + } + let msg = &format!( + "`{}` can be used on '{}', you can \ + dereference `{2}`: `*{2}`", + op.node.as_str(), + ty_mut.ty, + lstring + ); + err.help(msg); + suggested_deref = true; + } + } } + let missing_trait = match op.node { + hir::BiAdd => Some("std::ops::Add"), + hir::BiSub => Some("std::ops::Sub"), + hir::BiMul => Some("std::ops::Mul"), + hir::BiDiv => Some("std::ops::Div"), + hir::BiRem => Some("std::ops::Rem"), + hir::BiBitAnd => Some("std::ops::BitAnd"), + hir::BiBitXor => Some("std::ops::BitXor"), + hir::BiBitOr => Some("std::ops::BitOr"), + hir::BiShl => Some("std::ops::Shl"), + hir::BiShr => Some("std::ops::Shr"), + hir::BiEq | hir::BiNe => Some("std::cmp::PartialEq"), + hir::BiLt | hir::BiLe | hir::BiGt | hir::BiGe => + Some("std::cmp::PartialOrd"), + _ => None + }; + if let Some(missing_trait) = missing_trait { + if op.node == hir::BiAdd && + self.check_str_addition(expr, lhs_expr, rhs_expr, lhs_ty, + rhs_ty, &mut err) { + // This has nothing here because it means we did string + // concatenation (e.g. "Hello " + "World!"). This means + // we don't want the note in the else clause to be emitted + } else if let ty::TyParam(_) = lhs_ty.sty { + // FIXME: point to span of param + err.note(&format!( + "`{}` might need a bound for `{}`", + lhs_ty, missing_trait + )); + } else if !suggested_deref { + err.note(&format!( + "an implementation of `{}` might \ + be missing for `{}`", + missing_trait, lhs_ty + )); + } + } + err.emit(); } - err.emit(); } } self.tcx.types.err @@ -393,9 +468,29 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Err(()) => { let actual = self.resolve_type_vars_if_possible(&operand_ty); if !actual.references_error() { - struct_span_err!(self.tcx.sess, ex.span, E0600, + let mut err = struct_span_err!(self.tcx.sess, ex.span, E0600, "cannot apply unary operator `{}` to type `{}`", - op.as_str(), actual).emit(); + op.as_str(), actual); + err.span_label(ex.span, format!("cannot apply unary \ + operator `{}`", op.as_str())); + match actual.sty { + TyUint(_) if op == hir::UnNeg => { + err.note(&format!("unsigned values cannot be negated")); + }, + TyStr | TyNever | TyChar | TyTuple(_) | TyArray(_,_) => {}, + TyRef(_, ref lty) if lty.ty.sty == TyStr => {}, + _ => { + let missing_trait = match op { + hir::UnNeg => "std::ops::Neg", + hir::UnNot => "std::ops::Not", + hir::UnDeref => "std::ops::UnDerf" + }; + err.note(&format!("an implementation of `{}` might \ + be missing for `{}`", + missing_trait, operand_ty)); + } + } + err.emit(); } self.tcx.types.err } diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 26644c769575c..f849db4ec6027 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -11,9 +11,9 @@ //! This module provides constants which are specific to the implementation //! of the `f32` floating point data type. //! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! //! *[See also the `f32` primitive type](../../std/primitive.f32.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] @@ -195,8 +195,10 @@ impl f32 { } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding - /// error. This produces a more accurate result with better performance than - /// a separate multiplication operation followed by an add. + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` can be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. /// /// ``` /// use std::f32; diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index a7e63f59b1c67..40c3f4d0ef726 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -11,9 +11,9 @@ //! This module provides constants which are specific to the implementation //! of the `f64` floating point data type. //! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! //! *[See also the `f64` primitive type](../../std/primitive.f64.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] @@ -173,8 +173,10 @@ impl f64 { } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding - /// error. This produces a more accurate result with better performance than - /// a separate multiplication operation followed by an add. + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` can be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. /// /// ``` /// let m = 10.0_f64; diff --git a/src/libstd/primitive_docs.rs b/src/libstd/primitive_docs.rs index 437d7d74cae0d..7074928eaf6da 100644 --- a/src/libstd/primitive_docs.rs +++ b/src/libstd/primitive_docs.rs @@ -370,6 +370,8 @@ mod prim_unit { } // /// Raw, unsafe pointers, `*const T`, and `*mut T`. /// +/// *[See also the `std::ptr` module](ptr/index.html).* +/// /// Working with raw pointers in Rust is uncommon, /// typically limited to a few patterns. /// @@ -444,8 +446,6 @@ mod prim_unit { } /// but C APIs hand out a lot of pointers generally, so are a common source /// of raw pointers in Rust. /// -/// *[See also the `std::ptr` module](ptr/index.html).* -/// /// [`null`]: ../std/ptr/fn.null.html /// [`null_mut`]: ../std/ptr/fn.null_mut.html /// [`is_null`]: ../std/primitive.pointer.html#method.is_null @@ -563,6 +563,8 @@ mod prim_array { } // /// A dynamically-sized view into a contiguous sequence, `[T]`. /// +/// *[See also the `std::slice` module](slice/index.html).* +/// /// Slices are a view into a block of memory represented as a pointer and a /// length. /// @@ -585,8 +587,6 @@ mod prim_array { } /// assert_eq!(x, &[1, 7, 3]); /// ``` /// -/// *[See also the `std::slice` module](slice/index.html).* -/// #[stable(feature = "rust1", since = "1.0.0")] mod prim_slice { } @@ -594,15 +594,13 @@ mod prim_slice { } // /// String slices. /// +/// *[See also the `std::str` module](str/index.html).* +/// /// The `str` type, also called a 'string slice', is the most primitive string /// type. It is usually seen in its borrowed form, `&str`. It is also the type /// of string literals, `&'static str`. /// -/// Strings slices are always valid UTF-8. -/// -/// This documentation describes a number of methods and trait implementations -/// on the `str` type. For technical reasons, there is additional, separate -/// documentation in the [`std::str`](str/index.html) module as well. +/// String slices are always valid UTF-8. /// /// # Examples /// @@ -862,11 +860,11 @@ mod prim_u128 { } // /// The pointer-sized signed integer type. /// +/// *[See also the `std::isize` module](isize/index.html).* +/// /// The size of this primitive is how many bytes it takes to reference any /// location in memory. For example, on a 32 bit target, this is 4 bytes /// and on a 64 bit target, this is 8 bytes. -/// -/// *[See also the `std::isize` module](isize/index.html).* #[stable(feature = "rust1", since = "1.0.0")] mod prim_isize { } @@ -874,11 +872,11 @@ mod prim_isize { } // /// The pointer-sized unsigned integer type. /// +/// *[See also the `std::usize` module](usize/index.html).* +/// /// The size of this primitive is how many bytes it takes to reference any /// location in memory. For example, on a 32 bit target, this is 4 bytes /// and on a 64 bit target, this is 8 bytes. -/// -/// *[See also the `std::usize` module](usize/index.html).* #[stable(feature = "rust1", since = "1.0.0")] mod prim_usize { } diff --git a/src/libsyntax_pos/span_encoding.rs b/src/libsyntax_pos/span_encoding.rs index b55fe4bcb2672..601a0273ae911 100644 --- a/src/libsyntax_pos/span_encoding.rs +++ b/src/libsyntax_pos/span_encoding.rs @@ -31,11 +31,13 @@ pub struct Span(u32); impl Copy for Span {} impl Clone for Span { + #[inline] fn clone(&self) -> Span { *self } } impl PartialEq for Span { + #[inline] fn eq(&self, other: &Span) -> bool { let a = self.0; let b = other.0; @@ -44,6 +46,7 @@ impl PartialEq for Span { } impl Eq for Span {} impl Hash for Span { + #[inline] fn hash(&self, state: &mut H) { let a = self.0; a.hash(state) diff --git a/src/llvm b/src/llvm index b6c1a03fb498f..fd7dd99edf371 160000 --- a/src/llvm +++ b/src/llvm @@ -1 +1 @@ -Subproject commit b6c1a03fb498f6c03d1cbfd4404223a046f8c3b2 +Subproject commit fd7dd99edf371ac502ae4e70288c027f6692ace0 diff --git a/src/test/compile-fail-fulldeps/proc-macro/auxiliary/attributes-included.rs b/src/test/compile-fail-fulldeps/proc-macro/auxiliary/attributes-included.rs index bbfec5815ba5c..6b34ccc6543ea 100644 --- a/src/test/compile-fail-fulldeps/proc-macro/auxiliary/attributes-included.rs +++ b/src/test/compile-fail-fulldeps/proc-macro/auxiliary/attributes-included.rs @@ -86,7 +86,7 @@ fn assert_doc(slice: &mut &[TokenTree]) { } match &tokens[0] { - TokenTree::Term(tt) => assert_eq!("doc", tt.as_str()), + TokenTree::Term(tt) => assert_eq!("doc", &*tt.to_string()), _ => panic!("expected `doc`"), } match &tokens[1] { @@ -118,11 +118,11 @@ fn assert_invoc(slice: &mut &[TokenTree]) { fn assert_foo(slice: &mut &[TokenTree]) { match &slice[0] { - TokenTree::Term(tt) => assert_eq!(tt.as_str(), "fn"), + TokenTree::Term(tt) => assert_eq!(&*tt.to_string(), "fn"), _ => panic!("expected fn"), } match &slice[1] { - TokenTree::Term(tt) => assert_eq!(tt.as_str(), "foo"), + TokenTree::Term(tt) => assert_eq!(&*tt.to_string(), "foo"), _ => panic!("expected foo"), } match &slice[2] { diff --git a/src/test/run-make/cross-lang-lto/Makefile b/src/test/run-make/cross-lang-lto/Makefile index 98b509cd81f54..925f686fe1161 100644 --- a/src/test/run-make/cross-lang-lto/Makefile +++ b/src/test/run-make/cross-lang-lto/Makefile @@ -18,9 +18,9 @@ endif OBJDUMP=llvm-objdump SECTION_HEADERS=$(OBJDUMP) -section-headers -BUILD_LIB=$(RUSTC) lib.rs -Copt-level=2 -Z cross-lang-lto -Ccodegen-units=1 +BUILD_LIB=$(RUSTC) lib.rs -Copt-level=2 -Z cross-lang-lto=no-link -Ccodegen-units=1 -BUILD_EXE=$(RUSTC) main.rs -Copt-level=2 -Z cross-lang-lto -Ccodegen-units=1 --emit=obj +BUILD_EXE=$(RUSTC) main.rs -Copt-level=2 -Z cross-lang-lto=no-link -Ccodegen-units=1 --emit=obj all: staticlib staticlib-fat-lto staticlib-thin-lto rlib exe cdylib rdylib diff --git a/src/test/run-pass-fulldeps/auxiliary/cond_plugin.rs b/src/test/run-pass-fulldeps/auxiliary/cond_plugin.rs index 9e1ae59c01bc0..de26f8296e36b 100644 --- a/src/test/run-pass-fulldeps/auxiliary/cond_plugin.rs +++ b/src/test/run-pass-fulldeps/auxiliary/cond_plugin.rs @@ -33,7 +33,7 @@ pub fn cond(input: TokenStream) -> TokenStream { panic!("Invalid macro usage in cond: {}", cond); } let is_else = match test { - TokenTree::Term(word) => word.as_str() == "else", + TokenTree::Term(word) => &*word.to_string() == "else", _ => false, }; conds.push(if is_else || input.peek().is_none() { diff --git a/src/test/ui/binary-op-on-double-ref.stderr b/src/test/ui/binary-op-on-double-ref.stderr index 07aa3bfe40dea..c89defa3dd196 100644 --- a/src/test/ui/binary-op-on-double-ref.stderr +++ b/src/test/ui/binary-op-on-double-ref.stderr @@ -4,8 +4,7 @@ error[E0369]: binary operation `%` cannot be applied to type `&&{integer}` LL | x % 2 == 0 | ^^^^^ | - = note: this is a reference to a type that `%` can be applied to; you need to dereference this variable once for this operation to work - = note: an implementation of `std::ops::Rem` might be missing for `&&{integer}` + = help: `%` can be used on '{integer}', you can dereference `x`: `*x` error: aborting due to previous error diff --git a/src/test/ui/codemap_tests/issue-28308.stderr b/src/test/ui/codemap_tests/issue-28308.stderr index 2c8a33d95c03a..15c159a3b153c 100644 --- a/src/test/ui/codemap_tests/issue-28308.stderr +++ b/src/test/ui/codemap_tests/issue-28308.stderr @@ -2,7 +2,7 @@ error[E0600]: cannot apply unary operator `!` to type `&'static str` --> $DIR/issue-28308.rs:12:5 | LL | assert!("foo"); - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ cannot apply unary operator `!` error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0067.stderr b/src/test/ui/error-codes/E0067.stderr index 76f06c7c46379..43e1ca4096cf6 100644 --- a/src/test/ui/error-codes/E0067.stderr +++ b/src/test/ui/error-codes/E0067.stderr @@ -5,6 +5,8 @@ LL | LinkedList::new() += 1; //~ ERROR E0368 | -----------------^^^^^ | | | cannot use `+=` on type `std::collections::LinkedList<_>` + | + = note: an implementation of `std::ops::AddAssign` might be missing for `std::collections::LinkedList<_>` error[E0067]: invalid left-hand side expression --> $DIR/E0067.rs:14:5 diff --git a/src/test/ui/error-codes/E0600.stderr b/src/test/ui/error-codes/E0600.stderr index 500feb39f5e74..c29ec4fe6ae76 100644 --- a/src/test/ui/error-codes/E0600.stderr +++ b/src/test/ui/error-codes/E0600.stderr @@ -2,7 +2,7 @@ error[E0600]: cannot apply unary operator `!` to type `&'static str` --> $DIR/E0600.rs:12:5 | LL | !"a"; //~ ERROR E0600 - | ^^^^ + | ^^^^ cannot apply unary operator `!` error: aborting due to previous error diff --git a/src/test/ui/error-festival.stderr b/src/test/ui/error-festival.stderr index 345691352b407..69f11b4b7c08c 100644 --- a/src/test/ui/error-festival.stderr +++ b/src/test/ui/error-festival.stderr @@ -17,6 +17,8 @@ LL | x += 2; | -^^^^^ | | | cannot use `+=` on type `&str` + | + = note: an implementation of `std::ops::AddAssign` might be missing for `&str` error[E0599]: no method named `z` found for type `&str` in the current scope --> $DIR/error-festival.rs:26:7 @@ -28,7 +30,9 @@ error[E0600]: cannot apply unary operator `!` to type `Question` --> $DIR/error-festival.rs:29:5 | LL | !Question::Yes; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ cannot apply unary operator `!` + | + = note: an implementation of `std::ops::Not` might be missing for `Question` error[E0604]: only `u8` can be cast as `char`, not `u32` --> $DIR/error-festival.rs:35:5 diff --git a/src/test/ui/feature-gate-negate-unsigned.stderr b/src/test/ui/feature-gate-negate-unsigned.stderr index 1025b56f55bc3..85e9b56e4af9d 100644 --- a/src/test/ui/feature-gate-negate-unsigned.stderr +++ b/src/test/ui/feature-gate-negate-unsigned.stderr @@ -2,13 +2,17 @@ error[E0600]: cannot apply unary operator `-` to type `usize` --> $DIR/feature-gate-negate-unsigned.rs:20:23 | LL | let _max: usize = -1; - | ^^ + | ^^ cannot apply unary operator `-` + | + = note: unsigned values cannot be negated error[E0600]: cannot apply unary operator `-` to type `u8` --> $DIR/feature-gate-negate-unsigned.rs:24:14 | LL | let _y = -x; - | ^^ + | ^^ cannot apply unary operator `-` + | + = note: unsigned values cannot be negated error: aborting due to 2 previous errors diff --git a/src/test/ui/issue-5239-1.stderr b/src/test/ui/issue-5239-1.stderr index 2f9204e72d3fb..7ae01fb7d6012 100644 --- a/src/test/ui/issue-5239-1.stderr +++ b/src/test/ui/issue-5239-1.stderr @@ -5,6 +5,8 @@ LL | let x = |ref x: isize| { x += 1; }; | -^^^^^ | | | cannot use `+=` on type `&isize` + | + = help: `+=` can be used on 'isize', you can dereference `x`: `*x` error: aborting due to previous error diff --git a/src/test/ui/reachable/expr_unary.stderr b/src/test/ui/reachable/expr_unary.stderr index 165eccd42396b..b889c884fcbb2 100644 --- a/src/test/ui/reachable/expr_unary.stderr +++ b/src/test/ui/reachable/expr_unary.stderr @@ -2,7 +2,7 @@ error[E0600]: cannot apply unary operator `!` to type `!` --> $DIR/expr_unary.rs:17:16 | LL | let x: ! = ! { return; }; //~ ERROR unreachable - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ cannot apply unary operator `!` error: unreachable expression --> $DIR/expr_unary.rs:17:16 diff --git a/src/test/ui/rfc1598-generic-associated-types/collections.rs b/src/test/ui/rfc1598-generic-associated-types/collections.rs new file mode 100644 index 0000000000000..e71166ed65bba --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/collections.rs @@ -0,0 +1,97 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] +#![feature(associated_type_defaults)] + +//FIXME(#44265): "lifetime parameters are not allowed on this type" errors will be addressed in a +//follow-up PR + +// A Collection trait and collection families. Based on +// http://smallcultfollowing.com/babysteps/blog/2016/11/03/ +// associated-type-constructors-part-2-family-traits/ + +trait Collection { + type Iter<'iter>: Iterator; + type Family: CollectionFamily; + // Test associated type defaults with parameters + type Sibling: Collection = + <>::Family as CollectionFamily>::Member; + //~^ ERROR type parameters are not allowed on this type [E0109] + + fn empty() -> Self; + + fn add(&mut self, value: T); + + fn iterate<'iter>(&'iter self) -> Self::Iter<'iter>; + //~^ ERROR lifetime parameters are not allowed on this type [E0110] +} + +trait CollectionFamily { + type Member: Collection; +} + +struct VecFamily; + +impl CollectionFamily for VecFamily { + type Member = Vec; +} + +impl Collection for Vec { + type Iter<'iter> = std::slice::Iter<'iter, T>; + type Family = VecFamily; + + fn empty() -> Self { + Vec::new() + } + + fn add(&mut self, value: T) { + self.push(value) + } + + fn iterate<'iter>(&'iter self) -> Self::Iter<'iter> { + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + self.iter() + } +} + +fn floatify(ints: &C) -> <>::Family as CollectionFamily>::Member +//~^ ERROR type parameters are not allowed on this type [E0109] +where + C: Collection, +{ + let mut res = C::Family::Member::::empty(); + for &v in ints.iterate() { + res.add(v as f32); + } + res +} + +fn floatify_sibling(ints: &C) -> >::Sibling +//~^ ERROR type parameters are not allowed on this type [E0109] +where + C: Collection, +{ + let mut res = C::Family::Member::::empty(); + for &v in ints.iterate() { + res.add(v as f32); + } + res +} + +fn use_floatify() { + let a = vec![1i32, 2, 3]; + let b = floatify(a); + println!("{}", b.iterate().next()); + let c = floatify_sibling(a); + println!("{}", c.iterate().next()); +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/collections.stderr b/src/test/ui/rfc1598-generic-associated-types/collections.stderr new file mode 100644 index 0000000000000..ed96570583f4f --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/collections.stderr @@ -0,0 +1,34 @@ +error[E0109]: type parameters are not allowed on this type + --> $DIR/collections.rs:65:90 + | +LL | fn floatify(ints: &C) -> <>::Family as CollectionFamily>::Member + | ^^^ type parameter not allowed + +error[E0109]: type parameters are not allowed on this type + --> $DIR/collections.rs:77:69 + | +LL | fn floatify_sibling(ints: &C) -> >::Sibling + | ^^^ type parameter not allowed + +error[E0109]: type parameters are not allowed on this type + --> $DIR/collections.rs:26:71 + | +LL | <>::Family as CollectionFamily>::Member; + | ^ type parameter not allowed + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/collections.rs:33:50 + | +LL | fn iterate<'iter>(&'iter self) -> Self::Iter<'iter>; + | ^^^^^ lifetime parameter not allowed on this type + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/collections.rs:59:50 + | +LL | fn iterate<'iter>(&'iter self) -> Self::Iter<'iter> { + | ^^^^^ lifetime parameter not allowed on this type + +error: aborting due to 5 previous errors + +Some errors occurred: E0109, E0110. +For more information about an error, try `rustc --explain E0109`. diff --git a/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.rs b/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.rs index 0d9b487876e21..0429410031526 100644 --- a/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.rs +++ b/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.rs @@ -10,6 +10,8 @@ #![feature(generic_associated_types)] +use std::ops::Deref; + //FIXME(#44265): "lifetime parameters are not allowed on this type" errors will be addressed in a //follow-up PR @@ -18,11 +20,18 @@ trait Foo { } trait Baz { - type Quux<'a>; + type Quux<'a>: Foo; + + // This weird type tests that we can use universal function call syntax to access the Item on + type Baa<'a>: Deref as Foo>::Bar<'a, 'static>>; + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + //~| ERROR lifetime parameters are not allowed on this type [E0110] } impl Baz for T where T: Foo { - type Quux<'a> = ::Bar<'a, 'static>; + type Quux<'a> = T; + + type Baa<'a> = &'a ::Bar<'a, 'static>; //~^ ERROR lifetime parameters are not allowed on this type [E0110] } diff --git a/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.stderr b/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.stderr index 054530e24bd18..764a0db2478a8 100644 --- a/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.stderr +++ b/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.stderr @@ -1,9 +1,21 @@ error[E0110]: lifetime parameters are not allowed on this type - --> $DIR/construct_with_other_type.rs:25:37 + --> $DIR/construct_with_other_type.rs:26:46 | -LL | type Quux<'a> = ::Bar<'a, 'static>; - | ^^ lifetime parameter not allowed on this type +LL | type Baa<'a>: Deref as Foo>::Bar<'a, 'static>>; + | ^^ lifetime parameter not allowed on this type -error: aborting due to previous error +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/construct_with_other_type.rs:26:63 + | +LL | type Baa<'a>: Deref as Foo>::Bar<'a, 'static>>; + | ^^ lifetime parameter not allowed on this type + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/construct_with_other_type.rs:34:40 + | +LL | type Baa<'a> = &'a ::Bar<'a, 'static>; + | ^^ lifetime parameter not allowed on this type + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0110`. diff --git a/src/test/ui/rfc1598-generic-associated-types/iterable.rs b/src/test/ui/rfc1598-generic-associated-types/iterable.rs index 1287ddaf7f7fe..38967dbbe4530 100644 --- a/src/test/ui/rfc1598-generic-associated-types/iterable.rs +++ b/src/test/ui/rfc1598-generic-associated-types/iterable.rs @@ -20,13 +20,40 @@ trait Iterable { type Iter<'a>: Iterator>; //~^ ERROR lifetime parameters are not allowed on this type [E0110] - // This weird type tests that we can use universal function call syntax to access the Item on - // Self::Iter which we have declared to be an Iterator - type Iter2<'a>: Deref as Iterator>::Item>; + fn iter<'a>(&'a self) -> Self::Iter<'a>; //~^ ERROR lifetime parameters are not allowed on this type [E0110] +} - fn iter<'a>(&'a self) -> Self::Iter<'a>; +// Impl for struct type +impl Iterable for Vec { + type Item<'a> = &'a T; + type Iter<'a> = std::slice::Iter<'a, T>; + + fn iter<'a>(&'a self) -> Self::Iter<'a> { + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + self.iter() + } +} + +// Impl for a primitive type +impl Iterable for [T] { + type Item<'a> = &'a T; + type Iter<'a> = std::slice::Iter<'a, T>; + + fn iter<'a>(&'a self) -> Self::Iter<'a> { + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + self.iter() + } +} + +fn make_iter<'a, I: Iterable>(it: &'a I) -> I::Iter<'a> { + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + it.iter() +} + +fn get_first<'a, I: Iterable>(it: &'a I) -> Option> { //~^ ERROR lifetime parameters are not allowed on this type [E0110] + it.iter().next() } fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/iterable.stderr b/src/test/ui/rfc1598-generic-associated-types/iterable.stderr index d33eebb42d607..0e251300e451f 100644 --- a/src/test/ui/rfc1598-generic-associated-types/iterable.stderr +++ b/src/test/ui/rfc1598-generic-associated-types/iterable.stderr @@ -5,17 +5,35 @@ LL | type Iter<'a>: Iterator>; | ^^ lifetime parameter not allowed on this type error[E0110]: lifetime parameters are not allowed on this type - --> $DIR/iterable.rs:25:48 + --> $DIR/iterable.rs:49:53 | -LL | type Iter2<'a>: Deref as Iterator>::Item>; - | ^^ lifetime parameter not allowed on this type +LL | fn make_iter<'a, I: Iterable>(it: &'a I) -> I::Iter<'a> { + | ^^ lifetime parameter not allowed on this type error[E0110]: lifetime parameters are not allowed on this type - --> $DIR/iterable.rs:28:41 + --> $DIR/iterable.rs:54:60 + | +LL | fn get_first<'a, I: Iterable>(it: &'a I) -> Option> { + | ^^ lifetime parameter not allowed on this type + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/iterable.rs:23:41 | LL | fn iter<'a>(&'a self) -> Self::Iter<'a>; | ^^ lifetime parameter not allowed on this type -error: aborting due to 3 previous errors +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/iterable.rs:32:41 + | +LL | fn iter<'a>(&'a self) -> Self::Iter<'a> { + | ^^ lifetime parameter not allowed on this type + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/iterable.rs:43:41 + | +LL | fn iter<'a>(&'a self) -> Self::Iter<'a> { + | ^^ lifetime parameter not allowed on this type + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0110`. diff --git a/src/test/ui/rfc1598-generic-associated-types/parameter_number_and_kind.rs b/src/test/ui/rfc1598-generic-associated-types/parameter_number_and_kind.rs new file mode 100644 index 0000000000000..51527d4117c2c --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/parameter_number_and_kind.rs @@ -0,0 +1,56 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] +#![feature(associated_type_defaults)] + +//FIXME(#44265): "lifetime parameters are not allowed on this type" errors will be addressed in a +//follow-up PR + +//FIXME(#44265): Update expected errors once E110 is resolved, now does not get past `trait Foo` + +trait Foo { + type A<'a>; + type B<'a, 'b>; + type C; + type D; + type E<'a, T>; + // Test parameters in default values + type FOk = Self::E<'static, T>; + //~^ ERROR type parameters are not allowed on this type [E0109] + //~| ERROR lifetime parameters are not allowed on this type [E0110] + type FErr1 = Self::E<'static, 'static>; // Error + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + type FErr2 = Self::E<'static, T, u32>; // Error + //~^ ERROR type parameters are not allowed on this type [E0109] + //~| ERROR lifetime parameters are not allowed on this type [E0110] +} + +struct Fooy; + +impl Foo for Fooy { + type A = u32; // Error: parameter expected + type B<'a, T> = Vec; // Error: lifetime param expected + type C<'a> = u32; // Error: no param expected + type D<'a> = u32; // Error: type param expected + type E = u32; // Error: lifetime expected as the first param +} + +struct Fooer; + +impl Foo for Fooer { + type A = u32; // Error: lifetime parameter expected + type B<'a> = u32; // Error: another lifetime param expected + type C = T; // Error: no param expected + type D<'b, T> = u32; // Error: unexpected lifetime param + type E<'a, 'b> = u32; // Error: type expected as the second param +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/parameter_number_and_kind.stderr b/src/test/ui/rfc1598-generic-associated-types/parameter_number_and_kind.stderr new file mode 100644 index 0000000000000..df83fdaad5bfa --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/parameter_number_and_kind.stderr @@ -0,0 +1,34 @@ +error[E0109]: type parameters are not allowed on this type + --> $DIR/parameter_number_and_kind.rs:26:36 + | +LL | type FOk = Self::E<'static, T>; + | ^ type parameter not allowed + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/parameter_number_and_kind.rs:26:27 + | +LL | type FOk = Self::E<'static, T>; + | ^^^^^^^ lifetime parameter not allowed on this type + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/parameter_number_and_kind.rs:29:26 + | +LL | type FErr1 = Self::E<'static, 'static>; // Error + | ^^^^^^^ lifetime parameter not allowed on this type + +error[E0109]: type parameters are not allowed on this type + --> $DIR/parameter_number_and_kind.rs:31:38 + | +LL | type FErr2 = Self::E<'static, T, u32>; // Error + | ^ type parameter not allowed + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/parameter_number_and_kind.rs:31:29 + | +LL | type FErr2 = Self::E<'static, T, u32>; // Error + | ^^^^^^^ lifetime parameter not allowed on this type + +error: aborting due to 5 previous errors + +Some errors occurred: E0109, E0110. +For more information about an error, try `rustc --explain E0109`. diff --git a/src/test/ui/rfc1598-generic-associated-types/shadowing.rs b/src/test/ui/rfc1598-generic-associated-types/shadowing.rs new file mode 100644 index 0000000000000..6cdcaf2568394 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/shadowing.rs @@ -0,0 +1,42 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +//FIXME(#44265): The lifetime shadowing and type parameter shadowing +// should cause an error. Now it compiles (errorneously) and this will be addressed +// by a future PR. Then remove the following: +// compile-pass + +trait Shadow<'a> { + type Bar<'a>; // Error: shadowed lifetime +} + +trait NoShadow<'a> { + type Bar<'b>; // OK +} + +impl<'a> NoShadow<'a> for &'a u32 { + type Bar<'a> = i32; // Error: shadowed lifetime +} + +trait ShadowT { + type Bar; // Error: shadowed type parameter +} + +trait NoShadowT { + type Bar; // OK +} + +impl NoShadowT for Option { + type Bar = i32; // Error: shadowed type parameter +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/shadowing.stdout b/src/test/ui/rfc1598-generic-associated-types/shadowing.stdout new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs b/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs index f9e270ee92e22..522ddb5dc135e 100644 --- a/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs +++ b/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs @@ -35,4 +35,48 @@ struct Foo { fn foo(iter: T) where T: StreamingIterator, for<'a> T::Item<'a>: Display { /* ... */ } //~^ ERROR lifetime parameters are not allowed on this type [E0110] +// Full example of enumerate iterator + +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +struct StreamEnumerate { + iter: I, + count: usize, +} + +impl StreamingIterator for StreamEnumerate { + type Item<'a> = (usize, I::Item<'a>); + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + fn next<'a>(&'a self) -> Option> { + //~^ ERROR lifetime parameters are not allowed on this type [E0110] + match self.iter.next() { + None => None, + Some(val) => { + let r = Some((self.count, val)); + self.count += 1; + r + } + } + } +} + +impl StreamEnumerate { + pub fn new(iter: I) -> Self { + StreamEnumerate { + count: 0, + iter: iter, + } + } +} + +fn test_stream_enumerate() { + let v = vec!["a", "b", "c"]; + let se = StreamEnumerate::new(v.iter()); + let a: &str = se.next().unwrap().1; + for (i, s) in se { + println!("{} {}", i, s); + } + println!("{}", a); +} + + fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.stderr b/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.stderr index 9ab80151a7ed3..607a4b8d57996 100644 --- a/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.stderr +++ b/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.stderr @@ -16,6 +16,18 @@ error[E0110]: lifetime parameters are not allowed on this type LL | fn next<'a>(&'a self) -> Option>; | ^^ lifetime parameter not allowed on this type -error: aborting due to 3 previous errors +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/streaming_iterator.rs:47:37 + | +LL | type Item<'a> = (usize, I::Item<'a>); + | ^^ lifetime parameter not allowed on this type + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/streaming_iterator.rs:49:48 + | +LL | fn next<'a>(&'a self) -> Option> { + | ^^ lifetime parameter not allowed on this type + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0110`. diff --git a/src/test/ui/type-check/missing_trait_impl.rs b/src/test/ui/type-check/missing_trait_impl.rs index adf6b85b6429c..b3e79ce2447fb 100644 --- a/src/test/ui/type-check/missing_trait_impl.rs +++ b/src/test/ui/type-check/missing_trait_impl.rs @@ -14,3 +14,7 @@ fn main() { fn foo(x: T, y: T) { let z = x + y; //~ ERROR binary operation `+` cannot be applied to type `T` } + +fn bar(x: T) { + x += x; //~ ERROR binary assignment operation `+=` cannot be applied to type `T` +} diff --git a/src/test/ui/type-check/missing_trait_impl.stderr b/src/test/ui/type-check/missing_trait_impl.stderr index 777f16b12ce68..4b01a626814e5 100644 --- a/src/test/ui/type-check/missing_trait_impl.stderr +++ b/src/test/ui/type-check/missing_trait_impl.stderr @@ -6,6 +6,17 @@ LL | let z = x + y; //~ ERROR binary operation `+` cannot be applied to type | = note: `T` might need a bound for `std::ops::Add` -error: aborting due to previous error +error[E0368]: binary assignment operation `+=` cannot be applied to type `T` + --> $DIR/missing_trait_impl.rs:19:5 + | +LL | x += x; //~ ERROR binary assignment operation `+=` cannot be applied to type `T` + | -^^^^^ + | | + | cannot use `+=` on type `T` + | + = note: `T` might need a bound for `std::ops::AddAssign` + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0369`. +Some errors occurred: E0368, E0369. +For more information about an error, try `rustc --explain E0368`. diff --git a/src/test/ui/update-references.sh b/src/test/ui/update-references.sh index 47a85352b0044..00b4b5c5caa78 100755 --- a/src/test/ui/update-references.sh +++ b/src/test/ui/update-references.sh @@ -26,6 +26,7 @@ if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" fi +MYDIR=$(dirname $0) BUILD_DIR="$1" shift @@ -33,13 +34,13 @@ shift shopt -s nullglob while [[ "$1" != "" ]]; do - MYDIR=$(dirname $1) for EXT in "stderr" "stdout" "fixed"; do for OUT_NAME in $BUILD_DIR/${1%.rs}.*$EXT; do + OUT_DIR=`dirname "$1"` OUT_BASE=`basename "$OUT_NAME"` - if ! (diff $OUT_NAME $MYDIR/$OUT_BASE >& /dev/null); then - echo updating $MYDIR/$OUT_BASE - cp $OUT_NAME $MYDIR + if ! (diff $OUT_NAME $MYDIR/$OUT_DIR/$OUT_BASE >& /dev/null); then + echo updating $MYDIR/$OUT_DIR/$OUT_BASE + cp $OUT_NAME $MYDIR/$OUT_DIR fi done done