mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-07 20:58:39 +00:00
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);
|
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
|
// Detect whether or not we're a build script depending on whether --target
|
||||||
// is passed (a bit janky...)
|
// is passed (a bit janky...)
|
||||||
let target = args.windows(2)
|
let target = args.windows(2)
|
||||||
|
@ -294,7 +294,7 @@ def default_build_triple():
|
|||||||
raise ValueError('unknown byteorder: {}'.format(sys.byteorder))
|
raise ValueError('unknown byteorder: {}'.format(sys.byteorder))
|
||||||
# only the n64 ABI is supported, indicate it
|
# only the n64 ABI is supported, indicate it
|
||||||
ostype += 'abi64'
|
ostype += 'abi64'
|
||||||
elif cputype == 'sparcv9' or cputype == 'sparc64':
|
elif cputype == 'sparc' or cputype == 'sparcv9' or cputype == 'sparc64':
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
err = "unknown cpu type: {}".format(cputype)
|
err = "unknown cpu type: {}".format(cputype)
|
||||||
|
@ -444,10 +444,11 @@ impl<'a> Builder<'a> {
|
|||||||
|
|
||||||
fn run(self, builder: &Builder) -> Interned<PathBuf> {
|
fn run(self, builder: &Builder) -> Interned<PathBuf> {
|
||||||
let compiler = self.compiler;
|
let compiler = self.compiler;
|
||||||
let lib = if compiler.stage >= 1 && builder.build.config.libdir.is_some() {
|
let config = &builder.build.config;
|
||||||
builder.build.config.libdir.clone().unwrap()
|
let lib = if compiler.stage >= 1 && config.libdir_relative().is_some() {
|
||||||
|
builder.build.config.libdir_relative().unwrap()
|
||||||
} else {
|
} else {
|
||||||
PathBuf::from("lib")
|
Path::new("lib")
|
||||||
};
|
};
|
||||||
let sysroot = builder.sysroot(self.compiler).join(lib)
|
let sysroot = builder.sysroot(self.compiler).join(lib)
|
||||||
.join("rustlib").join(self.target).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) {
|
if let Some(target_linker) = self.build.linker(target) {
|
||||||
cargo.env("RUSTC_TARGET_LINKER", target_linker);
|
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" {
|
if cmd != "build" && cmd != "check" {
|
||||||
cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(self.compiler(2, self.build.build)));
|
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_VERSION", build.rust_version())
|
||||||
.env("CFG_PREFIX", build.config.prefix.clone().unwrap_or_default());
|
.env("CFG_PREFIX", build.config.prefix.clone().unwrap_or_default());
|
||||||
|
|
||||||
let libdir_relative =
|
let libdir_relative = build.config.libdir_relative().unwrap_or(Path::new("lib"));
|
||||||
build.config.libdir.clone().unwrap_or(PathBuf::from("lib"));
|
|
||||||
cargo.env("CFG_LIBDIR_RELATIVE", libdir_relative);
|
cargo.env("CFG_LIBDIR_RELATIVE", libdir_relative);
|
||||||
|
|
||||||
// If we're not building a compiler with debugging information then remove
|
// 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::env;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
@ -57,6 +57,7 @@ pub struct Config {
|
|||||||
pub profiler: bool,
|
pub profiler: bool,
|
||||||
pub ignore_git: bool,
|
pub ignore_git: bool,
|
||||||
pub exclude: Vec<PathBuf>,
|
pub exclude: Vec<PathBuf>,
|
||||||
|
pub rustc_error_format: Option<String>,
|
||||||
|
|
||||||
pub run_host_only: bool,
|
pub run_host_only: bool,
|
||||||
|
|
||||||
@ -330,6 +331,7 @@ impl Config {
|
|||||||
config.test_miri = false;
|
config.test_miri = false;
|
||||||
config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")];
|
config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")];
|
||||||
|
|
||||||
|
config.rustc_error_format = flags.rustc_error_format;
|
||||||
config.on_fail = flags.on_fail;
|
config.on_fail = flags.on_fail;
|
||||||
config.stage = flags.stage;
|
config.stage = flags.stage;
|
||||||
config.src = flags.src;
|
config.src = flags.src;
|
||||||
@ -564,6 +566,17 @@ impl Config {
|
|||||||
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 {
|
pub fn verbose(&self) -> bool {
|
||||||
self.verbose > 0
|
self.verbose > 0
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ pub struct Flags {
|
|||||||
pub cmd: Subcommand,
|
pub cmd: Subcommand,
|
||||||
pub incremental: bool,
|
pub incremental: bool,
|
||||||
pub exclude: Vec<PathBuf>,
|
pub exclude: Vec<PathBuf>,
|
||||||
|
pub rustc_error_format: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Subcommand {
|
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("", "src", "path to the root of the rust checkout", "DIR");
|
||||||
opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
|
opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
|
||||||
opts.optflag("h", "help", "print this help message");
|
opts.optflag("h", "help", "print this help message");
|
||||||
|
opts.optflag("", "error-format", "rustc error format");
|
||||||
|
|
||||||
// fn usage()
|
// fn usage()
|
||||||
let usage = |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
|
let usage = |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
|
||||||
@ -370,6 +372,7 @@ Arguments:
|
|||||||
verbose: matches.opt_count("verbose"),
|
verbose: matches.opt_count("verbose"),
|
||||||
stage,
|
stage,
|
||||||
on_fail: matches.opt_str("on-fail"),
|
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()),
|
keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
|
||||||
build: matches.opt_str("build").map(|s| INTERNER.intern_string(s)),
|
build: matches.opt_str("build").map(|s| INTERNER.intern_string(s)),
|
||||||
host: split(matches.opt_strs("host"))
|
host: split(matches.opt_strs("host"))
|
||||||
|
@ -480,9 +480,11 @@ impl Step for Openssl {
|
|||||||
"mips64el-unknown-linux-gnuabi64" => "linux64-mips64",
|
"mips64el-unknown-linux-gnuabi64" => "linux64-mips64",
|
||||||
"mipsel-unknown-linux-gnu" => "linux-mips32",
|
"mipsel-unknown-linux-gnu" => "linux-mips32",
|
||||||
"powerpc-unknown-linux-gnu" => "linux-ppc",
|
"powerpc-unknown-linux-gnu" => "linux-ppc",
|
||||||
|
"powerpc-unknown-netbsd" => "BSD-generic32",
|
||||||
"powerpc64-unknown-linux-gnu" => "linux-ppc64",
|
"powerpc64-unknown-linux-gnu" => "linux-ppc64",
|
||||||
"powerpc64le-unknown-linux-gnu" => "linux-ppc64le",
|
"powerpc64le-unknown-linux-gnu" => "linux-ppc64le",
|
||||||
"s390x-unknown-linux-gnu" => "linux64-s390x",
|
"s390x-unknown-linux-gnu" => "linux64-s390x",
|
||||||
|
"sparc-unknown-linux-gnu" => "linux-sparcv9",
|
||||||
"sparc64-unknown-linux-gnu" => "linux64-sparcv9",
|
"sparc64-unknown-linux-gnu" => "linux64-sparcv9",
|
||||||
"sparc64-unknown-netbsd" => "BSD-sparc64",
|
"sparc64-unknown-netbsd" => "BSD-sparc64",
|
||||||
"x86_64-apple-darwin" => "darwin64-x86_64-cc",
|
"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-freebsd" => "BSD-x86_64",
|
||||||
"x86_64-unknown-dragonfly" => "BSD-x86_64",
|
"x86_64-unknown-dragonfly" => "BSD-x86_64",
|
||||||
"x86_64-unknown-linux-gnu" => "linux-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-linux-musl" => "linux-x86_64",
|
||||||
"x86_64-unknown-netbsd" => "BSD-x86_64",
|
"x86_64-unknown-netbsd" => "BSD-x86_64",
|
||||||
_ => panic!("don't know how to configure OpenSSL for {}", target),
|
_ => 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
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// #![feature(entry_and_modify)]
|
|
||||||
/// use std::collections::BTreeMap;
|
/// use std::collections::BTreeMap;
|
||||||
///
|
///
|
||||||
/// let mut map: BTreeMap<&str, usize> = BTreeMap::new();
|
/// let mut map: BTreeMap<&str, usize> = BTreeMap::new();
|
||||||
@ -2129,7 +2128,7 @@ impl<'a, K: Ord, V> Entry<'a, K, V> {
|
|||||||
/// .or_insert(42);
|
/// .or_insert(42);
|
||||||
/// assert_eq!(map["poneyland"], 43);
|
/// 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
|
pub fn and_modify<F>(self, mut f: F) -> Self
|
||||||
where F: FnMut(&mut V)
|
where F: FnMut(&mut V)
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,8 @@ use cmp::Ordering;
|
|||||||
use ops::Try;
|
use ops::Try;
|
||||||
|
|
||||||
use super::{AlwaysOk, LoopState};
|
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::{Inspect, Map, Peekable, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, Rev};
|
||||||
use super::{Zip, Sum, Product};
|
use super::{Zip, Sum, Product};
|
||||||
use super::{ChainState, FromIterator, ZipImpl};
|
use super::{ChainState, FromIterator, ZipImpl};
|
||||||
@ -997,11 +998,15 @@ pub trait Iterator {
|
|||||||
/// an extra layer of indirection. `flat_map()` will remove this extra layer
|
/// an extra layer of indirection. `flat_map()` will remove this extra layer
|
||||||
/// on its own.
|
/// 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
|
/// Another way of thinking about `flat_map()`: [`map`]'s closure returns
|
||||||
/// one item for each element, and `flat_map()`'s closure returns an
|
/// one item for each element, and `flat_map()`'s closure returns an
|
||||||
/// iterator for each element.
|
/// iterator for each element.
|
||||||
///
|
///
|
||||||
/// [`map`]: #method.map
|
/// [`map`]: #method.map
|
||||||
|
/// [`flatten`]: #method.flatten
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
@ -1021,7 +1026,79 @@ pub trait Iterator {
|
|||||||
fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
|
fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
|
||||||
where Self: Sized, U: IntoIterator, F: FnMut(Self::Item) -> U,
|
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`].
|
/// 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
|
/// [`Iterator`]: trait.Iterator.html
|
||||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct FlatMap<I, U: IntoIterator, F> {
|
pub struct FlatMap<I, U: IntoIterator, F> {
|
||||||
iter: I,
|
inner: FlattenCompat<Map<I, F>, <U as IntoIterator>::IntoIter>
|
||||||
f: F,
|
}
|
||||||
frontiter: Option<U::IntoIter>,
|
|
||||||
backiter: Option<U::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")]
|
#[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
|
where U::IntoIter: fmt::Debug
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
f.debug_struct("FlatMap")
|
f.debug_struct("FlatMap").field("inner", &self.inner).finish()
|
||||||
.field("iter", &self.iter)
|
|
||||||
.field("frontiter", &self.frontiter)
|
|
||||||
.field("backiter", &self.backiter)
|
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2437,17 +2436,173 @@ impl<I: Iterator, U: IntoIterator, F> Iterator for FlatMap<I, U, F>
|
|||||||
{
|
{
|
||||||
type 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<U::Item> {
|
fn next(&mut self) -> Option<U::Item> {
|
||||||
loop {
|
loop {
|
||||||
if let Some(ref mut inner) = self.frontiter {
|
if let Some(ref mut inner) = self.frontiter {
|
||||||
if let Some(x) = inner.by_ref().next() {
|
if let elt@Some(_) = inner.next() { return elt }
|
||||||
return Some(x)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
match self.iter.next().map(&mut self.f) {
|
match self.iter.next() {
|
||||||
None => return self.backiter.as_mut().and_then(|it| it.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;
|
self.frontiter = None;
|
||||||
|
|
||||||
{
|
{
|
||||||
let f = &mut self.f;
|
|
||||||
let frontiter = &mut self.frontiter;
|
let frontiter = &mut self.frontiter;
|
||||||
init = self.iter.try_fold(init, |acc, x| {
|
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);
|
let r = mid.try_fold(acc, &mut fold);
|
||||||
*frontiter = Some(mid);
|
*frontiter = Some(mid);
|
||||||
r
|
r
|
||||||
@ -2497,27 +2651,23 @@ impl<I: Iterator, U: IntoIterator, F> Iterator for FlatMap<I, U, F>
|
|||||||
where Fold: FnMut(Acc, Self::Item) -> Acc,
|
where Fold: FnMut(Acc, Self::Item) -> Acc,
|
||||||
{
|
{
|
||||||
self.frontiter.into_iter()
|
self.frontiter.into_iter()
|
||||||
.chain(self.iter.map(self.f).map(U::into_iter))
|
.chain(self.iter.map(IntoIterator::into_iter))
|
||||||
.chain(self.backiter)
|
.chain(self.backiter)
|
||||||
.fold(init, |acc, iter| iter.fold(acc, &mut fold))
|
.fold(init, |acc, iter| iter.fold(acc, &mut fold))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
impl<I, U> DoubleEndedIterator for FlattenCompat<I, U>
|
||||||
impl<I: DoubleEndedIterator, U, F> DoubleEndedIterator for FlatMap<I, U, F> where
|
where I: DoubleEndedIterator, U: DoubleEndedIterator,
|
||||||
F: FnMut(I::Item) -> U,
|
I::Item: IntoIterator<IntoIter = U, Item = U::Item>
|
||||||
U: IntoIterator,
|
|
||||||
U::IntoIter: DoubleEndedIterator
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next_back(&mut self) -> Option<U::Item> {
|
fn next_back(&mut self) -> Option<U::Item> {
|
||||||
loop {
|
loop {
|
||||||
if let Some(ref mut inner) = self.backiter {
|
if let Some(ref mut inner) = self.backiter {
|
||||||
if let Some(y) = inner.next_back() {
|
if let elt@Some(_) = inner.next_back() { return elt }
|
||||||
return Some(y)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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()),
|
None => return self.frontiter.as_mut().and_then(|it| it.next_back()),
|
||||||
next => self.backiter = next.map(IntoIterator::into_iter),
|
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;
|
self.backiter = None;
|
||||||
|
|
||||||
{
|
{
|
||||||
let f = &mut self.f;
|
|
||||||
let backiter = &mut self.backiter;
|
let backiter = &mut self.backiter;
|
||||||
init = self.iter.try_rfold(init, |acc, x| {
|
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);
|
let r = mid.try_rfold(acc, &mut fold);
|
||||||
*backiter = Some(mid);
|
*backiter = Some(mid);
|
||||||
r
|
r
|
||||||
@ -2558,16 +2707,12 @@ impl<I: DoubleEndedIterator, U, F> DoubleEndedIterator for FlatMap<I, U, F> wher
|
|||||||
where Fold: FnMut(Acc, Self::Item) -> Acc,
|
where Fold: FnMut(Acc, Self::Item) -> Acc,
|
||||||
{
|
{
|
||||||
self.frontiter.into_iter()
|
self.frontiter.into_iter()
|
||||||
.chain(self.iter.map(self.f).map(U::into_iter))
|
.chain(self.iter.map(IntoIterator::into_iter))
|
||||||
.chain(self.backiter)
|
.chain(self.backiter)
|
||||||
.rfold(init, |acc, iter| iter.rfold(acc, &mut fold))
|
.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
|
/// An iterator that yields `None` forever after the underlying iterator
|
||||||
/// yields `None` once.
|
/// yields `None` once.
|
||||||
///
|
///
|
||||||
|
@ -93,6 +93,7 @@
|
|||||||
#![feature(doc_spotlight)]
|
#![feature(doc_spotlight)]
|
||||||
#![feature(rustc_const_unstable)]
|
#![feature(rustc_const_unstable)]
|
||||||
#![feature(iterator_repeat_with)]
|
#![feature(iterator_repeat_with)]
|
||||||
|
#![feature(iterator_flatten)]
|
||||||
|
|
||||||
#[prelude_import]
|
#[prelude_import]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
@ -73,7 +73,8 @@ pub fn parse_decimal(s: &str) -> ParseResult {
|
|||||||
}
|
}
|
||||||
Some(&b'.') => {
|
Some(&b'.') => {
|
||||||
let (fractional, s) = eat_digits(&s[1..]);
|
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;
|
return Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2892,7 +2892,7 @@ impl u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the value is an ASCII graphic character:
|
/// Checks if the value is an ASCII graphic character:
|
||||||
/// U+0021 '@' ... U+007E '~'.
|
/// U+0021 '!' ... U+007E '~'.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
@ -874,6 +874,44 @@ fn test_iterator_flat_map_fold() {
|
|||||||
assert_eq!(i, 0);
|
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]
|
#[test]
|
||||||
fn test_inspect() {
|
fn test_inspect() {
|
||||||
let xs = [1, 2, 3, 4];
|
let xs = [1, 2, 3, 4];
|
||||||
@ -1287,6 +1325,23 @@ fn test_double_ended_flat_map() {
|
|||||||
assert_eq!(it.next_back(), None);
|
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]
|
#[test]
|
||||||
fn test_double_ended_range() {
|
fn test_double_ended_range() {
|
||||||
assert_eq!((11..14).rev().collect::<Vec<_>>(), [13, 12, 11]);
|
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.try_rfold(0, i8::checked_add), None);
|
||||||
assert_eq!(iter.next_back(), Some(35));
|
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)]
|
||||||
#![feature(inclusive_range_syntax)]
|
#![feature(inclusive_range_syntax)]
|
||||||
#![feature(iterator_try_fold)]
|
#![feature(iterator_try_fold)]
|
||||||
|
#![feature(iterator_flatten)]
|
||||||
|
#![feature(conservative_impl_trait)]
|
||||||
#![feature(iter_rfind)]
|
#![feature(iter_rfind)]
|
||||||
#![feature(iter_rfold)]
|
#![feature(iter_rfold)]
|
||||||
#![feature(iterator_repeat_with)]
|
#![feature(iterator_repeat_with)]
|
||||||
|
@ -101,6 +101,12 @@ fn lonely_dot() {
|
|||||||
assert!(".".parse::<f64>().is_err());
|
assert!(".".parse::<f64>().is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exponentiated_dot() {
|
||||||
|
assert!(".e0".parse::<f32>().is_err());
|
||||||
|
assert!(".e0".parse::<f64>().is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lonely_sign() {
|
fn lonely_sign() {
|
||||||
assert!("+".parse::<f32>().is_err());
|
assert!("+".parse::<f32>().is_err());
|
||||||
|
@ -33,6 +33,7 @@ pub fn opts() -> TargetOptions {
|
|||||||
has_rpath: true,
|
has_rpath: true,
|
||||||
pre_link_args: args,
|
pre_link_args: args,
|
||||||
position_independent_executables: true,
|
position_independent_executables: true,
|
||||||
|
eliminate_frame_pointer: false, // FIXME 43575
|
||||||
relro_level: RelroLevel::Full,
|
relro_level: RelroLevel::Full,
|
||||||
exe_allocation_crate: super::maybe_jemalloc(),
|
exe_allocation_crate: super::maybe_jemalloc(),
|
||||||
.. Default::default()
|
.. Default::default()
|
||||||
|
@ -146,6 +146,7 @@ supported_targets! {
|
|||||||
("powerpc64-unknown-linux-gnu", powerpc64_unknown_linux_gnu),
|
("powerpc64-unknown-linux-gnu", powerpc64_unknown_linux_gnu),
|
||||||
("powerpc64le-unknown-linux-gnu", powerpc64le_unknown_linux_gnu),
|
("powerpc64le-unknown-linux-gnu", powerpc64le_unknown_linux_gnu),
|
||||||
("s390x-unknown-linux-gnu", s390x_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),
|
("sparc64-unknown-linux-gnu", sparc64_unknown_linux_gnu),
|
||||||
("arm-unknown-linux-gnueabi", arm_unknown_linux_gnueabi),
|
("arm-unknown-linux-gnueabi", arm_unknown_linux_gnueabi),
|
||||||
("arm-unknown-linux-gnueabihf", arm_unknown_linux_gnueabihf),
|
("arm-unknown-linux-gnueabihf", arm_unknown_linux_gnueabihf),
|
||||||
@ -186,6 +187,7 @@ supported_targets! {
|
|||||||
("x86_64-unknown-openbsd", x86_64_unknown_openbsd),
|
("x86_64-unknown-openbsd", x86_64_unknown_openbsd),
|
||||||
|
|
||||||
("i686-unknown-netbsd", i686_unknown_netbsd),
|
("i686-unknown-netbsd", i686_unknown_netbsd),
|
||||||
|
("powerpc-unknown-netbsd", powerpc_unknown_netbsd),
|
||||||
("sparc64-unknown-netbsd", sparc64_unknown_netbsd),
|
("sparc64-unknown-netbsd", sparc64_unknown_netbsd),
|
||||||
("x86_64-unknown-netbsd", x86_64_unknown_netbsd),
|
("x86_64-unknown-netbsd", x86_64_unknown_netbsd),
|
||||||
("x86_64-rumprun-netbsd", x86_64_rumprun_netbsd),
|
("x86_64-rumprun-netbsd", x86_64_rumprun_netbsd),
|
||||||
|
35
src/librustc_back/target/powerpc_unknown_netbsd.rs
Normal file
35
src/librustc_back/target/powerpc_unknown_netbsd.rs
Normal file
@ -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,
|
||||||
|
})
|
||||||
|
}
|
34
src/librustc_back/target/sparc_unknown_linux_gnu.rs
Normal file
34
src/librustc_back/target/sparc_unknown_linux_gnu.rs
Normal file
@ -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 rustc::ty::layout::{HasDataLayout, LayoutOf};
|
||||||
|
|
||||||
use libc::c_uint;
|
use libc::c_uint;
|
||||||
use std::{cmp, iter};
|
use std::cmp;
|
||||||
|
|
||||||
pub use syntax::abi::Abi;
|
pub use syntax::abi::Abi;
|
||||||
pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
|
pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
|
||||||
@ -279,30 +279,6 @@ impl Uniform {
|
|||||||
pub fn align(&self, cx: &CodegenCx) -> Align {
|
pub fn align(&self, cx: &CodegenCx) -> Align {
|
||||||
self.unit.align(cx)
|
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> {
|
pub trait LayoutExt<'tcx> {
|
||||||
@ -405,55 +381,81 @@ impl<'tcx> LayoutExt<'tcx> for TyLayout<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
pub enum CastTarget {
|
pub struct CastTarget {
|
||||||
Uniform(Uniform),
|
pub prefix: [Option<RegKind>; 8],
|
||||||
Pair(Reg, Reg)
|
pub prefix_chunk: Size,
|
||||||
|
pub rest: Uniform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Reg> for CastTarget {
|
impl From<Reg> for CastTarget {
|
||||||
fn from(unit: Reg) -> CastTarget {
|
fn from(unit: Reg) -> CastTarget {
|
||||||
CastTarget::Uniform(Uniform::from(unit))
|
CastTarget::from(Uniform::from(unit))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Uniform> for CastTarget {
|
impl From<Uniform> for CastTarget {
|
||||||
fn from(uniform: Uniform) -> CastTarget {
|
fn from(uniform: Uniform) -> CastTarget {
|
||||||
CastTarget::Uniform(uniform)
|
CastTarget {
|
||||||
|
prefix: [None; 8],
|
||||||
|
prefix_chunk: Size::from_bytes(0),
|
||||||
|
rest: uniform
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CastTarget {
|
impl CastTarget {
|
||||||
pub fn size(&self, cx: &CodegenCx) -> Size {
|
pub fn pair(a: Reg, b: Reg) -> CastTarget {
|
||||||
match *self {
|
CastTarget {
|
||||||
CastTarget::Uniform(u) => u.total,
|
prefix: [Some(a.kind), None, None, None, None, None, None, None],
|
||||||
CastTarget::Pair(a, b) => {
|
prefix_chunk: a.size,
|
||||||
(a.size.abi_align(a.align(cx)) + b.size)
|
rest: Uniform::from(b)
|
||||||
.abi_align(self.align(cx))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
pub fn align(&self, cx: &CodegenCx) -> Align {
|
||||||
match *self {
|
self.prefix.iter()
|
||||||
CastTarget::Uniform(u) => u.align(cx),
|
.filter_map(|x| x.map(|kind| Reg { kind: kind, size: self.prefix_chunk }.align(cx)))
|
||||||
CastTarget::Pair(a, b) => {
|
.fold(cx.data_layout().aggregate_align.max(self.rest.align(cx)),
|
||||||
cx.data_layout().aggregate_align
|
|acc, align| acc.max(align))
|
||||||
.max(a.align(cx))
|
|
||||||
.max(b.align(cx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn llvm_type(&self, cx: &CodegenCx) -> Type {
|
pub fn llvm_type(&self, cx: &CodegenCx) -> Type {
|
||||||
match *self {
|
let rest_ll_unit = self.rest.unit.llvm_type(cx);
|
||||||
CastTarget::Uniform(u) => u.llvm_type(cx),
|
let rest_count = self.rest.total.bytes() / self.rest.unit.size.bytes();
|
||||||
CastTarget::Pair(a, b) => {
|
let rem_bytes = self.rest.total.bytes() % self.rest.unit.size.bytes();
|
||||||
Type::struct_(cx, &[
|
|
||||||
a.llvm_type(cx),
|
if self.prefix.iter().all(|x| x.is_none()) {
|
||||||
b.llvm_type(cx)
|
// Simplify to a single unit when there is no prefix and size <= unit size
|
||||||
], false)
|
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
|
// extensions
|
||||||
|
|
||||||
|
pub fn get_args(&self) -> &[OsString] {
|
||||||
|
&self.args
|
||||||
|
}
|
||||||
|
|
||||||
pub fn take_args(&mut self) -> Vec<OsString> {
|
pub fn take_args(&mut self) -> Vec<OsString> {
|
||||||
mem::replace(&mut self.args, Vec::new())
|
mem::replace(&mut self.args, Vec::new())
|
||||||
}
|
}
|
||||||
|
@ -700,9 +700,6 @@ fn link_natively(sess: &Session,
|
|||||||
prog = time(sess.time_passes(), "running linker", || {
|
prog = time(sess.time_passes(), "running linker", || {
|
||||||
exec_linker(sess, &mut cmd, tmpdir)
|
exec_linker(sess, &mut cmd, tmpdir)
|
||||||
});
|
});
|
||||||
if !retry_on_segfault || i > 3 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
let output = match prog {
|
let output = match prog {
|
||||||
Ok(ref output) => output,
|
Ok(ref output) => output,
|
||||||
Err(_) => break,
|
Err(_) => break,
|
||||||
@ -713,6 +710,31 @@ fn link_natively(sess: &Session,
|
|||||||
let mut out = output.stderr.clone();
|
let mut out = output.stderr.clone();
|
||||||
out.extend(&output.stdout);
|
out.extend(&output.stdout);
|
||||||
let out = String::from_utf8_lossy(&out);
|
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_segv = "clang: error: unable to execute command: Segmentation fault: 11";
|
||||||
let msg_bus = "clang: error: unable to execute command: Bus error: 10";
|
let msg_bus = "clang: error: unable to execute command: Bus error: 10";
|
||||||
if !(out.contains(msg_segv) || out.contains(msg_bus)) {
|
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;
|
let used_link_args = &trans.crate_info.link_args;
|
||||||
|
|
||||||
if crate_type == config::CrateTypeExecutable &&
|
if crate_type == config::CrateTypeExecutable {
|
||||||
t.options.position_independent_executables {
|
let mut position_independent_executable = false;
|
||||||
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
|
if t.options.position_independent_executables {
|
||||||
&& !sess.crt_static() && !args.any(|x| *x == "-static") {
|
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();
|
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 add_object(&mut self, path: &Path);
|
||||||
fn gc_sections(&mut self, keep_metadata: bool);
|
fn gc_sections(&mut self, keep_metadata: bool);
|
||||||
fn position_independent_executable(&mut self);
|
fn position_independent_executable(&mut self);
|
||||||
|
fn no_position_independent_executable(&mut self);
|
||||||
fn partial_relro(&mut self);
|
fn partial_relro(&mut self);
|
||||||
fn full_relro(&mut self);
|
fn full_relro(&mut self);
|
||||||
fn optimize(&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 output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); }
|
||||||
fn add_object(&mut self, path: &Path) { self.cmd.arg(path); }
|
fn add_object(&mut self, path: &Path) { self.cmd.arg(path); }
|
||||||
fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); }
|
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 partial_relro(&mut self) { self.linker_arg("-z,relro"); }
|
||||||
fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); }
|
fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); }
|
||||||
fn build_static_executable(&mut self) { self.cmd.arg("-static"); }
|
fn build_static_executable(&mut self) { self.cmd.arg("-static"); }
|
||||||
@ -439,6 +441,10 @@ impl<'a> Linker for MsvcLinker<'a> {
|
|||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn no_position_independent_executable(&mut self) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
fn partial_relro(&mut self) {
|
fn partial_relro(&mut self) {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
@ -647,6 +653,10 @@ impl<'a> Linker for EmLinker<'a> {
|
|||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn no_position_independent_executable(&mut self) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
fn partial_relro(&mut self) {
|
fn partial_relro(&mut self) {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
@ -8,50 +8,160 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// 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 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>,
|
arg.extend_integer_width_to(bits);
|
||||||
ret: &mut ArgType<'tcx>,
|
}
|
||||||
offset: &mut Size) {
|
|
||||||
if !ret.layout.is_aggregate() {
|
fn bits_to_int_reg(bits: u64) -> Reg {
|
||||||
ret.extend_integer_width_to(64);
|
if bits <= 8 {
|
||||||
|
Reg::i8()
|
||||||
|
} else if bits <= 16 {
|
||||||
|
Reg::i16()
|
||||||
|
} else if bits <= 32 {
|
||||||
|
Reg::i32()
|
||||||
} else {
|
} else {
|
||||||
ret.make_indirect();
|
Reg::i64()
|
||||||
*offset += cx.tcx.data_layout.pointer_size;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn classify_arg_ty(cx: &CodegenCx, arg: &mut ArgType, offset: &mut Size) {
|
fn float_reg<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ret: &ArgType<'tcx>, i: usize) -> Option<Reg> {
|
||||||
let dl = &cx.tcx.data_layout;
|
match ret.layout.field(cx, i).abi {
|
||||||
let size = arg.layout.size;
|
layout::Abi::Scalar(ref scalar) => match scalar.value {
|
||||||
let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align);
|
layout::F32 => Some(Reg::f32()),
|
||||||
|
layout::F64 => Some(Reg::f64()),
|
||||||
|
_ => None
|
||||||
|
},
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if arg.layout.is_aggregate() {
|
fn classify_ret_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
|
||||||
arg.cast_to(Uniform {
|
if !ret.layout.is_aggregate() {
|
||||||
unit: Reg::i64(),
|
extend_integer_width_mips(ret, 64);
|
||||||
total: size
|
return;
|
||||||
});
|
|
||||||
if !offset.is_abi_aligned(align) {
|
|
||||||
arg.pad_with(Reg::i64());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
arg.extend_integer_width_to(64);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*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>) {
|
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() {
|
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 {
|
for arg in &mut fty.args {
|
||||||
if arg.is_ignore() { continue; }
|
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);
|
let mut target = CastTarget::from(lo);
|
||||||
if size > offset {
|
if size > offset {
|
||||||
if let Some(hi) = reg_component(cls, &mut i, 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);
|
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());
|
llvm_args.as_ptr());
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: the features must be known to LLVM or the feature
|
// WARNING: the features after applying `to_llvm_feature` must be known
|
||||||
// detection code will walk past the end of the feature array,
|
// to LLVM or the feature detection code will walk past the end of the feature
|
||||||
// leading to crashes.
|
// array, leading to crashes.
|
||||||
|
|
||||||
const ARM_WHITELIST: &'static [&'static str] = &["neon", "v7", "vfp2", "vfp3", "vfp4"];
|
const ARM_WHITELIST: &'static [&'static str] = &["neon", "v7", "vfp2", "vfp3", "vfp4"];
|
||||||
|
|
||||||
const AARCH64_WHITELIST: &'static [&'static str] = &["neon", "v7"];
|
const AARCH64_WHITELIST: &'static [&'static str] = &["neon", "v7"];
|
||||||
|
|
||||||
const X86_WHITELIST: &'static [&'static str] = &["avx", "avx2", "bmi", "bmi2", "sse",
|
const X86_WHITELIST: &'static [&'static str] = &["aes", "avx", "avx2", "avx512bw",
|
||||||
"sse2", "sse3", "sse4.1", "sse4.2",
|
"avx512cd", "avx512dq", "avx512er",
|
||||||
"ssse3", "tbm", "lzcnt", "popcnt",
|
"avx512f", "avx512ifma", "avx512pf",
|
||||||
"sse4a", "rdrnd", "rdseed", "fma",
|
"avx512vbmi", "avx512vl", "avx512vpopcntdq",
|
||||||
"xsave", "xsaveopt", "xsavec",
|
"bmi", "bmi2", "fma", "fxsr",
|
||||||
"xsaves", "aes", "pclmulqdq",
|
"lzcnt", "mmx", "pclmulqdq",
|
||||||
"avx512bw", "avx512cd",
|
"popcnt", "rdrand", "rdseed",
|
||||||
"avx512dq", "avx512er",
|
"sse", "sse2", "sse3", "sse4.1",
|
||||||
"avx512f", "avx512ifma",
|
"sse4.2", "sse4a", "ssse3",
|
||||||
"avx512pf", "avx512vbmi",
|
"tbm", "xsave", "xsavec",
|
||||||
"avx512vl", "avx512vpopcntdq",
|
"xsaveopt", "xsaves"];
|
||||||
"mmx", "fxsr"];
|
|
||||||
|
|
||||||
const HEXAGON_WHITELIST: &'static [&'static str] = &["hvx", "hvx-double"];
|
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 {
|
pub fn to_llvm_feature(s: &str) -> &str {
|
||||||
match s {
|
match s {
|
||||||
"pclmulqdq" => "pclmul",
|
"pclmulqdq" => "pclmul",
|
||||||
|
"rdrand" => "rdrnd",
|
||||||
s => s,
|
s => s,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,7 +245,7 @@ pub trait AsciiExt {
|
|||||||
fn is_ascii_punctuation(&self) -> bool { unimplemented!(); }
|
fn is_ascii_punctuation(&self) -> bool { unimplemented!(); }
|
||||||
|
|
||||||
/// Checks if the value is an ASCII graphic character:
|
/// 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
|
/// For strings, true if all characters in the string are
|
||||||
/// ASCII graphic characters.
|
/// ASCII graphic characters.
|
||||||
///
|
///
|
||||||
|
@ -2082,7 +2082,6 @@ impl<'a, K, V> Entry<'a, K, V> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// #![feature(entry_and_modify)]
|
|
||||||
/// use std::collections::HashMap;
|
/// use std::collections::HashMap;
|
||||||
///
|
///
|
||||||
/// let mut map: HashMap<&str, u32> = HashMap::new();
|
/// let mut map: HashMap<&str, u32> = HashMap::new();
|
||||||
@ -2097,7 +2096,7 @@ impl<'a, K, V> Entry<'a, K, V> {
|
|||||||
/// .or_insert(42);
|
/// .or_insert(42);
|
||||||
/// assert_eq!(map["poneyland"], 43);
|
/// 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
|
pub fn and_modify<F>(self, mut f: F) -> Self
|
||||||
where F: FnMut(&mut V)
|
where F: FnMut(&mut V)
|
||||||
{
|
{
|
||||||
|
@ -1545,6 +1545,26 @@ mod tests {
|
|||||||
drop(listener);
|
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]
|
#[test]
|
||||||
fn nodelay() {
|
fn nodelay() {
|
||||||
let addr = next_test_ip4();
|
let addr = next_test_ip4();
|
||||||
|
@ -1024,6 +1024,23 @@ mod tests {
|
|||||||
assert!(start.elapsed() > Duration::from_millis(400));
|
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]
|
#[test]
|
||||||
fn connect_send_recv() {
|
fn connect_send_recv() {
|
||||||
let addr = next_test_ip4();
|
let addr = next_test_ip4();
|
||||||
|
@ -14,7 +14,7 @@ use sync::{mutex, MutexGuard, PoisonError};
|
|||||||
use sys_common::condvar as sys;
|
use sys_common::condvar as sys;
|
||||||
use sys_common::mutex as sys_mutex;
|
use sys_common::mutex as sys_mutex;
|
||||||
use sys_common::poison::{self, LockResult};
|
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
|
/// A type indicating whether a timed wait on a condition variable returned
|
||||||
/// due to a time out or not.
|
/// 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
|
/// Waits on this condition variable for a notification, timing out after a
|
||||||
/// specified duration.
|
/// specified duration.
|
||||||
///
|
///
|
||||||
@ -295,7 +353,15 @@ impl Condvar {
|
|||||||
///
|
///
|
||||||
/// Note that the best effort is made to ensure that the time waited is
|
/// 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
|
/// 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
|
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
|
||||||
/// known to have elapsed.
|
/// known to have elapsed.
|
||||||
@ -304,6 +370,7 @@ impl Condvar {
|
|||||||
/// returns, regardless of whether the timeout elapsed or not.
|
/// returns, regardless of whether the timeout elapsed or not.
|
||||||
///
|
///
|
||||||
/// [`wait`]: #method.wait
|
/// [`wait`]: #method.wait
|
||||||
|
/// [`wait_timeout_until`]: #method.wait_timeout_until
|
||||||
/// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html
|
/// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # 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.
|
/// Wakes up one blocked thread on this condvar.
|
||||||
///
|
///
|
||||||
/// If there is a blocked thread on this condition variable, then it will
|
/// If there is a blocked thread on this condition variable, then it will
|
||||||
@ -480,6 +621,7 @@ impl Drop for Condvar {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
/// #![feature(wait_until)]
|
||||||
use sync::mpsc::channel;
|
use sync::mpsc::channel;
|
||||||
use sync::{Condvar, Mutex, Arc};
|
use sync::{Condvar, Mutex, Arc};
|
||||||
use sync::atomic::{AtomicBool, Ordering};
|
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]
|
#[test]
|
||||||
#[cfg_attr(target_os = "emscripten", ignore)]
|
#[cfg_attr(target_os = "emscripten", ignore)]
|
||||||
fn wait_timeout_wait() {
|
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]
|
#[test]
|
||||||
#[cfg_attr(target_os = "emscripten", ignore)]
|
#[cfg_attr(target_os = "emscripten", ignore)]
|
||||||
fn wait_timeout_wake() {
|
fn wait_timeout_wake() {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use cmp;
|
use cmp;
|
||||||
use io::{Error, ErrorKind, Result};
|
use io::{self, Error, ErrorKind, Result};
|
||||||
use mem;
|
use mem;
|
||||||
use net::{SocketAddr, Shutdown};
|
use net::{SocketAddr, Shutdown};
|
||||||
use path::Path;
|
use path::Path;
|
||||||
@ -130,6 +130,10 @@ impl TcpStream {
|
|||||||
pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
|
pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
|
||||||
let file = self.0.dup(b"read_timeout")?;
|
let file = self.0.dup(b"read_timeout")?;
|
||||||
if let Some(duration) = duration_option {
|
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 {
|
file.write(&TimeSpec {
|
||||||
tv_sec: duration.as_secs() as i64,
|
tv_sec: duration.as_secs() as i64,
|
||||||
tv_nsec: duration.subsec_nanos() as i32
|
tv_nsec: duration.subsec_nanos() as i32
|
||||||
@ -143,6 +147,10 @@ impl TcpStream {
|
|||||||
pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
|
pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
|
||||||
let file = self.0.dup(b"write_timeout")?;
|
let file = self.0.dup(b"write_timeout")?;
|
||||||
if let Some(duration) = duration_option {
|
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 {
|
file.write(&TimeSpec {
|
||||||
tv_sec: duration.as_secs() as i64,
|
tv_sec: duration.as_secs() as i64,
|
||||||
tv_nsec: duration.subsec_nanos() as i32
|
tv_nsec: duration.subsec_nanos() as i32
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
use cell::UnsafeCell;
|
use cell::UnsafeCell;
|
||||||
use cmp;
|
use cmp;
|
||||||
use io::{Error, ErrorKind, Result};
|
use io::{self, Error, ErrorKind, Result};
|
||||||
use mem;
|
use mem;
|
||||||
use net::{SocketAddr, Ipv4Addr, Ipv6Addr};
|
use net::{SocketAddr, Ipv4Addr, Ipv6Addr};
|
||||||
use path::Path;
|
use path::Path;
|
||||||
@ -179,6 +179,10 @@ impl UdpSocket {
|
|||||||
pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
|
pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
|
||||||
let file = self.0.dup(b"read_timeout")?;
|
let file = self.0.dup(b"read_timeout")?;
|
||||||
if let Some(duration) = duration_option {
|
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 {
|
file.write(&TimeSpec {
|
||||||
tv_sec: duration.as_secs() as i64,
|
tv_sec: duration.as_secs() as i64,
|
||||||
tv_nsec: duration.subsec_nanos() as i32
|
tv_nsec: duration.subsec_nanos() as i32
|
||||||
@ -192,6 +196,10 @@ impl UdpSocket {
|
|||||||
pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
|
pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
|
||||||
let file = self.0.dup(b"write_timeout")?;
|
let file = self.0.dup(b"write_timeout")?;
|
||||||
if let Some(duration) = duration_option {
|
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 {
|
file.write(&TimeSpec {
|
||||||
tv_sec: duration.as_secs() as i64,
|
tv_sec: duration.as_secs() as i64,
|
||||||
tv_nsec: duration.subsec_nanos() as i32
|
tv_nsec: duration.subsec_nanos() as i32
|
||||||
|
@ -1410,7 +1410,7 @@ impl IntoRawFd for UnixDatagram {
|
|||||||
#[cfg(all(test, not(target_os = "emscripten")))]
|
#[cfg(all(test, not(target_os = "emscripten")))]
|
||||||
mod test {
|
mod test {
|
||||||
use thread;
|
use thread;
|
||||||
use io;
|
use io::{self, ErrorKind};
|
||||||
use io::prelude::*;
|
use io::prelude::*;
|
||||||
use time::Duration;
|
use time::Duration;
|
||||||
use sys_common::io::test::tmpdir;
|
use sys_common::io::test::tmpdir;
|
||||||
@ -1613,6 +1613,27 @@ mod test {
|
|||||||
assert!(kind == io::ErrorKind::WouldBlock || kind == io::ErrorKind::TimedOut);
|
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]
|
#[test]
|
||||||
fn test_unix_datagram() {
|
fn test_unix_datagram() {
|
||||||
let dir = tmpdir();
|
let dir = tmpdir();
|
||||||
@ -1712,6 +1733,24 @@ mod test {
|
|||||||
thread.join().unwrap();
|
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]
|
#[test]
|
||||||
fn abstract_namespace_not_allowed() {
|
fn abstract_namespace_not_allowed() {
|
||||||
assert!(UnixStream::connect("\0asdf").is_err());
|
assert!(UnixStream::connect("\0asdf").is_err());
|
||||||
|
@ -1332,7 +1332,7 @@ impl char {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the value is an ASCII graphic character:
|
/// Checks if the value is an ASCII graphic character:
|
||||||
/// U+0021 '@' ... U+007E '~'.
|
/// U+0021 '!' ... U+007E '~'.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
@ -36,6 +36,8 @@ ifeq ($(filter powerpc,$(LLVM_COMPONENTS)),powerpc)
|
|||||||
nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add
|
nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add
|
||||||
$(RUSTC) --target=powerpc64le-unknown-linux-gnu atomic_lock_free.rs
|
$(RUSTC) --target=powerpc64le-unknown-linux-gnu atomic_lock_free.rs
|
||||||
nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add
|
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
|
$(RUSTC) --target=s390x-unknown-linux-gnu atomic_lock_free.rs
|
||||||
nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add
|
nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add
|
||||||
endif
|
endif
|
||||||
|
80
src/test/ui/macros/macro-at-most-once-rep-ambig.stderr
Normal file
80
src/test/ui/macros/macro-at-most-once-rep-ambig.stderr
Normal file
@ -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",
|
"powerpc64-unknown-linux-gnu",
|
||||||
"powerpc64le-unknown-linux-gnu",
|
"powerpc64le-unknown-linux-gnu",
|
||||||
"s390x-unknown-linux-gnu",
|
"s390x-unknown-linux-gnu",
|
||||||
|
"sparc-unknown-linux-gnu",
|
||||||
"sparc64-unknown-linux-gnu",
|
"sparc64-unknown-linux-gnu",
|
||||||
"sparcv9-sun-solaris",
|
"sparcv9-sun-solaris",
|
||||||
"wasm32-unknown-emscripten",
|
"wasm32-unknown-emscripten",
|
||||||
|
Loading…
Reference in New Issue
Block a user