diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index 55d104b182698..ca35a896e08c8 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -61,6 +61,11 @@ fn main() { args.remove(n); } + if let Some(s) = env::var_os("RUSTC_ERROR_FORMAT") { + args.push("--error-format".into()); + args.push(s); + } + // Detect whether or not we're a build script depending on whether --target // is passed (a bit janky...) let target = args.windows(2) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 603a97ddfd412..5966bb65df9c8 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -294,7 +294,7 @@ def default_build_triple(): raise ValueError('unknown byteorder: {}'.format(sys.byteorder)) # only the n64 ABI is supported, indicate it ostype += 'abi64' - elif cputype == 'sparcv9' or cputype == 'sparc64': + elif cputype == 'sparc' or cputype == 'sparcv9' or cputype == 'sparc64': pass else: err = "unknown cpu type: {}".format(cputype) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 66a1c97246200..b5946b44e05ef 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -444,10 +444,11 @@ impl<'a> Builder<'a> { fn run(self, builder: &Builder) -> Interned { let compiler = self.compiler; - let lib = if compiler.stage >= 1 && builder.build.config.libdir.is_some() { - builder.build.config.libdir.clone().unwrap() + let config = &builder.build.config; + let lib = if compiler.stage >= 1 && config.libdir_relative().is_some() { + builder.build.config.libdir_relative().unwrap() } else { - PathBuf::from("lib") + Path::new("lib") }; let sysroot = builder.sysroot(self.compiler).join(lib) .join("rustlib").join(self.target).join("lib"); @@ -598,6 +599,9 @@ impl<'a> Builder<'a> { if let Some(target_linker) = self.build.linker(target) { cargo.env("RUSTC_TARGET_LINKER", target_linker); } + if let Some(ref error_format) = self.config.rustc_error_format { + cargo.env("RUSTC_ERROR_FORMAT", error_format); + } if cmd != "build" && cmd != "check" { cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(self.compiler(2, self.build.build))); } diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 2dcc0e0e7cd9f..c85b04ddc0245 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -516,8 +516,7 @@ fn rustc_cargo_env(build: &Build, cargo: &mut Command) { .env("CFG_VERSION", build.rust_version()) .env("CFG_PREFIX", build.config.prefix.clone().unwrap_or_default()); - let libdir_relative = - build.config.libdir.clone().unwrap_or(PathBuf::from("lib")); + let libdir_relative = build.config.libdir_relative().unwrap_or(Path::new("lib")); cargo.env("CFG_LIBDIR_RELATIVE", libdir_relative); // If we're not building a compiler with debugging information then remove diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 812ca6d64fb6a..6bc20181a0330 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -17,7 +17,7 @@ use std::collections::{HashMap, HashSet}; use std::env; use std::fs::File; use std::io::prelude::*; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process; use std::cmp; @@ -57,6 +57,7 @@ pub struct Config { pub profiler: bool, pub ignore_git: bool, pub exclude: Vec, + pub rustc_error_format: Option, pub run_host_only: bool, @@ -330,6 +331,7 @@ impl Config { config.test_miri = false; config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")]; + config.rustc_error_format = flags.rustc_error_format; config.on_fail = flags.on_fail; config.stage = flags.stage; config.src = flags.src; @@ -564,6 +566,17 @@ impl Config { config } + /// Try to find the relative path of `libdir`. + pub fn libdir_relative(&self) -> Option<&Path> { + let libdir = self.libdir.as_ref()?; + if libdir.is_relative() { + Some(libdir) + } else { + // Try to make it relative to the prefix. + libdir.strip_prefix(self.prefix.as_ref()?).ok() + } + } + pub fn verbose(&self) -> bool { self.verbose > 0 } diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index eb5c3b8ce147f..8ca5910a11c0d 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -43,6 +43,7 @@ pub struct Flags { pub cmd: Subcommand, pub incremental: bool, pub exclude: Vec, + pub rustc_error_format: Option, } pub enum Subcommand { @@ -118,6 +119,7 @@ To learn more about a subcommand, run `./x.py -h`"); opts.optopt("", "src", "path to the root of the rust checkout", "DIR"); opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS"); opts.optflag("h", "help", "print this help message"); + opts.optflag("", "error-format", "rustc error format"); // fn usage() let usage = |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! { @@ -370,6 +372,7 @@ Arguments: verbose: matches.opt_count("verbose"), stage, on_fail: matches.opt_str("on-fail"), + rustc_error_format: matches.opt_str("error-format"), keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()), build: matches.opt_str("build").map(|s| INTERNER.intern_string(s)), host: split(matches.opt_strs("host")) diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 7836ad214edc4..15dd7fabfa58b 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -480,9 +480,11 @@ impl Step for Openssl { "mips64el-unknown-linux-gnuabi64" => "linux64-mips64", "mipsel-unknown-linux-gnu" => "linux-mips32", "powerpc-unknown-linux-gnu" => "linux-ppc", + "powerpc-unknown-netbsd" => "BSD-generic32", "powerpc64-unknown-linux-gnu" => "linux-ppc64", "powerpc64le-unknown-linux-gnu" => "linux-ppc64le", "s390x-unknown-linux-gnu" => "linux64-s390x", + "sparc-unknown-linux-gnu" => "linux-sparcv9", "sparc64-unknown-linux-gnu" => "linux64-sparcv9", "sparc64-unknown-netbsd" => "BSD-sparc64", "x86_64-apple-darwin" => "darwin64-x86_64-cc", @@ -490,6 +492,7 @@ impl Step for Openssl { "x86_64-unknown-freebsd" => "BSD-x86_64", "x86_64-unknown-dragonfly" => "BSD-x86_64", "x86_64-unknown-linux-gnu" => "linux-x86_64", + "x86_64-unknown-linux-gnux32" => "linux-x32", "x86_64-unknown-linux-musl" => "linux-x86_64", "x86_64-unknown-netbsd" => "BSD-x86_64", _ => panic!("don't know how to configure OpenSSL for {}", target), diff --git a/src/doc/unstable-book/src/library-features/entry-and-modify.md b/src/doc/unstable-book/src/library-features/entry-and-modify.md deleted file mode 100644 index 1280c71e83c92..0000000000000 --- a/src/doc/unstable-book/src/library-features/entry-and-modify.md +++ /dev/null @@ -1,77 +0,0 @@ -# `entry_and_modify` - -The tracking issue for this feature is: [#44733] - -[#44733]: /~https://github.com/rust-lang/rust/issues/44733 - ------------------------- - -This introduces a new method for the Entry API of maps -(`std::collections::HashMap` and `std::collections::BTreeMap`), so that -occupied entries can be modified before any potential inserts into the -map. - -For example: - -```rust -#![feature(entry_and_modify)] -# fn main() { -use std::collections::HashMap; - -struct Foo { - new: bool, -} - -let mut map: HashMap<&str, Foo> = HashMap::new(); - -map.entry("quux") - .and_modify(|e| e.new = false) - .or_insert(Foo { new: true }); -# } -``` - -This is not possible with the stable API alone since inserting a default -_before_ modifying the `new` field would mean we would lose the default state: - -```rust -# fn main() { -use std::collections::HashMap; - -struct Foo { - new: bool, -} - -let mut map: HashMap<&str, Foo> = HashMap::new(); - -map.entry("quux").or_insert(Foo { new: true }).new = false; -# } -``` - -In the above code the `new` field will never be `true`, even though we only -intended to update that field to `false` for previously extant entries. - -To achieve the same effect as `and_modify` we would have to manually match -against the `Occupied` and `Vacant` variants of the `Entry` enum, which is -a little less user-friendly, and much more verbose: - -```rust -# fn main() { -use std::collections::HashMap; -use std::collections::hash_map::Entry; - -struct Foo { - new: bool, -} - -let mut map: HashMap<&str, Foo> = HashMap::new(); - -match map.entry("quux") { - Entry::Occupied(entry) => { - entry.into_mut().new = false; - }, - Entry::Vacant(entry) => { - entry.insert(Foo { new: true }); - }, -}; -# } -``` diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index b320bed54320a..618ef81fdd981 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -2114,7 +2114,6 @@ impl<'a, K: Ord, V> Entry<'a, K, V> { /// # Examples /// /// ``` - /// #![feature(entry_and_modify)] /// use std::collections::BTreeMap; /// /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); @@ -2129,7 +2128,7 @@ impl<'a, K: Ord, V> Entry<'a, K, V> { /// .or_insert(42); /// assert_eq!(map["poneyland"], 43); /// ``` - #[unstable(feature = "entry_and_modify", issue = "44733")] + #[stable(feature = "entry_and_modify", since = "1.26.0")] pub fn and_modify(self, mut f: F) -> Self where F: FnMut(&mut V) { diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index 877793cb3c57c..9d8a71250f88a 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -12,7 +12,8 @@ use cmp::Ordering; use ops::Try; use super::{AlwaysOk, LoopState}; -use super::{Chain, Cycle, Cloned, Enumerate, Filter, FilterMap, FlatMap, Fuse}; +use super::{Chain, Cycle, Cloned, Enumerate, Filter, FilterMap, Fuse}; +use super::{Flatten, FlatMap, flatten_compat}; use super::{Inspect, Map, Peekable, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, Rev}; use super::{Zip, Sum, Product}; use super::{ChainState, FromIterator, ZipImpl}; @@ -997,11 +998,15 @@ pub trait Iterator { /// an extra layer of indirection. `flat_map()` will remove this extra layer /// on its own. /// + /// You can think of [`flat_map(f)`][flat_map] as the semantic equivalent + /// of [`map`]ping, and then [`flatten`]ing as in `map(f).flatten()`. + /// /// Another way of thinking about `flat_map()`: [`map`]'s closure returns /// one item for each element, and `flat_map()`'s closure returns an /// iterator for each element. /// /// [`map`]: #method.map + /// [`flatten`]: #method.flatten /// /// # Examples /// @@ -1021,7 +1026,79 @@ pub trait Iterator { fn flat_map(self, f: F) -> FlatMap where Self: Sized, U: IntoIterator, F: FnMut(Self::Item) -> U, { - FlatMap{iter: self, f: f, frontiter: None, backiter: None } + FlatMap { inner: flatten_compat(self.map(f)) } + } + + /// Creates an iterator that flattens nested structure. + /// + /// This is useful when you have an iterator of iterators or an iterator of + /// things that can be turned into iterators and you want to remove one + /// level of indirection. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iterator_flatten)] + /// + /// let data = vec![vec![1, 2, 3, 4], vec![5, 6]]; + /// let flattened = data.into_iter().flatten().collect::>(); + /// assert_eq!(flattened, &[1, 2, 3, 4, 5, 6]); + /// ``` + /// + /// Mapping and then flattening: + /// + /// ``` + /// #![feature(iterator_flatten)] + /// + /// let words = ["alpha", "beta", "gamma"]; + /// + /// // chars() returns an iterator + /// let merged: String = words.iter() + /// .map(|s| s.chars()) + /// .flatten() + /// .collect(); + /// assert_eq!(merged, "alphabetagamma"); + /// ``` + /// + /// You can also rewrite this in terms of [`flat_map()`] which is preferable + /// in this case since that conveys intent clearer: + /// + /// ``` + /// let words = ["alpha", "beta", "gamma"]; + /// + /// // chars() returns an iterator + /// let merged: String = words.iter() + /// .flat_map(|s| s.chars()) + /// .collect(); + /// assert_eq!(merged, "alphabetagamma"); + /// ``` + /// + /// Flattening once only removes one level of nesting: + /// + /// ``` + /// #![feature(iterator_flatten)] + /// + /// let d3 = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]; + /// + /// let d2 = d3.iter().flatten().collect::>(); + /// assert_eq!(d2, [&[1, 2], &[3, 4], &[5, 6], &[7, 8]]); + /// + /// let d1 = d3.iter().flatten().flatten().collect::>(); + /// assert_eq!(d1, [&1, &2, &3, &4, &5, &6, &7, &8]); + /// ``` + /// + /// Here we see that `flatten()` does not perform a "deep" flatten. + /// Instead, only one level of nesting is removed. That is, if you + /// `flatten()` a three-dimensional array the result will be + /// two-dimensional and not one-dimensional. To get a one-dimensional + /// structure, you have to `flatten()` again. + #[inline] + #[unstable(feature = "iterator_flatten", issue = "48213")] + fn flatten(self) -> Flatten + where Self: Sized, Self::Item: IntoIterator { + Flatten { inner: flatten_compat(self) } } /// Creates an iterator which ends after the first [`None`]. diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 1a2da83429af9..623cad754dd72 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -2410,12 +2410,15 @@ impl Iterator for Scan where /// [`Iterator`]: trait.Iterator.html #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone)] pub struct FlatMap { - iter: I, - f: F, - frontiter: Option, - backiter: Option, + inner: FlattenCompat, ::IntoIter> +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for FlatMap + where ::IntoIter: Clone +{ + fn clone(&self) -> Self { FlatMap { inner: self.inner.clone() } } } #[stable(feature = "core_impl_debug", since = "1.9.0")] @@ -2423,11 +2426,7 @@ impl fmt::Debug for FlatMap where U::IntoIter: fmt::Debug { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("FlatMap") - .field("iter", &self.iter) - .field("frontiter", &self.frontiter) - .field("backiter", &self.backiter) - .finish() + f.debug_struct("FlatMap").field("inner", &self.inner).finish() } } @@ -2437,17 +2436,173 @@ impl Iterator for FlatMap { type Item = U::Item; + #[inline] + fn next(&mut self) -> Option { self.inner.next() } + + #[inline] + fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + self.inner.try_fold(init, fold) + } + + #[inline] + fn fold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.inner.fold(init, fold) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for FlatMap + where F: FnMut(I::Item) -> U, + U: IntoIterator, + U::IntoIter: DoubleEndedIterator +{ + #[inline] + fn next_back(&mut self) -> Option { self.inner.next_back() } + + #[inline] + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + self.inner.try_rfold(init, fold) + } + + #[inline] + fn rfold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.inner.rfold(init, fold) + } +} + +#[unstable(feature = "fused", issue = "35602")] +impl FusedIterator for FlatMap + where I: FusedIterator, U: IntoIterator, F: FnMut(I::Item) -> U {} + +/// An iterator that flattens one level of nesting in an iterator of things +/// that can be turned into iterators. +/// +/// This `struct` is created by the [`flatten`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`flatten`]: trait.Iterator.html#method.flatten +/// [`Iterator`]: trait.Iterator.html +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[unstable(feature = "iterator_flatten", issue = "48213")] +pub struct Flatten +where I::Item: IntoIterator { + inner: FlattenCompat::IntoIter>, +} + +#[unstable(feature = "iterator_flatten", issue = "48213")] +impl fmt::Debug for Flatten + where I: Iterator + fmt::Debug, U: Iterator + fmt::Debug, + I::Item: IntoIterator, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Flatten").field("inner", &self.inner).finish() + } +} + +#[unstable(feature = "iterator_flatten", issue = "48213")] +impl Clone for Flatten + where I: Iterator + Clone, U: Iterator + Clone, + I::Item: IntoIterator, +{ + fn clone(&self) -> Self { Flatten { inner: self.inner.clone() } } +} + +#[unstable(feature = "iterator_flatten", issue = "48213")] +impl Iterator for Flatten + where I: Iterator, U: Iterator, + I::Item: IntoIterator +{ + type Item = U::Item; + + #[inline] + fn next(&mut self) -> Option { self.inner.next() } + + #[inline] + fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + self.inner.try_fold(init, fold) + } + + #[inline] + fn fold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.inner.fold(init, fold) + } +} + +#[unstable(feature = "iterator_flatten", issue = "48213")] +impl DoubleEndedIterator for Flatten + where I: DoubleEndedIterator, U: DoubleEndedIterator, + I::Item: IntoIterator +{ + #[inline] + fn next_back(&mut self) -> Option { self.inner.next_back() } + + #[inline] + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + self.inner.try_rfold(init, fold) + } + + #[inline] + fn rfold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.inner.rfold(init, fold) + } +} + +#[unstable(feature = "fused", issue = "35602")] +impl FusedIterator for Flatten + where I: FusedIterator, U: Iterator, + I::Item: IntoIterator {} + +/// Adapts an iterator by flattening it, for use in `flatten()` and `flat_map()`. +fn flatten_compat(iter: I) -> FlattenCompat { + FlattenCompat { iter, frontiter: None, backiter: None } +} + +/// Real logic of both `Flatten` and `FlatMap` which simply delegate to +/// this type. +#[derive(Clone, Debug)] +struct FlattenCompat { + iter: I, + frontiter: Option, + backiter: Option, +} + +impl Iterator for FlattenCompat + where I: Iterator, U: Iterator, + I::Item: IntoIterator +{ + type Item = U::Item; + #[inline] fn next(&mut self) -> Option { loop { if let Some(ref mut inner) = self.frontiter { - if let Some(x) = inner.by_ref().next() { - return Some(x) - } + if let elt@Some(_) = inner.next() { return elt } } - match self.iter.next().map(&mut self.f) { + match self.iter.next() { None => return self.backiter.as_mut().and_then(|it| it.next()), - next => self.frontiter = next.map(IntoIterator::into_iter), + Some(inner) => self.frontiter = Some(inner.into_iter()), } } } @@ -2473,10 +2628,9 @@ impl Iterator for FlatMap self.frontiter = None; { - let f = &mut self.f; let frontiter = &mut self.frontiter; init = self.iter.try_fold(init, |acc, x| { - let mut mid = f(x).into_iter(); + let mut mid = x.into_iter(); let r = mid.try_fold(acc, &mut fold); *frontiter = Some(mid); r @@ -2497,27 +2651,23 @@ impl Iterator for FlatMap where Fold: FnMut(Acc, Self::Item) -> Acc, { self.frontiter.into_iter() - .chain(self.iter.map(self.f).map(U::into_iter)) + .chain(self.iter.map(IntoIterator::into_iter)) .chain(self.backiter) .fold(init, |acc, iter| iter.fold(acc, &mut fold)) } } -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for FlatMap where - F: FnMut(I::Item) -> U, - U: IntoIterator, - U::IntoIter: DoubleEndedIterator +impl DoubleEndedIterator for FlattenCompat + where I: DoubleEndedIterator, U: DoubleEndedIterator, + I::Item: IntoIterator { #[inline] fn next_back(&mut self) -> Option { loop { if let Some(ref mut inner) = self.backiter { - if let Some(y) = inner.next_back() { - return Some(y) - } + if let elt@Some(_) = inner.next_back() { return elt } } - match self.iter.next_back().map(&mut self.f) { + match self.iter.next_back() { None => return self.frontiter.as_mut().and_then(|it| it.next_back()), next => self.backiter = next.map(IntoIterator::into_iter), } @@ -2534,10 +2684,9 @@ impl DoubleEndedIterator for FlatMap wher self.backiter = None; { - let f = &mut self.f; let backiter = &mut self.backiter; init = self.iter.try_rfold(init, |acc, x| { - let mut mid = f(x).into_iter(); + let mut mid = x.into_iter(); let r = mid.try_rfold(acc, &mut fold); *backiter = Some(mid); r @@ -2558,16 +2707,12 @@ impl DoubleEndedIterator for FlatMap wher where Fold: FnMut(Acc, Self::Item) -> Acc, { self.frontiter.into_iter() - .chain(self.iter.map(self.f).map(U::into_iter)) + .chain(self.iter.map(IntoIterator::into_iter)) .chain(self.backiter) .rfold(init, |acc, iter| iter.rfold(acc, &mut fold)) } } -#[unstable(feature = "fused", issue = "35602")] -impl FusedIterator for FlatMap - where I: FusedIterator, U: IntoIterator, F: FnMut(I::Item) -> U {} - /// An iterator that yields `None` forever after the underlying iterator /// yields `None` once. /// diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index d2162d307e038..3dd30ee1c69e2 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -93,6 +93,7 @@ #![feature(doc_spotlight)] #![feature(rustc_const_unstable)] #![feature(iterator_repeat_with)] +#![feature(iterator_flatten)] #[prelude_import] #[allow(unused)] diff --git a/src/libcore/num/dec2flt/parse.rs b/src/libcore/num/dec2flt/parse.rs index d20986faa0fc2..69418434ebead 100644 --- a/src/libcore/num/dec2flt/parse.rs +++ b/src/libcore/num/dec2flt/parse.rs @@ -73,7 +73,8 @@ pub fn parse_decimal(s: &str) -> ParseResult { } Some(&b'.') => { let (fractional, s) = eat_digits(&s[1..]); - if integral.is_empty() && fractional.is_empty() && s.is_empty() { + if integral.is_empty() && fractional.is_empty() { + // We require at least a single digit before or after the point. return Invalid; } diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 43330b63f9b9c..7d5aa25016bdd 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -2892,7 +2892,7 @@ impl u8 { } /// Checks if the value is an ASCII graphic character: - /// U+0021 '@' ... U+007E '~'. + /// U+0021 '!' ... U+007E '~'. /// /// # Examples /// diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index f91c919d7447d..edd75f7795ed7 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -874,6 +874,44 @@ fn test_iterator_flat_map_fold() { assert_eq!(i, 0); } +#[test] +fn test_iterator_flatten() { + let xs = [0, 3, 6]; + let ys = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let it = xs.iter().map(|&x| (x..).step_by(1).take(3)).flatten(); + let mut i = 0; + for x in it { + assert_eq!(x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); +} + +/// Test `Flatten::fold` with items already picked off the front and back, +/// to make sure all parts of the `Flatten` are folded correctly. +#[test] +fn test_iterator_flatten_fold() { + let xs = [0, 3, 6]; + let ys = [1, 2, 3, 4, 5, 6, 7]; + let mut it = xs.iter().map(|&x| x..x+3).flatten(); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let i = it.fold(0, |i, x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().map(|&x| x..x+3).flatten(); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let i = it.rfold(ys.len(), |i, x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + #[test] fn test_inspect() { let xs = [1, 2, 3, 4]; @@ -1287,6 +1325,23 @@ fn test_double_ended_flat_map() { assert_eq!(it.next_back(), None); } +#[test] +fn test_double_ended_flatten() { + let u = [0,1]; + let v = [5,6,7,8]; + let mut it = u.iter().map(|x| &v[*x..v.len()]).flatten(); + assert_eq!(it.next_back().unwrap(), &8); + assert_eq!(it.next().unwrap(), &5); + assert_eq!(it.next_back().unwrap(), &7); + assert_eq!(it.next_back().unwrap(), &6); + assert_eq!(it.next_back().unwrap(), &8); + assert_eq!(it.next().unwrap(), &6); + assert_eq!(it.next_back().unwrap(), &7); + assert_eq!(it.next_back(), None); + assert_eq!(it.next(), None); + assert_eq!(it.next_back(), None); +} + #[test] fn test_double_ended_range() { assert_eq!((11..14).rev().collect::>(), [13, 12, 11]); @@ -1978,3 +2033,54 @@ fn test_flat_map_try_folds() { assert_eq!(iter.try_rfold(0, i8::checked_add), None); assert_eq!(iter.next_back(), Some(35)); } + +#[test] +fn test_flatten_try_folds() { + let f = &|acc, x| i32::checked_add(acc*2/3, x); + let mr = &|x| (5*x)..(5*x + 5); + assert_eq!((0..10).map(mr).flatten().try_fold(7, f), (0..50).try_fold(7, f)); + assert_eq!((0..10).map(mr).flatten().try_rfold(7, f), (0..50).try_rfold(7, f)); + let mut iter = (0..10).map(mr).flatten(); + iter.next(); iter.next_back(); // have front and back iters in progress + assert_eq!(iter.try_rfold(7, f), (1..49).try_rfold(7, f)); + + let mut iter = (0..10).map(|x| (4*x)..(4*x + 4)).flatten(); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(17)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(35)); +} + +#[test] +fn test_functor_laws() { + // identity: + fn identity(x: T) -> T { x } + assert_eq!((0..10).map(identity).sum::(), (0..10).sum()); + + // composition: + fn f(x: usize) -> usize { x + 3 } + fn g(x: usize) -> usize { x * 2 } + fn h(x: usize) -> usize { g(f(x)) } + assert_eq!((0..10).map(f).map(g).sum::(), (0..10).map(h).sum()); +} + +#[test] +fn test_monad_laws_left_identity() { + fn f(x: usize) -> impl Iterator { + (0..10).map(move |y| x * y) + } + assert_eq!(once(42).flat_map(f.clone()).sum::(), f(42).sum()); +} + +#[test] +fn test_monad_laws_right_identity() { + assert_eq!((0..10).flat_map(|x| once(x)).sum::(), (0..10).sum()); +} + +#[test] +fn test_monad_laws_associativity() { + fn f(x: usize) -> impl Iterator { 0..x } + fn g(x: usize) -> impl Iterator { (0..x).rev() } + assert_eq!((0..10).flat_map(f).flat_map(g).sum::(), + (0..10).flat_map(|x| f(x).flat_map(g)).sum::()); +} diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 3e901a9d442ce..7954d52f6b1e3 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -25,6 +25,8 @@ #![feature(inclusive_range)] #![feature(inclusive_range_syntax)] #![feature(iterator_try_fold)] +#![feature(iterator_flatten)] +#![feature(conservative_impl_trait)] #![feature(iter_rfind)] #![feature(iter_rfold)] #![feature(iterator_repeat_with)] diff --git a/src/libcore/tests/num/dec2flt/mod.rs b/src/libcore/tests/num/dec2flt/mod.rs index 9934e1dab9662..17b2f59cd4df2 100644 --- a/src/libcore/tests/num/dec2flt/mod.rs +++ b/src/libcore/tests/num/dec2flt/mod.rs @@ -101,6 +101,12 @@ fn lonely_dot() { assert!(".".parse::().is_err()); } +#[test] +fn exponentiated_dot() { + assert!(".e0".parse::().is_err()); + assert!(".e0".parse::().is_err()); +} + #[test] fn lonely_sign() { assert!("+".parse::().is_err()); diff --git a/src/librustc_back/target/freebsd_base.rs b/src/librustc_back/target/freebsd_base.rs index 21dca99aa5005..a0f84a6ab0495 100644 --- a/src/librustc_back/target/freebsd_base.rs +++ b/src/librustc_back/target/freebsd_base.rs @@ -33,6 +33,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, pre_link_args: args, position_independent_executables: true, + eliminate_frame_pointer: false, // FIXME 43575 relro_level: RelroLevel::Full, exe_allocation_crate: super::maybe_jemalloc(), .. Default::default() diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 2872c59157d6b..d356105e39314 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -146,6 +146,7 @@ supported_targets! { ("powerpc64-unknown-linux-gnu", powerpc64_unknown_linux_gnu), ("powerpc64le-unknown-linux-gnu", powerpc64le_unknown_linux_gnu), ("s390x-unknown-linux-gnu", s390x_unknown_linux_gnu), + ("sparc-unknown-linux-gnu", sparc_unknown_linux_gnu), ("sparc64-unknown-linux-gnu", sparc64_unknown_linux_gnu), ("arm-unknown-linux-gnueabi", arm_unknown_linux_gnueabi), ("arm-unknown-linux-gnueabihf", arm_unknown_linux_gnueabihf), @@ -186,6 +187,7 @@ supported_targets! { ("x86_64-unknown-openbsd", x86_64_unknown_openbsd), ("i686-unknown-netbsd", i686_unknown_netbsd), + ("powerpc-unknown-netbsd", powerpc_unknown_netbsd), ("sparc64-unknown-netbsd", sparc64_unknown_netbsd), ("x86_64-unknown-netbsd", x86_64_unknown_netbsd), ("x86_64-rumprun-netbsd", x86_64_rumprun_netbsd), diff --git a/src/librustc_back/target/powerpc_unknown_netbsd.rs b/src/librustc_back/target/powerpc_unknown_netbsd.rs new file mode 100644 index 0000000000000..2c78dbd206171 --- /dev/null +++ b/src/librustc_back/target/powerpc_unknown_netbsd.rs @@ -0,0 +1,35 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use LinkerFlavor; +use target::{Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::netbsd_base::opts(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.max_atomic_width = Some(32); + + // see #36994 + base.exe_allocation_crate = None; + + Ok(Target { + llvm_target: "powerpc-unknown-netbsd".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), + arch: "powerpc".to_string(), + target_os: "netbsd".to_string(), + target_env: "".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/src/librustc_back/target/sparc_unknown_linux_gnu.rs b/src/librustc_back/target/sparc_unknown_linux_gnu.rs new file mode 100644 index 0000000000000..a03e6937b2ca7 --- /dev/null +++ b/src/librustc_back/target/sparc_unknown_linux_gnu.rs @@ -0,0 +1,34 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use LinkerFlavor; +use target::{Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + base.cpu = "v9".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mv8plus".to_string()); + base.exe_allocation_crate = None; + + Ok(Target { + llvm_target: "sparc-unknown-linux-gnu".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-p:32:32-i64:64-f128:64-n32-S64".to_string(), + arch: "sparc".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index 12698964d2e65..ee0f2415bd808 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -40,7 +40,7 @@ use rustc::ty::layout::{self, Align, Size, TyLayout}; use rustc::ty::layout::{HasDataLayout, LayoutOf}; use libc::c_uint; -use std::{cmp, iter}; +use std::cmp; pub use syntax::abi::Abi; pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; @@ -279,30 +279,6 @@ impl Uniform { pub fn align(&self, cx: &CodegenCx) -> Align { self.unit.align(cx) } - - pub fn llvm_type(&self, cx: &CodegenCx) -> Type { - let llunit = self.unit.llvm_type(cx); - - if self.total <= self.unit.size { - return llunit; - } - - let count = self.total.bytes() / self.unit.size.bytes(); - let rem_bytes = self.total.bytes() % self.unit.size.bytes(); - - if rem_bytes == 0 { - return Type::array(&llunit, count); - } - - // Only integers can be really split further. - assert_eq!(self.unit.kind, RegKind::Integer); - - let args: Vec<_> = (0..count).map(|_| llunit) - .chain(iter::once(Type::ix(cx, rem_bytes * 8))) - .collect(); - - Type::struct_(cx, &args, false) - } } pub trait LayoutExt<'tcx> { @@ -405,55 +381,81 @@ impl<'tcx> LayoutExt<'tcx> for TyLayout<'tcx> { } #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum CastTarget { - Uniform(Uniform), - Pair(Reg, Reg) +pub struct CastTarget { + pub prefix: [Option; 8], + pub prefix_chunk: Size, + pub rest: Uniform, } impl From for CastTarget { fn from(unit: Reg) -> CastTarget { - CastTarget::Uniform(Uniform::from(unit)) + CastTarget::from(Uniform::from(unit)) } } impl From for CastTarget { fn from(uniform: Uniform) -> CastTarget { - CastTarget::Uniform(uniform) + CastTarget { + prefix: [None; 8], + prefix_chunk: Size::from_bytes(0), + rest: uniform + } } } impl CastTarget { - pub fn size(&self, cx: &CodegenCx) -> Size { - match *self { - CastTarget::Uniform(u) => u.total, - CastTarget::Pair(a, b) => { - (a.size.abi_align(a.align(cx)) + b.size) - .abi_align(self.align(cx)) - } + pub fn pair(a: Reg, b: Reg) -> CastTarget { + CastTarget { + prefix: [Some(a.kind), None, None, None, None, None, None, None], + prefix_chunk: a.size, + rest: Uniform::from(b) } } + pub fn size(&self, cx: &CodegenCx) -> Size { + (self.prefix_chunk * self.prefix.iter().filter(|x| x.is_some()).count() as u64) + .abi_align(self.rest.align(cx)) + self.rest.total + } + pub fn align(&self, cx: &CodegenCx) -> Align { - match *self { - CastTarget::Uniform(u) => u.align(cx), - CastTarget::Pair(a, b) => { - cx.data_layout().aggregate_align - .max(a.align(cx)) - .max(b.align(cx)) - } - } + self.prefix.iter() + .filter_map(|x| x.map(|kind| Reg { kind: kind, size: self.prefix_chunk }.align(cx))) + .fold(cx.data_layout().aggregate_align.max(self.rest.align(cx)), + |acc, align| acc.max(align)) } pub fn llvm_type(&self, cx: &CodegenCx) -> Type { - match *self { - CastTarget::Uniform(u) => u.llvm_type(cx), - CastTarget::Pair(a, b) => { - Type::struct_(cx, &[ - a.llvm_type(cx), - b.llvm_type(cx) - ], false) + let rest_ll_unit = self.rest.unit.llvm_type(cx); + let rest_count = self.rest.total.bytes() / self.rest.unit.size.bytes(); + let rem_bytes = self.rest.total.bytes() % self.rest.unit.size.bytes(); + + if self.prefix.iter().all(|x| x.is_none()) { + // Simplify to a single unit when there is no prefix and size <= unit size + if self.rest.total <= self.rest.unit.size { + return rest_ll_unit; + } + + // Simplify to array when all chunks are the same size and type + if rem_bytes == 0 { + return Type::array(&rest_ll_unit, rest_count); } } + + // Create list of fields in the main structure + let mut args: Vec<_> = + self.prefix.iter().flat_map(|option_kind| option_kind.map( + |kind| Reg { kind: kind, size: self.prefix_chunk }.llvm_type(cx))) + .chain((0..rest_count).map(|_| rest_ll_unit)) + .collect(); + + // Append final integer + if rem_bytes != 0 { + // Only integers can be really split further. + assert_eq!(self.rest.unit.kind, RegKind::Integer); + args.push(Type::ix(cx, rem_bytes * 8)); + } + + Type::struct_(cx, &args, false) } } diff --git a/src/librustc_trans/back/command.rs b/src/librustc_trans/back/command.rs index 3b765a493e0e7..0bccef1e62a8e 100644 --- a/src/librustc_trans/back/command.rs +++ b/src/librustc_trans/back/command.rs @@ -109,6 +109,10 @@ impl Command { // extensions + pub fn get_args(&self) -> &[OsString] { + &self.args + } + pub fn take_args(&mut self) -> Vec { mem::replace(&mut self.args, Vec::new()) } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 4fe294a790fc4..d7c75dea8d04e 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -700,9 +700,6 @@ fn link_natively(sess: &Session, prog = time(sess.time_passes(), "running linker", || { exec_linker(sess, &mut cmd, tmpdir) }); - if !retry_on_segfault || i > 3 { - break - } let output = match prog { Ok(ref output) => output, Err(_) => break, @@ -713,6 +710,31 @@ fn link_natively(sess: &Session, let mut out = output.stderr.clone(); out.extend(&output.stdout); let out = String::from_utf8_lossy(&out); + + // Check to see if the link failed with "unrecognized command line option: + // '-no-pie'" for gcc or "unknown argument: '-no-pie'" for clang. If so, + // reperform the link step without the -no-pie option. This is safe because + // if the linker doesn't support -no-pie then it should not default to + // linking executables as pie. Different versions of gcc seem to use + // different quotes in the error message so don't check for them. + if sess.target.target.options.linker_is_gnu && + (out.contains("unrecognized command line option") || + out.contains("unknown argument")) && + out.contains("-no-pie") && + cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") { + info!("linker output: {:?}", out); + warn!("Linker does not support -no-pie command line option. Retrying without."); + for arg in cmd.take_args() { + if arg.to_string_lossy() != "-no-pie" { + cmd.arg(arg); + } + } + info!("{:?}", &cmd); + continue; + } + if !retry_on_segfault || i > 3 { + break + } let msg_segv = "clang: error: unable to execute command: Segmentation fault: 11"; let msg_bus = "clang: error: unable to execute command: Bus error: 10"; if !(out.contains(msg_segv) || out.contains(msg_bus)) { @@ -949,16 +971,30 @@ fn link_args(cmd: &mut Linker, let used_link_args = &trans.crate_info.link_args; - if crate_type == config::CrateTypeExecutable && - t.options.position_independent_executables { - let empty_vec = Vec::new(); - let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); - let more_args = &sess.opts.cg.link_arg; - let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter()); - - if get_reloc_model(sess) == llvm::RelocMode::PIC - && !sess.crt_static() && !args.any(|x| *x == "-static") { + if crate_type == config::CrateTypeExecutable { + let mut position_independent_executable = false; + + if t.options.position_independent_executables { + let empty_vec = Vec::new(); + let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); + let more_args = &sess.opts.cg.link_arg; + let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter()); + + if get_reloc_model(sess) == llvm::RelocMode::PIC + && !sess.crt_static() && !args.any(|x| *x == "-static") { + position_independent_executable = true; + } + } + + if position_independent_executable { cmd.position_independent_executable(); + } else { + // recent versions of gcc can be configured to generate position + // independent executables by default. We have to pass -no-pie to + // explicitly turn that off. + if sess.target.target.options.linker_is_gnu { + cmd.no_position_independent_executable(); + } } } diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index aa29c3cc12058..7e7811c56c74e 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -105,6 +105,7 @@ pub trait Linker { fn add_object(&mut self, path: &Path); fn gc_sections(&mut self, keep_metadata: bool); fn position_independent_executable(&mut self); + fn no_position_independent_executable(&mut self); fn partial_relro(&mut self); fn full_relro(&mut self); fn optimize(&mut self); @@ -179,6 +180,7 @@ impl<'a> Linker for GccLinker<'a> { fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); } fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } + fn no_position_independent_executable(&mut self) { self.cmd.arg("-no-pie"); } fn partial_relro(&mut self) { self.linker_arg("-z,relro"); } fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); } fn build_static_executable(&mut self) { self.cmd.arg("-static"); } @@ -439,6 +441,10 @@ impl<'a> Linker for MsvcLinker<'a> { // noop } + fn no_position_independent_executable(&mut self) { + // noop + } + fn partial_relro(&mut self) { // noop } @@ -647,6 +653,10 @@ impl<'a> Linker for EmLinker<'a> { // noop } + fn no_position_independent_executable(&mut self) { + // noop + } + fn partial_relro(&mut self) { // noop } diff --git a/src/librustc_trans/cabi_mips64.rs b/src/librustc_trans/cabi_mips64.rs index e44063faab810..94bf53cee1edb 100644 --- a/src/librustc_trans/cabi_mips64.rs +++ b/src/librustc_trans/cabi_mips64.rs @@ -8,50 +8,160 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use abi::{ArgType, FnType, LayoutExt, Reg, Uniform}; +use abi::{ArgAttribute, ArgType, CastTarget, FnType, LayoutExt, PassMode, Reg, RegKind, Uniform}; use context::CodegenCx; +use rustc::ty::layout::{self, Size}; -use rustc::ty::layout::Size; +fn extend_integer_width_mips(arg: &mut ArgType, bits: u64) { + // Always sign extend u32 values on 64-bit mips + if let layout::Abi::Scalar(ref scalar) = arg.layout.abi { + if let layout::Int(i, signed) = scalar.value { + if !signed && i.size().bits() == 32 { + if let PassMode::Direct(ref mut attrs) = arg.mode { + attrs.set(ArgAttribute::SExt); + return; + } + } + } + } + + arg.extend_integer_width_to(bits); +} + +fn bits_to_int_reg(bits: u64) -> Reg { + if bits <= 8 { + Reg::i8() + } else if bits <= 16 { + Reg::i16() + } else if bits <= 32 { + Reg::i32() + } else { + Reg::i64() + } +} -fn classify_ret_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - ret: &mut ArgType<'tcx>, - offset: &mut Size) { +fn float_reg<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ret: &ArgType<'tcx>, i: usize) -> Option { + match ret.layout.field(cx, i).abi { + layout::Abi::Scalar(ref scalar) => match scalar.value { + layout::F32 => Some(Reg::f32()), + layout::F64 => Some(Reg::f64()), + _ => None + }, + _ => None + } +} + +fn classify_ret_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ret: &mut ArgType<'tcx>) { if !ret.layout.is_aggregate() { - ret.extend_integer_width_to(64); + extend_integer_width_mips(ret, 64); + return; + } + + let size = ret.layout.size; + let bits = size.bits(); + if bits <= 128 { + // Unlike other architectures which return aggregates in registers, MIPS n64 limits the + // use of float registers to structures (not unions) containing exactly one or two + // float fields. + + if let layout::FieldPlacement::Arbitrary { .. } = ret.layout.fields { + if ret.layout.fields.count() == 1 { + if let Some(reg) = float_reg(cx, ret, 0) { + ret.cast_to(reg); + return; + } + } else if ret.layout.fields.count() == 2 { + if let Some(reg0) = float_reg(cx, ret, 0) { + if let Some(reg1) = float_reg(cx, ret, 1) { + ret.cast_to(CastTarget::pair(reg0, reg1)); + return; + } + } + } + } + + // Cast to a uniform int structure + ret.cast_to(Uniform { + unit: bits_to_int_reg(bits), + total: size + }); } else { ret.make_indirect(); - *offset += cx.tcx.data_layout.pointer_size; } } -fn classify_arg_ty(cx: &CodegenCx, arg: &mut ArgType, offset: &mut Size) { +fn classify_arg_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, arg: &mut ArgType<'tcx>) { + if !arg.layout.is_aggregate() { + extend_integer_width_mips(arg, 64); + return; + } + let dl = &cx.tcx.data_layout; let size = arg.layout.size; - let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align); + let mut prefix = [None; 8]; + let mut prefix_index = 0; - if arg.layout.is_aggregate() { - arg.cast_to(Uniform { - unit: Reg::i64(), - total: size - }); - if !offset.is_abi_aligned(align) { - arg.pad_with(Reg::i64()); + match arg.layout.fields { + layout::FieldPlacement::Array { .. } => { + // Arrays are passed indirectly + arg.make_indirect(); + return; } - } else { - arg.extend_integer_width_to(64); - } + layout::FieldPlacement::Union(_) => { + // Unions and are always treated as a series of 64-bit integer chunks + }, + layout::FieldPlacement::Arbitrary { .. } => { + // Structures are split up into a series of 64-bit integer chunks, but any aligned + // doubles not part of another aggregate are passed as floats. + let mut last_offset = Size::from_bytes(0); + + for i in 0..arg.layout.fields.count() { + let field = arg.layout.field(cx, i); + let offset = arg.layout.fields.offset(i); + + // We only care about aligned doubles + if let layout::Abi::Scalar(ref scalar) = field.abi { + if let layout::F64 = scalar.value { + if offset.is_abi_aligned(dl.f64_align) { + // Insert enough integers to cover [last_offset, offset) + assert!(last_offset.is_abi_aligned(dl.f64_align)); + for _ in 0..((offset - last_offset).bits() / 64) + .min((prefix.len() - prefix_index) as u64) { + + prefix[prefix_index] = Some(RegKind::Integer); + prefix_index += 1; + } + + if prefix_index == prefix.len() { + break; + } + + prefix[prefix_index] = Some(RegKind::Float); + prefix_index += 1; + last_offset = offset + Reg::f64().size; + } + } + } + } + } + }; - *offset = offset.abi_align(align) + size.abi_align(align); + // Extract first 8 chunks as the prefix + let rest_size = size - Size::from_bytes(8) * prefix_index as u64; + arg.cast_to(CastTarget { + prefix: prefix, + prefix_chunk: Size::from_bytes(8), + rest: Uniform { unit: Reg::i64(), total: rest_size } + }); } pub fn compute_abi_info<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, fty: &mut FnType<'tcx>) { - let mut offset = Size::from_bytes(0); if !fty.ret.is_ignore() { - classify_ret_ty(cx, &mut fty.ret, &mut offset); + classify_ret_ty(cx, &mut fty.ret); } for arg in &mut fty.args { if arg.is_ignore() { continue; } - classify_arg_ty(cx, arg, &mut offset); + classify_arg_ty(cx, arg); } } diff --git a/src/librustc_trans/cabi_x86_64.rs b/src/librustc_trans/cabi_x86_64.rs index b8144a3ca7a3e..7eadaa7f493a3 100644 --- a/src/librustc_trans/cabi_x86_64.rs +++ b/src/librustc_trans/cabi_x86_64.rs @@ -171,7 +171,7 @@ fn cast_target(cls: &[Option], size: Size) -> CastTarget { let mut target = CastTarget::from(lo); if size > offset { if let Some(hi) = reg_component(cls, &mut i, size - offset) { - target = CastTarget::Pair(lo, hi); + target = CastTarget::pair(lo, hi); } } assert_eq!(reg_component(cls, &mut i, Size::from_bytes(0)), None); diff --git a/src/librustc_trans/llvm_util.rs b/src/librustc_trans/llvm_util.rs index b25562252e72e..6271dcdfb2433 100644 --- a/src/librustc_trans/llvm_util.rs +++ b/src/librustc_trans/llvm_util.rs @@ -75,26 +75,25 @@ unsafe fn configure_llvm(sess: &Session) { llvm_args.as_ptr()); } -// WARNING: the features must be known to LLVM or the feature -// detection code will walk past the end of the feature array, -// leading to crashes. +// WARNING: the features after applying `to_llvm_feature` must be known +// to LLVM or the feature detection code will walk past the end of the feature +// array, leading to crashes. const ARM_WHITELIST: &'static [&'static str] = &["neon", "v7", "vfp2", "vfp3", "vfp4"]; const AARCH64_WHITELIST: &'static [&'static str] = &["neon", "v7"]; -const X86_WHITELIST: &'static [&'static str] = &["avx", "avx2", "bmi", "bmi2", "sse", - "sse2", "sse3", "sse4.1", "sse4.2", - "ssse3", "tbm", "lzcnt", "popcnt", - "sse4a", "rdrnd", "rdseed", "fma", - "xsave", "xsaveopt", "xsavec", - "xsaves", "aes", "pclmulqdq", - "avx512bw", "avx512cd", - "avx512dq", "avx512er", - "avx512f", "avx512ifma", - "avx512pf", "avx512vbmi", - "avx512vl", "avx512vpopcntdq", - "mmx", "fxsr"]; +const X86_WHITELIST: &'static [&'static str] = &["aes", "avx", "avx2", "avx512bw", + "avx512cd", "avx512dq", "avx512er", + "avx512f", "avx512ifma", "avx512pf", + "avx512vbmi", "avx512vl", "avx512vpopcntdq", + "bmi", "bmi2", "fma", "fxsr", + "lzcnt", "mmx", "pclmulqdq", + "popcnt", "rdrand", "rdseed", + "sse", "sse2", "sse3", "sse4.1", + "sse4.2", "sse4a", "ssse3", + "tbm", "xsave", "xsavec", + "xsaveopt", "xsaves"]; const HEXAGON_WHITELIST: &'static [&'static str] = &["hvx", "hvx-double"]; @@ -108,6 +107,7 @@ const MIPS_WHITELIST: &'static [&'static str] = &["msa"]; pub fn to_llvm_feature(s: &str) -> &str { match s { "pclmulqdq" => "pclmul", + "rdrand" => "rdrnd", s => s, } } diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs index 82e1a3447dc5d..430c9df396a5e 100644 --- a/src/libstd/ascii.rs +++ b/src/libstd/ascii.rs @@ -245,7 +245,7 @@ pub trait AsciiExt { fn is_ascii_punctuation(&self) -> bool { unimplemented!(); } /// Checks if the value is an ASCII graphic character: - /// U+0021 '@' ... U+007E '~'. + /// U+0021 '!' ... U+007E '~'. /// For strings, true if all characters in the string are /// ASCII graphic characters. /// diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index a82ff915093c6..4dfdc23ebee53 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -2082,7 +2082,6 @@ impl<'a, K, V> Entry<'a, K, V> { /// # Examples /// /// ``` - /// #![feature(entry_and_modify)] /// use std::collections::HashMap; /// /// let mut map: HashMap<&str, u32> = HashMap::new(); @@ -2097,7 +2096,7 @@ impl<'a, K, V> Entry<'a, K, V> { /// .or_insert(42); /// assert_eq!(map["poneyland"], 43); /// ``` - #[unstable(feature = "entry_and_modify", issue = "44733")] + #[stable(feature = "entry_and_modify", since = "1.26.0")] pub fn and_modify(self, mut f: F) -> Self where F: FnMut(&mut V) { diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index 263a2c13249b4..7931d14ed1166 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -1545,6 +1545,26 @@ mod tests { drop(listener); } + // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors + // when passed zero Durations + #[test] + fn test_timeout_zero_duration() { + let addr = next_test_ip4(); + + let listener = t!(TcpListener::bind(&addr)); + let stream = t!(TcpStream::connect(&addr)); + + let result = stream.set_write_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + let result = stream.set_read_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + drop(listener); + } + #[test] fn nodelay() { let addr = next_test_ip4(); diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index 5e19519b88fd5..b49cc78ac4073 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -1024,6 +1024,23 @@ mod tests { assert!(start.elapsed() > Duration::from_millis(400)); } + // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors + // when passed zero Durations + #[test] + fn test_timeout_zero_duration() { + let addr = next_test_ip4(); + + let socket = t!(UdpSocket::bind(&addr)); + + let result = socket.set_write_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + let result = socket.set_read_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + } + #[test] fn connect_send_recv() { let addr = next_test_ip4(); diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 54bb65136508b..3014283da5b27 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -14,7 +14,7 @@ use sync::{mutex, MutexGuard, PoisonError}; use sys_common::condvar as sys; use sys_common::mutex as sys_mutex; use sys_common::poison::{self, LockResult}; -use time::Duration; +use time::{Duration, Instant}; /// A type indicating whether a timed wait on a condition variable returned /// due to a time out or not. @@ -221,6 +221,64 @@ impl Condvar { } } + /// Blocks the current thread until this condition variable receives a + /// notification and the required condition is met. Spurious wakeups are + /// ignored and this function will only return once the condition has been + /// met. + /// + /// This function will atomically unlock the mutex specified (represented by + /// `guard`) and block the current thread. This means that any calls + /// to [`notify_one`] or [`notify_all`] which happen logically after the + /// mutex is unlocked are candidates to wake this thread up. When this + /// function call returns, the lock specified will have been re-acquired. + /// + /// # Errors + /// + /// This function will return an error if the mutex being waited on is + /// poisoned when this thread re-acquires the lock. For more information, + /// see information about [poisoning] on the [`Mutex`] type. + /// + /// [`notify_one`]: #method.notify_one + /// [`notify_all`]: #method.notify_all + /// [poisoning]: ../sync/struct.Mutex.html#poisoning + /// [`Mutex`]: ../sync/struct.Mutex.html + /// + /// # Examples + /// + /// ``` + /// #![feature(wait_until)] + /// + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// thread::spawn(move|| { + /// let &(ref lock, ref cvar) = &*pair2; + /// let mut started = lock.lock().unwrap(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let &(ref lock, ref cvar) = &*pair; + /// // As long as the value inside the `Mutex` is false, we wait. + /// let _guard = cvar.wait_until(lock.lock().unwrap(), |started| { *started }).unwrap(); + /// ``` + #[unstable(feature = "wait_until", issue = "47960")] + pub fn wait_until<'a, T, F>(&self, mut guard: MutexGuard<'a, T>, + mut condition: F) + -> LockResult> + where F: FnMut(&mut T) -> bool { + while !condition(&mut *guard) { + guard = self.wait(guard)?; + } + Ok(guard) + } + + /// Waits on this condition variable for a notification, timing out after a /// specified duration. /// @@ -295,7 +353,15 @@ impl Condvar { /// /// Note that the best effort is made to ensure that the time waited is /// measured with a monotonic clock, and not affected by the changes made to - /// the system time. + /// the system time. This function is susceptible to spurious wakeups. + /// Condition variables normally have a boolean predicate associated with + /// them, and the predicate must always be checked each time this function + /// returns to protect against spurious wakeups. Additionally, it is + /// typically desirable for the time-out to not exceed some duration in + /// spite of spurious wakes, thus the sleep-duration is decremented by the + /// amount slept. Alternatively, use the `wait_timeout_until` method + /// to wait until a condition is met with a total time-out regardless + /// of spurious wakes. /// /// The returned [`WaitTimeoutResult`] value indicates if the timeout is /// known to have elapsed. @@ -304,6 +370,7 @@ impl Condvar { /// returns, regardless of whether the timeout elapsed or not. /// /// [`wait`]: #method.wait + /// [`wait_timeout_until`]: #method.wait_timeout_until /// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html /// /// # Examples @@ -355,6 +422,80 @@ impl Condvar { } } + /// Waits on this condition variable for a notification, timing out after a + /// specified duration. Spurious wakes will not cause this function to + /// return. + /// + /// The semantics of this function are equivalent to [`wait_until`] except + /// that the thread will be blocked for roughly no longer than `dur`. This + /// method should not be used for precise timing due to anomalies such as + /// preemption or platform differences that may not cause the maximum + /// amount of time waited to be precisely `dur`. + /// + /// Note that the best effort is made to ensure that the time waited is + /// measured with a monotonic clock, and not affected by the changes made to + /// the system time. + /// + /// The returned [`WaitTimeoutResult`] value indicates if the timeout is + /// known to have elapsed without the condition being met. + /// + /// Like [`wait_until`], the lock specified will be re-acquired when this + /// function returns, regardless of whether the timeout elapsed or not. + /// + /// [`wait_until`]: #method.wait_until + /// [`wait_timeout`]: #method.wait_timeout + /// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html + /// + /// # Examples + /// + /// ``` + /// #![feature(wait_timeout_until)] + /// + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// use std::time::Duration; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// thread::spawn(move|| { + /// let &(ref lock, ref cvar) = &*pair2; + /// let mut started = lock.lock().unwrap(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // wait for the thread to start up + /// let &(ref lock, ref cvar) = &*pair; + /// let result = cvar.wait_timeout_until( + /// lock.lock().unwrap(), + /// Duration::from_millis(100), + /// |&mut started| started, + /// ).unwrap(); + /// if result.1.timed_out() { + /// // timed-out without the condition ever evaluating to true. + /// } + /// // access the locked mutex via result.0 + /// ``` + #[unstable(feature = "wait_timeout_until", issue = "47960")] + pub fn wait_timeout_until<'a, T, F>(&self, mut guard: MutexGuard<'a, T>, + dur: Duration, mut condition: F) + -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> + where F: FnMut(&mut T) -> bool { + let start = Instant::now(); + loop { + if condition(&mut *guard) { + return Ok((guard, WaitTimeoutResult(false))); + } + let timeout = match dur.checked_sub(start.elapsed()) { + Some(timeout) => timeout, + None => return Ok((guard, WaitTimeoutResult(true))), + }; + guard = self.wait_timeout(guard, timeout)?.0; + } + } + /// Wakes up one blocked thread on this condvar. /// /// If there is a blocked thread on this condition variable, then it will @@ -480,6 +621,7 @@ impl Drop for Condvar { #[cfg(test)] mod tests { + /// #![feature(wait_until)] use sync::mpsc::channel; use sync::{Condvar, Mutex, Arc}; use sync::atomic::{AtomicBool, Ordering}; @@ -548,6 +690,29 @@ mod tests { } } + #[test] + #[cfg_attr(target_os = "emscripten", ignore)] + fn wait_until() { + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair2 = pair.clone(); + + // Inside of our lock, spawn a new thread, and then wait for it to start. + thread::spawn(move|| { + let &(ref lock, ref cvar) = &*pair2; + let mut started = lock.lock().unwrap(); + *started = true; + // We notify the condvar that the value has changed. + cvar.notify_one(); + }); + + // Wait for the thread to start up. + let &(ref lock, ref cvar) = &*pair; + let guard = cvar.wait_until(lock.lock().unwrap(), |started| { + *started + }); + assert!(*guard.unwrap()); + } + #[test] #[cfg_attr(target_os = "emscripten", ignore)] fn wait_timeout_wait() { @@ -567,6 +732,53 @@ mod tests { } } + #[test] + #[cfg_attr(target_os = "emscripten", ignore)] + fn wait_timeout_until_wait() { + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + let g = m.lock().unwrap(); + let (_g, wait) = c.wait_timeout_until(g, Duration::from_millis(1), |_| { false }).unwrap(); + // no spurious wakeups. ensure it timed-out + assert!(wait.timed_out()); + } + + #[test] + #[cfg_attr(target_os = "emscripten", ignore)] + fn wait_timeout_until_instant_satisfy() { + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + let g = m.lock().unwrap(); + let (_g, wait) = c.wait_timeout_until(g, Duration::from_millis(0), |_| { true }).unwrap(); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); + } + + #[test] + #[cfg_attr(target_os = "emscripten", ignore)] + fn wait_timeout_until_wake() { + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair_copy = pair.clone(); + + let &(ref m, ref c) = &*pair; + let g = m.lock().unwrap(); + let _t = thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair_copy; + let mut started = lock.lock().unwrap(); + thread::sleep(Duration::from_millis(1)); + *started = true; + cvar.notify_one(); + }); + let (g2, wait) = c.wait_timeout_until(g, Duration::from_millis(u64::MAX), |&mut notified| { + notified + }).unwrap(); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); + assert!(*g2); + } + #[test] #[cfg_attr(target_os = "emscripten", ignore)] fn wait_timeout_wake() { diff --git a/src/libstd/sys/redox/net/tcp.rs b/src/libstd/sys/redox/net/tcp.rs index 319965ab3965e..b5664908479cf 100644 --- a/src/libstd/sys/redox/net/tcp.rs +++ b/src/libstd/sys/redox/net/tcp.rs @@ -9,7 +9,7 @@ // except according to those terms. use cmp; -use io::{Error, ErrorKind, Result}; +use io::{self, Error, ErrorKind, Result}; use mem; use net::{SocketAddr, Shutdown}; use path::Path; @@ -130,6 +130,10 @@ impl TcpStream { pub fn set_read_timeout(&self, duration_option: Option) -> Result<()> { let file = self.0.dup(b"read_timeout")?; if let Some(duration) = duration_option { + if duration.as_secs() == 0 && duration.subsec_nanos() == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } file.write(&TimeSpec { tv_sec: duration.as_secs() as i64, tv_nsec: duration.subsec_nanos() as i32 @@ -143,6 +147,10 @@ impl TcpStream { pub fn set_write_timeout(&self, duration_option: Option) -> Result<()> { let file = self.0.dup(b"write_timeout")?; if let Some(duration) = duration_option { + if duration.as_secs() == 0 && duration.subsec_nanos() == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } file.write(&TimeSpec { tv_sec: duration.as_secs() as i64, tv_nsec: duration.subsec_nanos() as i32 diff --git a/src/libstd/sys/redox/net/udp.rs b/src/libstd/sys/redox/net/udp.rs index 7e7666e7ef364..2ed67bd2836f2 100644 --- a/src/libstd/sys/redox/net/udp.rs +++ b/src/libstd/sys/redox/net/udp.rs @@ -10,7 +10,7 @@ use cell::UnsafeCell; use cmp; -use io::{Error, ErrorKind, Result}; +use io::{self, Error, ErrorKind, Result}; use mem; use net::{SocketAddr, Ipv4Addr, Ipv6Addr}; use path::Path; @@ -179,6 +179,10 @@ impl UdpSocket { pub fn set_read_timeout(&self, duration_option: Option) -> Result<()> { let file = self.0.dup(b"read_timeout")?; if let Some(duration) = duration_option { + if duration.as_secs() == 0 && duration.subsec_nanos() == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } file.write(&TimeSpec { tv_sec: duration.as_secs() as i64, tv_nsec: duration.subsec_nanos() as i32 @@ -192,6 +196,10 @@ impl UdpSocket { pub fn set_write_timeout(&self, duration_option: Option) -> Result<()> { let file = self.0.dup(b"write_timeout")?; if let Some(duration) = duration_option { + if duration.as_secs() == 0 && duration.subsec_nanos() == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } file.write(&TimeSpec { tv_sec: duration.as_secs() as i64, tv_nsec: duration.subsec_nanos() as i32 diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs index 31bdc5ea1f565..ab4aef1a58234 100644 --- a/src/libstd/sys/unix/ext/net.rs +++ b/src/libstd/sys/unix/ext/net.rs @@ -1410,7 +1410,7 @@ impl IntoRawFd for UnixDatagram { #[cfg(all(test, not(target_os = "emscripten")))] mod test { use thread; - use io; + use io::{self, ErrorKind}; use io::prelude::*; use time::Duration; use sys_common::io::test::tmpdir; @@ -1613,6 +1613,27 @@ mod test { assert!(kind == io::ErrorKind::WouldBlock || kind == io::ErrorKind::TimedOut); } + // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors + // when passed zero Durations + #[test] + fn test_unix_stream_timeout_zero_duration() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + + let listener = or_panic!(UnixListener::bind(&socket_path)); + let stream = or_panic!(UnixStream::connect(&socket_path)); + + let result = stream.set_write_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + let result = stream.set_read_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + drop(listener); + } + #[test] fn test_unix_datagram() { let dir = tmpdir(); @@ -1712,6 +1733,24 @@ mod test { thread.join().unwrap(); } + // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors + // when passed zero Durations + #[test] + fn test_unix_datagram_timeout_zero_duration() { + let dir = tmpdir(); + let path = dir.path().join("sock"); + + let datagram = or_panic!(UnixDatagram::bind(&path)); + + let result = datagram.set_write_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + let result = datagram.set_read_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + } + #[test] fn abstract_namespace_not_allowed() { assert!(UnixStream::connect("\0asdf").is_err()); diff --git a/src/libstd_unicode/char.rs b/src/libstd_unicode/char.rs index b4be4a9691183..844ff7a3c1252 100644 --- a/src/libstd_unicode/char.rs +++ b/src/libstd_unicode/char.rs @@ -1332,7 +1332,7 @@ impl char { } /// Checks if the value is an ASCII graphic character: - /// U+0021 '@' ... U+007E '~'. + /// U+0021 '!' ... U+007E '~'. /// /// # Examples /// diff --git a/src/test/run-make/atomic-lock-free/Makefile b/src/test/run-make/atomic-lock-free/Makefile index 4849b0307423d..63b8608afc763 100644 --- a/src/test/run-make/atomic-lock-free/Makefile +++ b/src/test/run-make/atomic-lock-free/Makefile @@ -36,6 +36,8 @@ ifeq ($(filter powerpc,$(LLVM_COMPONENTS)),powerpc) nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add $(RUSTC) --target=powerpc64le-unknown-linux-gnu atomic_lock_free.rs nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add +endif +ifeq ($(filter systemz,$(LLVM_COMPONENTS)),systemz) $(RUSTC) --target=s390x-unknown-linux-gnu atomic_lock_free.rs nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add endif diff --git a/src/test/compile-fail/macro-at-most-once-rep-ambig.rs b/src/test/ui/macros/macro-at-most-once-rep-ambig.rs similarity index 100% rename from src/test/compile-fail/macro-at-most-once-rep-ambig.rs rename to src/test/ui/macros/macro-at-most-once-rep-ambig.rs diff --git a/src/test/ui/macros/macro-at-most-once-rep-ambig.stderr b/src/test/ui/macros/macro-at-most-once-rep-ambig.stderr new file mode 100644 index 0000000000000..67a77e0a481d8 --- /dev/null +++ b/src/test/ui/macros/macro-at-most-once-rep-ambig.stderr @@ -0,0 +1,80 @@ +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:40:11 + | +40 | foo!(a?a?a); //~ ERROR no rules expected the token `?` + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:41:11 + | +41 | foo!(a?a); //~ ERROR no rules expected the token `?` + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:42:11 + | +42 | foo!(a?); //~ ERROR no rules expected the token `?` + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:43:11 + | +43 | baz!(a?a?a); //~ ERROR no rules expected the token `?` + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:44:11 + | +44 | baz!(a?a); //~ ERROR no rules expected the token `?` + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:45:11 + | +45 | baz!(a?); //~ ERROR no rules expected the token `?` + | ^ + +error: unexpected end of macro invocation + --> $DIR/macro-at-most-once-rep-ambig.rs:46:11 + | +46 | baz!(a,); //~ ERROR unexpected end of macro invocation + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:47:11 + | +47 | baz!(a?a?a,); //~ ERROR no rules expected the token `?` + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:48:11 + | +48 | baz!(a?a,); //~ ERROR no rules expected the token `?` + | ^ + +error: no rules expected the token `?` + --> $DIR/macro-at-most-once-rep-ambig.rs:49:11 + | +49 | baz!(a?,); //~ ERROR no rules expected the token `?` + | ^ + +error: unexpected end of macro invocation + --> $DIR/macro-at-most-once-rep-ambig.rs:50:5 + | +50 | barplus!(); //~ ERROR unexpected end of macro invocation + | ^^^^^^^^^^^ + +error: unexpected end of macro invocation + --> $DIR/macro-at-most-once-rep-ambig.rs:51:15 + | +51 | barplus!(a?); //~ ERROR unexpected end of macro invocation + | ^ + +error: unexpected end of macro invocation + --> $DIR/macro-at-most-once-rep-ambig.rs:52:15 + | +52 | barstar!(a?); //~ ERROR unexpected end of macro invocation + | ^ + +error: aborting due to 13 previous errors + diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 4113f8fd124c7..0845179532224 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -86,6 +86,7 @@ static TARGETS: &'static [&'static str] = &[ "powerpc64-unknown-linux-gnu", "powerpc64le-unknown-linux-gnu", "s390x-unknown-linux-gnu", + "sparc-unknown-linux-gnu", "sparc64-unknown-linux-gnu", "sparcv9-sun-solaris", "wasm32-unknown-emscripten",