Auto merge of #48531 - kennytm:rollup, r=kennytm
Rollup of 17 pull requests - Successful merges: #47964, #47970, #48076, #48115, #48166, #48281, #48297, #48302, #48362, #48369, #48489, #48491, #48494, #48517, #48529, #48235, #48330 - Failed merges:
This commit is contained in:
commit
322d7f7b97
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -444,10 +444,11 @@ impl<'a> Builder<'a> {
|
|||
|
||||
fn run(self, builder: &Builder) -> Interned<PathBuf> {
|
||||
let compiler = self.compiler;
|
||||
let lib = if compiler.stage >= 1 && builder.build.config.libdir.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)));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<PathBuf>,
|
||||
pub rustc_error_format: Option<String>,
|
||||
|
||||
pub run_host_only: bool,
|
||||
|
||||
|
@ -330,6 +331,7 @@ impl Config {
|
|||
config.test_miri = false;
|
||||
config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")];
|
||||
|
||||
config.rustc_error_format = flags.rustc_error_format;
|
||||
config.on_fail = flags.on_fail;
|
||||
config.stage = flags.stage;
|
||||
config.src = flags.src;
|
||||
|
@ -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
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ pub struct Flags {
|
|||
pub cmd: Subcommand,
|
||||
pub incremental: bool,
|
||||
pub exclude: Vec<PathBuf>,
|
||||
pub rustc_error_format: Option<String>,
|
||||
}
|
||||
|
||||
pub enum Subcommand {
|
||||
|
@ -118,6 +119,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
|
|||
opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
|
||||
opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
|
||||
opts.optflag("h", "help", "print this help message");
|
||||
opts.optflag("", "error-format", "rustc error format");
|
||||
|
||||
// fn usage()
|
||||
let usage = |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
|
||||
|
@ -370,6 +372,7 @@ Arguments:
|
|||
verbose: matches.opt_count("verbose"),
|
||||
stage,
|
||||
on_fail: matches.opt_str("on-fail"),
|
||||
rustc_error_format: matches.opt_str("error-format"),
|
||||
keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
|
||||
build: matches.opt_str("build").map(|s| INTERNER.intern_string(s)),
|
||||
host: split(matches.opt_strs("host"))
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 });
|
||||
},
|
||||
};
|
||||
# }
|
||||
```
|
|
@ -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<F>(self, mut f: F) -> Self
|
||||
where F: FnMut(&mut V)
|
||||
{
|
||||
|
|
|
@ -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<U, F>(self, f: F) -> FlatMap<Self, U, F>
|
||||
where Self: Sized, U: IntoIterator, F: FnMut(Self::Item) -> U,
|
||||
{
|
||||
FlatMap{iter: self, f: f, frontiter: None, backiter: None }
|
||||
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::<Vec<u8>>();
|
||||
/// assert_eq!(flattened, &[1, 2, 3, 4, 5, 6]);
|
||||
/// ```
|
||||
///
|
||||
/// Mapping and then flattening:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(iterator_flatten)]
|
||||
///
|
||||
/// let words = ["alpha", "beta", "gamma"];
|
||||
///
|
||||
/// // chars() returns an iterator
|
||||
/// let merged: String = words.iter()
|
||||
/// .map(|s| s.chars())
|
||||
/// .flatten()
|
||||
/// .collect();
|
||||
/// assert_eq!(merged, "alphabetagamma");
|
||||
/// ```
|
||||
///
|
||||
/// 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::<Vec<_>>();
|
||||
/// assert_eq!(d2, [&[1, 2], &[3, 4], &[5, 6], &[7, 8]]);
|
||||
///
|
||||
/// let d1 = d3.iter().flatten().flatten().collect::<Vec<_>>();
|
||||
/// assert_eq!(d1, [&1, &2, &3, &4, &5, &6, &7, &8]);
|
||||
/// ```
|
||||
///
|
||||
/// Here we see that `flatten()` does not perform a "deep" flatten.
|
||||
/// Instead, only one level of nesting is removed. That is, if you
|
||||
/// `flatten()` a three-dimensional array the result will be
|
||||
/// two-dimensional and not one-dimensional. To get a one-dimensional
|
||||
/// structure, you have to `flatten()` again.
|
||||
#[inline]
|
||||
#[unstable(feature = "iterator_flatten", issue = "48213")]
|
||||
fn flatten(self) -> Flatten<Self>
|
||||
where Self: Sized, Self::Item: IntoIterator {
|
||||
Flatten { inner: flatten_compat(self) }
|
||||
}
|
||||
|
||||
/// Creates an iterator which ends after the first [`None`].
|
||||
|
|
|
@ -2410,12 +2410,15 @@ impl<B, I, St, F> Iterator for Scan<I, St, F> 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<I, U: IntoIterator, F> {
|
||||
iter: I,
|
||||
f: F,
|
||||
frontiter: Option<U::IntoIter>,
|
||||
backiter: Option<U::IntoIter>,
|
||||
inner: FlattenCompat<Map<I, F>, <U as IntoIterator>::IntoIter>
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<I: Clone, U: Clone + IntoIterator, F: Clone> Clone for FlatMap<I, U, F>
|
||||
where <U as IntoIterator>::IntoIter: Clone
|
||||
{
|
||||
fn clone(&self) -> Self { FlatMap { inner: self.inner.clone() } }
|
||||
}
|
||||
|
||||
#[stable(feature = "core_impl_debug", since = "1.9.0")]
|
||||
|
@ -2423,11 +2426,7 @@ impl<I: fmt::Debug, U: IntoIterator, F> fmt::Debug for FlatMap<I, U, F>
|
|||
where U::IntoIter: fmt::Debug
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("FlatMap")
|
||||
.field("iter", &self.iter)
|
||||
.field("frontiter", &self.frontiter)
|
||||
.field("backiter", &self.backiter)
|
||||
.finish()
|
||||
f.debug_struct("FlatMap").field("inner", &self.inner).finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2437,17 +2436,173 @@ impl<I: Iterator, U: IntoIterator, F> Iterator for FlatMap<I, U, F>
|
|||
{
|
||||
type Item = U::Item;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<U::Item> { self.inner.next() }
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() }
|
||||
|
||||
#[inline]
|
||||
fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R where
|
||||
Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try<Ok=Acc>
|
||||
{
|
||||
self.inner.try_fold(init, fold)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
|
||||
where Fold: FnMut(Acc, Self::Item) -> Acc,
|
||||
{
|
||||
self.inner.fold(init, fold)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<I: DoubleEndedIterator, U, F> DoubleEndedIterator for FlatMap<I, U, F>
|
||||
where F: FnMut(I::Item) -> U,
|
||||
U: IntoIterator,
|
||||
U::IntoIter: DoubleEndedIterator
|
||||
{
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<U::Item> { self.inner.next_back() }
|
||||
|
||||
#[inline]
|
||||
fn try_rfold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R where
|
||||
Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try<Ok=Acc>
|
||||
{
|
||||
self.inner.try_rfold(init, fold)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rfold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
|
||||
where Fold: FnMut(Acc, Self::Item) -> Acc,
|
||||
{
|
||||
self.inner.rfold(init, fold)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "fused", issue = "35602")]
|
||||
impl<I, U, F> FusedIterator for FlatMap<I, U, F>
|
||||
where I: FusedIterator, U: IntoIterator, F: FnMut(I::Item) -> U {}
|
||||
|
||||
/// An iterator that flattens one level of nesting in an iterator of things
|
||||
/// that can be turned into iterators.
|
||||
///
|
||||
/// 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<I: Iterator>
|
||||
where I::Item: IntoIterator {
|
||||
inner: FlattenCompat<I, <I::Item as IntoIterator>::IntoIter>,
|
||||
}
|
||||
|
||||
#[unstable(feature = "iterator_flatten", issue = "48213")]
|
||||
impl<I, U> fmt::Debug for Flatten<I>
|
||||
where I: Iterator + fmt::Debug, U: Iterator + fmt::Debug,
|
||||
I::Item: IntoIterator<IntoIter = U, Item = U::Item>,
|
||||
{
|
||||
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<I, U> Clone for Flatten<I>
|
||||
where I: Iterator + Clone, U: Iterator + Clone,
|
||||
I::Item: IntoIterator<IntoIter = U, Item = U::Item>,
|
||||
{
|
||||
fn clone(&self) -> Self { Flatten { inner: self.inner.clone() } }
|
||||
}
|
||||
|
||||
#[unstable(feature = "iterator_flatten", issue = "48213")]
|
||||
impl<I, U> Iterator for Flatten<I>
|
||||
where I: Iterator, U: Iterator,
|
||||
I::Item: IntoIterator<IntoIter = U, Item = U::Item>
|
||||
{
|
||||
type Item = U::Item;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<U::Item> { self.inner.next() }
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() }
|
||||
|
||||
#[inline]
|
||||
fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R where
|
||||
Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try<Ok=Acc>
|
||||
{
|
||||
self.inner.try_fold(init, fold)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
|
||||
where Fold: FnMut(Acc, Self::Item) -> Acc,
|
||||
{
|
||||
self.inner.fold(init, fold)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "iterator_flatten", issue = "48213")]
|
||||
impl<I, U> DoubleEndedIterator for Flatten<I>
|
||||
where I: DoubleEndedIterator, U: DoubleEndedIterator,
|
||||
I::Item: IntoIterator<IntoIter = U, Item = U::Item>
|
||||
{
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<U::Item> { self.inner.next_back() }
|
||||
|
||||
#[inline]
|
||||
fn try_rfold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R where
|
||||
Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try<Ok=Acc>
|
||||
{
|
||||
self.inner.try_rfold(init, fold)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rfold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
|
||||
where Fold: FnMut(Acc, Self::Item) -> Acc,
|
||||
{
|
||||
self.inner.rfold(init, fold)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "fused", issue = "35602")]
|
||||
impl<I, U> FusedIterator for Flatten<I>
|
||||
where I: FusedIterator, U: Iterator,
|
||||
I::Item: IntoIterator<IntoIter = U, Item = U::Item> {}
|
||||
|
||||
/// Adapts an iterator by flattening it, for use in `flatten()` and `flat_map()`.
|
||||
fn flatten_compat<I, U>(iter: I) -> FlattenCompat<I, U> {
|
||||
FlattenCompat { iter, frontiter: None, backiter: None }
|
||||
}
|
||||
|
||||
/// Real logic of both `Flatten` and `FlatMap` which simply delegate to
|
||||
/// this type.
|
||||
#[derive(Clone, Debug)]
|
||||
struct FlattenCompat<I, U> {
|
||||
iter: I,
|
||||
frontiter: Option<U>,
|
||||
backiter: Option<U>,
|
||||
}
|
||||
|
||||
impl<I, U> Iterator for FlattenCompat<I, U>
|
||||
where I: Iterator, U: Iterator,
|
||||
I::Item: IntoIterator<IntoIter = U, Item = U::Item>
|
||||
{
|
||||
type Item = U::Item;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<U::Item> {
|
||||
loop {
|
||||
if let Some(ref mut inner) = self.frontiter {
|
||||
if let Some(x) = inner.by_ref().next() {
|
||||
return Some(x)
|
||||
}
|
||||
if let elt@Some(_) = inner.next() { return elt }
|
||||
}
|
||||
match self.iter.next().map(&mut self.f) {
|
||||
match self.iter.next() {
|
||||
None => return self.backiter.as_mut().and_then(|it| it.next()),
|
||||
next => self.frontiter = next.map(IntoIterator::into_iter),
|
||||
Some(inner) => self.frontiter = Some(inner.into_iter()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2473,10 +2628,9 @@ impl<I: Iterator, U: IntoIterator, F> Iterator for FlatMap<I, U, F>
|
|||
self.frontiter = None;
|
||||
|
||||
{
|
||||
let f = &mut self.f;
|
||||
let frontiter = &mut self.frontiter;
|
||||
init = self.iter.try_fold(init, |acc, x| {
|
||||
let mut mid = f(x).into_iter();
|
||||
let mut mid = x.into_iter();
|
||||
let r = mid.try_fold(acc, &mut fold);
|
||||
*frontiter = Some(mid);
|
||||
r
|
||||
|
@ -2497,27 +2651,23 @@ impl<I: Iterator, U: IntoIterator, F> Iterator for FlatMap<I, U, F>
|
|||
where Fold: FnMut(Acc, Self::Item) -> Acc,
|
||||
{
|
||||
self.frontiter.into_iter()
|
||||
.chain(self.iter.map(self.f).map(U::into_iter))
|
||||
.chain(self.iter.map(IntoIterator::into_iter))
|
||||
.chain(self.backiter)
|
||||
.fold(init, |acc, iter| iter.fold(acc, &mut fold))
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<I: DoubleEndedIterator, U, F> DoubleEndedIterator for FlatMap<I, U, F> where
|
||||
F: FnMut(I::Item) -> U,
|
||||
U: IntoIterator,
|
||||
U::IntoIter: DoubleEndedIterator
|
||||
impl<I, U> DoubleEndedIterator for FlattenCompat<I, U>
|
||||
where I: DoubleEndedIterator, U: DoubleEndedIterator,
|
||||
I::Item: IntoIterator<IntoIter = U, Item = U::Item>
|
||||
{
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<U::Item> {
|
||||
loop {
|
||||
if let Some(ref mut inner) = self.backiter {
|
||||
if let Some(y) = inner.next_back() {
|
||||
return Some(y)
|
||||
}
|
||||
if let elt@Some(_) = inner.next_back() { return elt }
|
||||
}
|
||||
match self.iter.next_back().map(&mut self.f) {
|
||||
match self.iter.next_back() {
|
||||
None => return self.frontiter.as_mut().and_then(|it| it.next_back()),
|
||||
next => self.backiter = next.map(IntoIterator::into_iter),
|
||||
}
|
||||
|
@ -2534,10 +2684,9 @@ impl<I: DoubleEndedIterator, U, F> DoubleEndedIterator for FlatMap<I, U, F> wher
|
|||
self.backiter = None;
|
||||
|
||||
{
|
||||
let f = &mut self.f;
|
||||
let backiter = &mut self.backiter;
|
||||
init = self.iter.try_rfold(init, |acc, x| {
|
||||
let mut mid = f(x).into_iter();
|
||||
let mut mid = x.into_iter();
|
||||
let r = mid.try_rfold(acc, &mut fold);
|
||||
*backiter = Some(mid);
|
||||
r
|
||||
|
@ -2558,16 +2707,12 @@ impl<I: DoubleEndedIterator, U, F> DoubleEndedIterator for FlatMap<I, U, F> wher
|
|||
where Fold: FnMut(Acc, Self::Item) -> Acc,
|
||||
{
|
||||
self.frontiter.into_iter()
|
||||
.chain(self.iter.map(self.f).map(U::into_iter))
|
||||
.chain(self.iter.map(IntoIterator::into_iter))
|
||||
.chain(self.backiter)
|
||||
.rfold(init, |acc, iter| iter.rfold(acc, &mut fold))
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "fused", issue = "35602")]
|
||||
impl<I, U, F> FusedIterator for FlatMap<I, U, F>
|
||||
where I: FusedIterator, U: IntoIterator, F: FnMut(I::Item) -> U {}
|
||||
|
||||
/// An iterator that yields `None` forever after the underlying iterator
|
||||
/// yields `None` once.
|
||||
///
|
||||
|
|
|
@ -93,6 +93,7 @@
|
|||
#![feature(doc_spotlight)]
|
||||
#![feature(rustc_const_unstable)]
|
||||
#![feature(iterator_repeat_with)]
|
||||
#![feature(iterator_flatten)]
|
||||
|
||||
#[prelude_import]
|
||||
#[allow(unused)]
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -2892,7 +2892,7 @@ impl u8 {
|
|||
}
|
||||
|
||||
/// Checks if the value is an ASCII graphic character:
|
||||
/// U+0021 '@' ... U+007E '~'.
|
||||
/// U+0021 '!' ... U+007E '~'.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
|
@ -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::<Vec<_>>(), [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<T>(x: T) -> T { x }
|
||||
assert_eq!((0..10).map(identity).sum::<usize>(), (0..10).sum());
|
||||
|
||||
// composition:
|
||||
fn f(x: usize) -> usize { x + 3 }
|
||||
fn g(x: usize) -> usize { x * 2 }
|
||||
fn h(x: usize) -> usize { g(f(x)) }
|
||||
assert_eq!((0..10).map(f).map(g).sum::<usize>(), (0..10).map(h).sum());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_monad_laws_left_identity() {
|
||||
fn f(x: usize) -> impl Iterator<Item = usize> {
|
||||
(0..10).map(move |y| x * y)
|
||||
}
|
||||
assert_eq!(once(42).flat_map(f.clone()).sum::<usize>(), f(42).sum());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_monad_laws_right_identity() {
|
||||
assert_eq!((0..10).flat_map(|x| once(x)).sum::<usize>(), (0..10).sum());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_monad_laws_associativity() {
|
||||
fn f(x: usize) -> impl Iterator<Item = usize> { 0..x }
|
||||
fn g(x: usize) -> impl Iterator<Item = usize> { (0..x).rev() }
|
||||
assert_eq!((0..10).flat_map(f).flat_map(g).sum::<usize>(),
|
||||
(0..10).flat_map(|x| f(x).flat_map(g)).sum::<usize>());
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -101,6 +101,12 @@ fn lonely_dot() {
|
|||
assert!(".".parse::<f64>().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exponentiated_dot() {
|
||||
assert!(".e0".parse::<f32>().is_err());
|
||||
assert!(".e0".parse::<f64>().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lonely_sign() {
|
||||
assert!("+".parse::<f32>().is_err());
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use LinkerFlavor;
|
||||
use target::{Target, TargetResult};
|
||||
|
||||
pub fn target() -> TargetResult {
|
||||
let mut base = super::netbsd_base::opts();
|
||||
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
|
||||
base.max_atomic_width = Some(32);
|
||||
|
||||
// see #36994
|
||||
base.exe_allocation_crate = None;
|
||||
|
||||
Ok(Target {
|
||||
llvm_target: "powerpc-unknown-netbsd".to_string(),
|
||||
target_endian: "big".to_string(),
|
||||
target_pointer_width: "32".to_string(),
|
||||
target_c_int_width: "32".to_string(),
|
||||
data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(),
|
||||
arch: "powerpc".to_string(),
|
||||
target_os: "netbsd".to_string(),
|
||||
target_env: "".to_string(),
|
||||
target_vendor: "unknown".to_string(),
|
||||
linker_flavor: LinkerFlavor::Gcc,
|
||||
options: base,
|
||||
})
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use LinkerFlavor;
|
||||
use target::{Target, TargetResult};
|
||||
|
||||
pub fn target() -> TargetResult {
|
||||
let mut base = super::linux_base::opts();
|
||||
base.cpu = "v9".to_string();
|
||||
base.max_atomic_width = Some(64);
|
||||
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mv8plus".to_string());
|
||||
base.exe_allocation_crate = None;
|
||||
|
||||
Ok(Target {
|
||||
llvm_target: "sparc-unknown-linux-gnu".to_string(),
|
||||
target_endian: "big".to_string(),
|
||||
target_pointer_width: "32".to_string(),
|
||||
target_c_int_width: "32".to_string(),
|
||||
data_layout: "E-m:e-p:32:32-i64:64-f128:64-n32-S64".to_string(),
|
||||
arch: "sparc".to_string(),
|
||||
target_os: "linux".to_string(),
|
||||
target_env: "gnu".to_string(),
|
||||
target_vendor: "unknown".to_string(),
|
||||
linker_flavor: LinkerFlavor::Gcc,
|
||||
options: base,
|
||||
})
|
||||
}
|
|
@ -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<RegKind>; 8],
|
||||
pub prefix_chunk: Size,
|
||||
pub rest: Uniform,
|
||||
}
|
||||
|
||||
impl From<Reg> for CastTarget {
|
||||
fn from(unit: Reg) -> CastTarget {
|
||||
CastTarget::Uniform(Uniform::from(unit))
|
||||
CastTarget::from(Uniform::from(unit))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Uniform> for CastTarget {
|
||||
fn from(uniform: Uniform) -> CastTarget {
|
||||
CastTarget::Uniform(uniform)
|
||||
CastTarget {
|
||||
prefix: [None; 8],
|
||||
prefix_chunk: Size::from_bytes(0),
|
||||
rest: uniform
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CastTarget {
|
||||
pub fn size(&self, cx: &CodegenCx) -> Size {
|
||||
match *self {
|
||||
CastTarget::Uniform(u) => u.total,
|
||||
CastTarget::Pair(a, b) => {
|
||||
(a.size.abi_align(a.align(cx)) + b.size)
|
||||
.abi_align(self.align(cx))
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -109,6 +109,10 @@ impl Command {
|
|||
|
||||
// extensions
|
||||
|
||||
pub fn get_args(&self) -> &[OsString] {
|
||||
&self.args
|
||||
}
|
||||
|
||||
pub fn take_args(&mut self) -> Vec<OsString> {
|
||||
mem::replace(&mut self.args, Vec::new())
|
||||
}
|
||||
|
|
|
@ -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 crate_type == config::CrateTypeExecutable {
|
||||
let mut position_independent_executable = false;
|
||||
|
||||
if get_reloc_model(sess) == llvm::RelocMode::PIC
|
||||
&& !sess.crt_static() && !args.any(|x| *x == "-static") {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn classify_ret_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
|
||||
ret: &mut ArgType<'tcx>,
|
||||
offset: &mut Size) {
|
||||
if !ret.layout.is_aggregate() {
|
||||
ret.extend_integer_width_to(64);
|
||||
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 {
|
||||
ret.make_indirect();
|
||||
*offset += cx.tcx.data_layout.pointer_size;
|
||||
Reg::i64()
|
||||
}
|
||||
}
|
||||
|
||||
fn classify_arg_ty(cx: &CodegenCx, arg: &mut ArgType, offset: &mut Size) {
|
||||
let dl = &cx.tcx.data_layout;
|
||||
let size = arg.layout.size;
|
||||
let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align);
|
||||
fn float_reg<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ret: &ArgType<'tcx>, i: usize) -> Option<Reg> {
|
||||
match ret.layout.field(cx, i).abi {
|
||||
layout::Abi::Scalar(ref scalar) => match scalar.value {
|
||||
layout::F32 => Some(Reg::f32()),
|
||||
layout::F64 => Some(Reg::f64()),
|
||||
_ => None
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
} else {
|
||||
arg.extend_integer_width_to(64);
|
||||
fn classify_ret_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
|
||||
if !ret.layout.is_aggregate() {
|
||||
extend_integer_width_mips(ret, 64);
|
||||
return;
|
||||
}
|
||||
|
||||
*offset = offset.abi_align(align) + size.abi_align(align);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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 mut prefix = [None; 8];
|
||||
let mut prefix_index = 0;
|
||||
|
||||
match arg.layout.fields {
|
||||
layout::FieldPlacement::Array { .. } => {
|
||||
// Arrays are passed indirectly
|
||||
arg.make_indirect();
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -171,7 +171,7 @@ fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
|
|||
let mut target = CastTarget::from(lo);
|
||||
if size > offset {
|
||||
if let Some(hi) = reg_component(cls, &mut i, size - offset) {
|
||||
target = CastTarget::Pair(lo, hi);
|
||||
target = CastTarget::pair(lo, hi);
|
||||
}
|
||||
}
|
||||
assert_eq!(reg_component(cls, &mut i, Size::from_bytes(0)), None);
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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<F>(self, mut f: F) -> Self
|
||||
where F: FnMut(&mut V)
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<MutexGuard<'a, T>>
|
||||
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() {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
// except according to those terms.
|
||||
|
||||
use cmp;
|
||||
use io::{Error, ErrorKind, Result};
|
||||
use io::{self, Error, ErrorKind, Result};
|
||||
use mem;
|
||||
use net::{SocketAddr, Shutdown};
|
||||
use path::Path;
|
||||
|
@ -130,6 +130,10 @@ impl TcpStream {
|
|||
pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
|
||||
let file = self.0.dup(b"read_timeout")?;
|
||||
if let Some(duration) = duration_option {
|
||||
if duration.as_secs() == 0 && duration.subsec_nanos() == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"cannot set a 0 duration timeout"));
|
||||
}
|
||||
file.write(&TimeSpec {
|
||||
tv_sec: duration.as_secs() as i64,
|
||||
tv_nsec: duration.subsec_nanos() as i32
|
||||
|
@ -143,6 +147,10 @@ impl TcpStream {
|
|||
pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
|
||||
let file = self.0.dup(b"write_timeout")?;
|
||||
if let Some(duration) = duration_option {
|
||||
if duration.as_secs() == 0 && duration.subsec_nanos() == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"cannot set a 0 duration timeout"));
|
||||
}
|
||||
file.write(&TimeSpec {
|
||||
tv_sec: duration.as_secs() as i64,
|
||||
tv_nsec: duration.subsec_nanos() as i32
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
use cell::UnsafeCell;
|
||||
use cmp;
|
||||
use io::{Error, ErrorKind, Result};
|
||||
use io::{self, Error, ErrorKind, Result};
|
||||
use mem;
|
||||
use net::{SocketAddr, Ipv4Addr, Ipv6Addr};
|
||||
use path::Path;
|
||||
|
@ -179,6 +179,10 @@ impl UdpSocket {
|
|||
pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
|
||||
let file = self.0.dup(b"read_timeout")?;
|
||||
if let Some(duration) = duration_option {
|
||||
if duration.as_secs() == 0 && duration.subsec_nanos() == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"cannot set a 0 duration timeout"));
|
||||
}
|
||||
file.write(&TimeSpec {
|
||||
tv_sec: duration.as_secs() as i64,
|
||||
tv_nsec: duration.subsec_nanos() as i32
|
||||
|
@ -192,6 +196,10 @@ impl UdpSocket {
|
|||
pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
|
||||
let file = self.0.dup(b"write_timeout")?;
|
||||
if let Some(duration) = duration_option {
|
||||
if duration.as_secs() == 0 && duration.subsec_nanos() == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"cannot set a 0 duration timeout"));
|
||||
}
|
||||
file.write(&TimeSpec {
|
||||
tv_sec: duration.as_secs() as i64,
|
||||
tv_nsec: duration.subsec_nanos() as i32
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -1332,7 +1332,7 @@ impl char {
|
|||
}
|
||||
|
||||
/// Checks if the value is an ASCII graphic character:
|
||||
/// U+0021 '@' ... U+007E '~'.
|
||||
/// U+0021 '!' ... U+007E '~'.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue